Перекошенная текстура LWJGL

Я пытался использовать текстуру с LWJGL, оказалось, что RBG-png несколько искажены. Пример: Исходное изображение/Текстура

Код загрузки на 98% состоит из примера захватчиков пространства вики lwjgl. Texture.java:

public int target, textureID, height, width, texWidth, texHeight;
private float widthRatio, heightRatio;

public Texture(int target, int textureID) {

    this.target = target;
    this.textureID = textureID;
}

public void bind() {

    GL11.glBindTexture(target, textureID);
}

public void setWidth(int width) {

    this.width = width;
    setWidth();
}

public void setHeight(int height) {

    this.height = height;
    setHeight();
}

public int getImageWidth() {

    return width;
}

public int getImageHeight() {

    return height;
}

public float getWidth() {

    return widthRatio;
}

public float getHeight() {

    return heightRatio;
}

public void setTextureWidth(int texWidth) {

    this.texWidth = texWidth;
    setWidth();
}

public void setTextureHeight(int texHeight) {

    this.texHeight = texHeight;
    setHeight();
}

private void setWidth() {

    if (texWidth != 0)
        widthRatio = ((float) width) / texWidth;
}

private void setHeight() {

    if (texHeight != 0)
        heightRatio = ((float) height) / texHeight;
}

Текстурлоадер.java:

private static HashMap<String, Texture> table = new HashMap<String, Texture>();

    private static ColorModel glAlphaColorModel, glColorModel;
    private static IntBuffer textureIDBuffer = BufferUtils.createIntBuffer(1);

    static {
        glAlphaColorModel = new ComponentColorModel(
                ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] { 8, 8,
                        8, 8 }, true, false, ComponentColorModel.TRANSLUCENT,
                DataBuffer.TYPE_BYTE);

        glColorModel = new ComponentColorModel(
                ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] { 8, 8,
                        8, 0 }, false, false, ComponentColorModel.OPAQUE,
                DataBuffer.TYPE_BYTE);
    }

    private static int createTextureID() {

        GL11.glGenTextures(textureIDBuffer);
        return textureIDBuffer.get(0);
    }

    public static Texture getTexture(String name, BufferedImage image)
            throws IOException {

        Texture tex = table.get(name);

        if (tex != null)
            return tex;

        tex = getTexture(image, GL11.GL_TEXTURE_2D, GL11.GL_RGBA,
                GL11.GL_LINEAR, GL11.GL_LINEAR);

        table.put(name, tex);

        return tex;
    }

    public static Texture getTexture(BufferedImage image, int target,
            int dstPixelFormat, int minFilter, int magFilter)
            throws IOException {

        int srcPixelFormat;

        int textureID = createTextureID();
        Texture texture = new Texture(target, textureID);

        GL11.glBindTexture(target, textureID);

        texture.setWidth(image.getWidth());
        texture.setHeight(image.getHeight());

        if (image.getColorModel().hasAlpha())
            srcPixelFormat = GL11.GL_RGBA;
        else
            srcPixelFormat = GL11.GL_RGB;

        ByteBuffer textureBuffer = convertImageData(image, texture);

        if (target == GL11.GL_TEXTURE_2D) {

            GL11.glTexParameteri(target, GL11.GL_TEXTURE_MIN_FILTER, minFilter);
            GL11.glTexParameteri(target, GL11.GL_TEXTURE_MAG_FILTER, magFilter);
        }

        GL11.glTexImage2D(target, 0, dstPixelFormat, image.getWidth(),
                image.getHeight(), 0, srcPixelFormat,
                GL11.GL_UNSIGNED_BYTE, textureBuffer);

        return texture;
    }

    private static ByteBuffer convertImageData(BufferedImage bufferedImage,
            Texture texture) {

        ByteBuffer imageBuffer;
        WritableRaster raster;
        BufferedImage texImage;

        int texWidth = bufferedImage.getWidth();
        int texHeight = bufferedImage.getHeight();

        texture.setTextureHeight(texHeight);
        texture.setTextureWidth(texWidth);

        if (bufferedImage.getColorModel().hasAlpha()) {
            raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
                    texWidth, texHeight, 4, null);
            texImage = new BufferedImage(glAlphaColorModel, raster, false,
                    new Hashtable());
        } else {
            raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
                    texWidth, texHeight, 3, null);
            texImage = new BufferedImage(glColorModel, raster, false,
                    new Hashtable());
        }

        Graphics g = texImage.getGraphics();
        g.setColor(new Color(0f, 0f, 0f, 0f));
        g.fillRect(0, 0, texWidth, texHeight);
        g.drawImage(bufferedImage, 0, 0, null);

        // texImage is NOT skewed at this point

        byte[] data = ((DataBufferByte) texImage.getRaster().getDataBuffer())
                .getData();

        imageBuffer = ByteBuffer.allocateDirect(data.length);
        imageBuffer.order(ByteOrder.nativeOrder());
        imageBuffer.put(data, 0, data.length);
        imageBuffer.flip();

        return imageBuffer;
    }

person Jan Weber    schedule 07.01.2013    source источник


Ответы (2)


Я знаю, что это старый вопрос, но я сам столкнулся с этим минуту назад. Надеюсь, кто-то еще выиграет, если вы уже разобрались с этим самостоятельно.

Причина, по которой текстуры перекошены и обесцвечены, заключается в том, что они не совпадают должным образом с вашим UNPACK_ALIGNMENT. Насколько я понимаю, количество байтов в каждой строке должно быть кратно тому, что установлено для UNPACK_ALIGNMENT (по умолчанию 4).

В 4-компонентном формате это не проблема, поскольку каждый пиксель состоит из 4 байтов, поэтому изображение любого размера выравнивается правильно. Но в других форматах данные дополняются, чтобы обеспечить их правильное выравнивание, что вызывает подобные проблемы.

Вы можете изменить размер своего изображения, чтобы оно правильно выравнивалось ((width * formatComponents) % 4 == 0) , изменить формат на 4-компонентный формат или изменить UNPACK_ALIGNMENT на что-то, что не дополняет ваши изображения, используя:

glPixelStore(GL_UNPACK_ALIGNMENT, alignment); //Alignment must be 1, 2, 4, or 8.

http://www.opengl.org/wiki/Common_Mistakes#Texture_upload_and_pixel_reads

person Brendan Jones    schedule 24.04.2013
comment
Это помогло мне. Убедившись, что я передаю данные RGBA вместо данных RGB, я предотвратил перекос всех текстур. - person user1349637; 08.12.2014

У вас может возникнуть проблема с тем, как PNG хранится в файле и как он загружается Java. Я не уверен, сталкивался ли я с этой проблемой раньше, но я знаю, что в какой-то момент у меня были проблемы с загрузкой изображений в LWJGL. Вот класс, который я собрал, который обеспечивает преобразование PNG в формат, понятный OpenGL, без перекоса или проблем с цветом. Как предупреждение, это сработало для моих PNG, но существует множество возможных форматов, в которых может быть BufferedImage, и я не верю, что этот код охватывает все случаи. Надеюсь, это поможет вам. Он должен быть полностью автономным, за исключением другого класса (SimpleTexture), который я также включу здесь для вас. Обратите внимание, что класс BasicTextureLoader на самом деле является Runnable, поскольку изначально он был разработан для загрузки текстур в фоновом режиме.

import org.lwjgl.opengl.OpenGLException;

import javax.imageio.ImageIO;

import static org.lwjgl.opengl.GL11.*;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.IOException;
import java.net.URL;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

/**
 * User: FreezerburnVinny
 * Date: 1/1/12
 * Time: 5:59 PM
 */
public class BasicTextureLoader implements Runnable {
    public SimpleTexture texture;
    public BufferedImage image;
    public int width, height;
    protected String mFile;
    protected boolean mShouldRemoveBackground, mKeepBufferedImage;

    public BasicTextureLoader( String file, boolean shouldRemoveBackground ) {
        mFile = file;
        this.texture = null;
        this.width = 0;
        this.height = 0;
        this.mShouldRemoveBackground = shouldRemoveBackground;
        this.mKeepBufferedImage = false;
    }

    public BasicTextureLoader( String file, boolean shouldRemoveBackground, boolean keepBufferedImage ) {
        mFile = file;
        this.texture = null;
        this.width = 0;
        this.height = 0;
        this.mShouldRemoveBackground = shouldRemoveBackground;
        this.mKeepBufferedImage = keepBufferedImage;
    }

     protected ByteBuffer convertBufferedImageToByteBuffer( BufferedImage image ) {
        ByteBuffer buffer = ByteBuffer.allocateDirect( width * height * 4 );
        buffer.order( ByteOrder.nativeOrder() );
        byte[] bytes = ( (DataBufferByte) image.getRaster().getDataBuffer() ).getData();
        switch( image.getType() ) {
            case BufferedImage.TYPE_3BYTE_BGR:
                convertBGRBufferedImageToByteBuffer( buffer, bytes );
                break;
            case BufferedImage.TYPE_4BYTE_ABGR:
                convertABGRBufferedImageToByteBuffer( buffer, bytes );
                break;
            case BufferedImage.TYPE_4BYTE_ABGR_PRE:
                convertBGRBufferedImageToByteBuffer( buffer, bytes );
                break;
            case BufferedImage.TYPE_INT_ARGB:
                convertARGBBufferedImageToByteBuffer( buffer, bytes );
                break;
            case BufferedImage.TYPE_INT_BGR:
                convertBGRBufferedImageToByteBuffer( buffer, bytes );
                break;
            case BufferedImage.TYPE_INT_RGB:
                convertRGBBufferedImageToByteBuffer( buffer, bytes );
                break;
            case 12:
                convertRGBBufferedImageToByteBuffer( buffer, bytes );
                break;
            default:
                throw new OpenGLException( "Unsupported image type: " + image.getType() );
        }
        return buffer;
    }

    protected void convertARGBBufferedImageToByteBuffer( ByteBuffer buffer, byte[] bytes ) {
        byte backgrounda = (byte) 255;
        byte backgroundr = (byte) 255;
        byte backgroundg = (byte) 255;
        byte backgroundb = (byte) 255;
        for( int i = 0; i < bytes.length; i+=4 ) {
            byte alpha = bytes[ i ];
            byte red = bytes[ i + 1 ];
            byte green = bytes[ i + 2 ];
            byte blue = bytes[ i + 3 ];
            if( mShouldRemoveBackground ) {
                if( i == 0 ) {
                    backgrounda = alpha;
                    backgroundr = red;
                    backgroundg = green;
                    backgroundb = blue;
                }
                else if( alpha == backgrounda && red == backgroundr &&
                        green == backgroundg && blue == backgroundb ) {
                    alpha = 0;
                }
            }
            buffer.put( red );
            buffer.put( green );
            buffer.put( blue );
            buffer.put( alpha );
        }
        buffer.rewind();
    }

    protected void convertABGRBufferedImageToByteBuffer( ByteBuffer buffer, byte[] bytes ) {
        byte backgrounda = (byte) 255;
        byte backgroundr = (byte) 255;
        byte backgroundg = (byte) 255;
        byte backgroundb = (byte) 255;
//        System.err.println( buffer.limit() );
        for( int i = 0; i < bytes.length; i+=4 ) {
            byte alpha = bytes[ i ];
            byte blue = bytes[ i + 1 ];
            byte green = bytes[ i + 2 ];
            byte red = bytes[ i + 3 ];
            if( mShouldRemoveBackground ) {
                if( i == 0 ) {
                    backgrounda = alpha;
                    backgroundr = red;
                    backgroundg = green;
                    backgroundb = blue;
                }
                else if( alpha == backgrounda && red == backgroundr &&
                        green == backgroundg && blue == backgroundb ) {
                    alpha = 0;
                }
            }
            buffer.put( red );
            buffer.put( green );
            buffer.put( blue );
            buffer.put( alpha );
        }
        buffer.rewind();
    }

    protected void convertBGRBufferedImageToByteBuffer( ByteBuffer buffer, byte[] bytes ) {
        byte backgrounda = (byte) 255;
        byte backgroundr = (byte) 255;
        byte backgroundg = (byte) 255;
        byte backgroundb = (byte) 255;
        for( int i = 0; i < bytes.length; i+=3 ) {
            byte blue = bytes[ i ];
            byte green = bytes[ i + 1 ];
            byte red = bytes[ i + 2 ];
            byte alpha = (byte) 0xFF;
            buffer.put( red );
            buffer.put( green );
            buffer.put( blue );
            if( mShouldRemoveBackground ) {
                if( i == 0 ) {
                    backgrounda = alpha;
                    backgroundr = red;
                    backgroundg = green;
                    backgroundb = blue;
                }
                else if( alpha == backgrounda && red == backgroundr &&
                        green == backgroundg && blue == backgroundb ) {
                    alpha = 0;
                }
            }
            buffer.put( alpha );
        }
        buffer.rewind();
    }

    protected void convertRGBBufferedImageToByteBuffer( ByteBuffer buffer, byte[] bytes ) {
        byte backgrounda = (byte) 255;
        byte backgroundr = (byte) 255;
        byte backgroundg = (byte) 255;
        byte backgroundb = (byte) 255;
        for( int i = 0; i < bytes.length; i+=3 ) {
            byte red = bytes[ i ];
            byte green = bytes[ i + 1 ];
            byte blue = bytes[ i + 2 ];
            byte alpha = (byte) 0xFF;
            buffer.put( red );
            buffer.put( green );
            buffer.put( blue );
            if( mShouldRemoveBackground ) {
                if( i == 0 ) {
                    backgrounda = alpha;
                    backgroundr = red;
                    backgroundg = green;
                    backgroundb = blue;
                }
                else if( alpha == backgrounda && red == backgroundr &&
                        green == backgroundg && blue == backgroundb ) {
                    alpha = 0;
                }
            }
            buffer.put( alpha );
        }
        buffer.rewind();
    }

    protected int nextPowerOf2( int num ) {
        int ret = 2;
        while( ret < num ) ret *= 2;
        return ret;
    }

    protected int genTextureFromBufferedImage( BufferedImage image ) {
        int tex = -1;
        try {
//            width = nextPowerOf2( image.getWidth() );
//            height = nextPowerOf2( image.getHeight() );
            width = image.getWidth();
            height = image.getHeight();
            ByteBuffer imageBuffer = convertBufferedImageToByteBuffer( image );
            tex = glGenTextures();
            glBindTexture( GL_TEXTURE_2D, tex );
            glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height,
                    0, GL_RGBA, GL_UNSIGNED_BYTE, imageBuffer );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
        }
        catch( IndexOutOfBoundsException e ) {
            e.printStackTrace();
            glDeleteTextures( tex );
            tex = -1;
        }
        catch( OpenGLException e ) {
            e.printStackTrace();
            glDeleteTextures( tex );
            tex = -1;
        }
        return tex;
    }

    protected int genTexture( String file ) {
        try {
            if( System.getProperty( "os.name" ).toLowerCase().contains( "win"  ) ) {
                String fileName = System.getProperty( "user.dir" );
                fileName = fileName.substring( 2 );
                fileName = "file:" + fileName + "\\" + file;
                image = ImageIO.read( new URL( fileName ) );
            }
            else {
                String fileName = "file:" + System.getProperty( "user.dir" ) + "/" + file;
//                System.err.println( fileName );
                image = ImageIO.read( new URL( fileName ) );
            }
            return genTextureFromBufferedImage( image );
        }
        catch( IOException e ) {
            e.printStackTrace();
        }
        // If we reach here, an error happened
        return -1;
    }

    @Override
    public void run() {
        texture = new SimpleTexture( GL_TEXTURE_2D, genTexture( mFile ) );
        if( !mKeepBufferedImage ) {
            image.flush();
            image = null;
        }
        texture.setWidth( width );
        texture.setHeight( height );
    }
}

И класс SimpleTexture:

import org.lwjgl.opengl.GL11;

/**
 * Author: FreezerburnVinny
 * Date: 1/5/12
 * Time: $(TIME}
 */
public class SimpleTexture extends Texture {
    private int mTarget, mName;
    private double mWidth, mHeight;
    private double mx1, mx2, my1, my2;

    public SimpleTexture( int target, int name ) {
        this.mTarget = target;
        this.mName = name;
        this.mWidth = 0.0;
        this.mHeight = 0.0;
        this.mx1 = 0.0;
        this.mx2 = 1.0;
        this.my1 = 0.0;
        this.my2 = 1.0;
    }
    public SimpleTexture( int target, int name, double width, double height ) {
        this.mTarget = target;
        this.mName = name;
        this.mWidth = width;
        this.mHeight = height;
    }

    public SimpleTexture( int target, int name, double width, double height,
                          double x1, double x2, double y1, double y2 ) {
        this.mTarget = target;
        this.mName = name;
        this.mWidth = width;
        this.mHeight = height;
        this.mx1 = x1;
        this.mx2 = x2;
        this.my1 = y1;
        this.my2 = y2;
    }

    public double getTexCoordx1() { return mx2; }
    public double getTexCoordy1() { return my1; }
    public double getTexCoordx2() { return mx2; }
    public double getTexCoordy2() { return my2; }
    public double getTexCoordx3() { return mx1; }
    public double getTexCoordy3() { return my2; }
    public double getTexCoordx4() { return mx1; }
    public double getTexCoordy4() { return my1; }

    public double getWidth() { return mWidth; }
    public double getHeight()  { return mHeight; }
    public int getName() { return mName; }
    public int getTarget() { return mTarget; }

    public void setWidth( double width ) { this.mWidth = width; }
    public void setHeight( double height) { this.mHeight = height; }

    public void bind() {
        if( Texture.lastBound != mName ) {
            GL11.glBindTexture( mTarget, mName );
            Texture.lastBound = mName;
        }
    }

    public boolean isValidTexture() {
        if( mName == -1 ) return false;
        return GL11.glIsTexture( mName );
    }

    @Override
    public void restart() {
    }

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }

    @Override
    public void stopAt( int frame ) {
    }

    @Override
    public void stopAfterFullCycle() {
    }

    @Override
    public int numFrames() {
        return 1;
    }
}

РЕДАКТИРОВАТЬ: Упс, чуть не забыл, что SimpleTexture полагался на класс Texture. Ну вот:

/**
 * Author: FreezerburnVinny
 * Date: 1/10/12
 * Time: $(TIME}
 */
public abstract class Texture {
    protected static int lastBound = -1;

    public abstract double getTexCoordx1();
    public abstract double getTexCoordx2();
    public abstract double getTexCoordx3();
    public abstract double getTexCoordx4();

    public abstract double getTexCoordy1();
    public abstract double getTexCoordy2();
    public abstract double getTexCoordy3();
    public abstract double getTexCoordy4();

    public abstract double getWidth();
    public abstract double getHeight();
    public abstract int getName();
    public abstract int getTarget();

    public abstract void setWidth( double width );
    public abstract void setHeight( double height );

    public abstract void bind();

    public abstract boolean isValidTexture();

    public abstract void restart();
    public abstract void pause();
    public abstract void resume();
    public abstract void stopAt( int frame );
    public abstract void stopAfterFullCycle();
    public abstract int numFrames();
}
person Freezerburn    schedule 07.01.2013
comment
Я нашел медленный, но работающий метод: 1. Загрузить изображение 2. Создать BufferedImage с рабочими свойствами 3. Нарисовать изображение в BufferedImage 4. Использовать его Спасибо, что направили меня в правильном направлении! (преобразования, форматы и т.д..) - person Jan Weber; 08.01.2013
comment
Ах, да, на самом деле это почти то же самое, что вы делаете с рисованием на чистом Java. (создайте совместимое изображение, нарисуйте загруженное изображение на совместимое) Рад, что смог вести вас в правильном направлении :) - person Freezerburn; 08.01.2013