1

我试图在 LWJGL 中使用纹理,结果发现 RBG-png 有点歪斜。示例: 原始图片/纹理

加载代码是lwjgl wiki space入侵者示例的98%.. 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;
}

TextureLoader.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;
    }
4

2 回答 2

2

我知道这是一个老问题,但我刚才自己也遇到了这个问题。希望其他人会受益,假设您已经自己解决了这个问题。

纹理歪斜和变色的原因是因为它与您的UNPACK_ALIGNMENT. UNPACK_ALIGNMENT据我了解,每行中的字节数必须是设置为(默认为 4)的倍数。

对于 4 分量格式,这不是问题,因为每个像素由 4 个字节组成,因此任何维度的图像都可以正确对齐。但是对于其他格式,数据会被填充以保持正确对齐,这会导致这样的问题。

您可以更改图像的大小以使其正确对齐((width * formatComponents) % 4 == 0),将格式更改为 4-component 格式,或更改为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

于 2013-04-24T03:48:42.540 回答
0

您可能对 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();
}
于 2013-01-07T20:53:44.387 回答