OpenGL纹理颜色错误

5
我已经写了一个简单的程序,创建了一个正交透视图,并将包含PNG格式的纹理放在一个四边形上。然而,我无法弄清楚为什么一些颜色会显示混乱。
PNG文件看起来像这样(中间的白色矩形是透明的):
四边形在我的OpenGL程序中看起来像这样:
下面是初始化OpenGL的代码以及OpenGL线程调用的方法中发生的事情。我使用JOGL。
public void init(GLAutoDrawable gLDrawable) {
    gl.glGenTextures(1, textureId, 0);
    gl.glBindTexture(GL2.GL_TEXTURE_2D, textureId[0]);
    gl.glTexParameterf(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_NEAREST);
    gl.glTexParameterf(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_LINEAR);
    gl.glTexParameterf(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_S, GL2.GL_REPEAT);
    gl.glTexParameterf(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_T, GL2.GL_REPEAT);

    BufferedImage image = null;
    try {
        image = ImageIO.read(new File("d:\\temp\\projects\\openglTest1\\texTest.png"));
    } catch (IOException e1) {e1.printStackTrace();}
    DataBufferByte dataBufferByte = (DataBufferByte) image.getRaster().getDataBuffer();
    Buffer imageBuffer = ByteBuffer.wrap(dataBufferByte.getData());
    gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL2.GL_RGBA, image.getWidth(), image.getHeight(), 0, GL2.GL_RGBA, GL.GL_UNSIGNED_BYTE, imageBuffer);

    gl.glEnable(GL2.GL_TEXTURE_2D);
    gl.glBlendFunc(GL2.GL_ONE, GL2.GL_ONE_MINUS_SRC_ALPHA); 
    gl.glEnable(GL2.GL_BLEND_SRC);      

    gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    gl.glClearDepth(1.0f);
    gl.glEnable(GL.GL_DEPTH_TEST);
    gl.glDepthFunc(GL.GL_LEQUAL);
    gl.glHint(GL2ES1.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);     
}

//this is called by the OpenGL Thread
public void display(GLAutoDrawable gLDrawable) {
    gl.glClear(GL.GL_COLOR_BUFFER_BIT);
    gl.glClear(GL.GL_DEPTH_BUFFER_BIT);

    gl.glEnableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
    gl.glEnableClientState(GLPointerFunc.GL_TEXTURE_COORD_ARRAY);
    gl.glFrontFace(GL2.GL_CCW);
    gl.glVertexPointer(3, GL.GL_FLOAT, 0, vertexBuffer);
    gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, textureBuffer);
    gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_BYTE, indexBuffer);
    gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
    gl.glDisableClientState(GL2.GL_TEXTURE_COORD_ARRAY);
}

对我来说这很令人困惑,因为虽然我不是OpenGL专家,但在使用这些命令之前我尝试了解它们的作用。实际上,我在Android上也做了同样的事情,一切都正常显示,但是在Java中使用JOGL时,我得到了这里描述的结果。我唯一不同的地方是加载png图像的方式。在Adroid上有一个辅助方法,如下:

GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmapStatic, 0);

在使用JOGL时,我通过以下方式进行自己的加载:

try {
   image = ImageIO.read(new File("d:\\temp\\projects\\openglTest1\\texTest.png"));
} catch (IOException e1) {e1.printStackTrace();}
DataBufferByte dataBufferByte = (DataBufferByte) image.getRaster().getDataBuffer();
Buffer imageBuffer = ByteBuffer.wrap(dataBufferByte.getData());
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL2.GL_RGBA, image.getWidth(), image.getHeight(), 0, GL2.GL_RGBA, GL.GL_UNSIGNED_BYTE, imageBuffer);

如上所述。

==更新==

根据jcadam的评论,我已尝试将像素数据的格式设置为GL_BGRA:

gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL2.GL_RGBA, image.getWidth(), image.getHeight(), 0, GL2.GL_BGRA, GL.GL_UNSIGNED_BYTE, imageBuffer);

颜色仍然混乱,但这次混乱的方式不同: enter image description here 如何确定我的png图像是哪种特定格式?
== 更新2 - 解决方案实现 ==
首先,我要感谢jcadam,rotoglup和Tim指导我走上了正确的方向。
简而言之,问题在于Java解码图像时像素的排序方式并不总是适合传递到OpenGL。更准确地说,如果您的图像中没有Alpha通道,则没问题,但如果您有alpha通道,则排序是错误的,一些颜色会混乱。
现在,我开始制作自己的手动实现,对于32位PNG和24位JPEG效果很好:
public void texImage2D(File imageLocation,GL gl) {

    BufferedImage initialImage = null;
    try {
        initialImage = ImageIO.read(imageLocation);
    } catch (IOException e1) {
        throw new RuntimeException(e1.getMessage(), e1);
    }

    int imgHeight = initialImage.getHeight(null);
    int imgWidth = initialImage.getWidth(null);
    ColorModel cm = initialImage.getColorModel();
    boolean hasAlpha = cm.hasAlpha();

    Buffer buffer = null;
    int openGlInternalFormat = -1;
    int openGlImageFormat = -1;


    if(!hasAlpha) {
        DataBufferByte dataBufferByte = (DataBufferByte) initialImage.getRaster().getDataBuffer();
        buffer = ByteBuffer.wrap(dataBufferByte.getData());
        openGlInternalFormat = GL2.GL_RGB;
        openGlImageFormat = GL2.GL_BGR;
    } else {
        openGlInternalFormat = GL2.GL_RGBA;
        openGlImageFormat = GL2.GL_RGBA;
        WritableRaster raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, imgWidth, imgHeight, 4, null);
        ComponentColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
                new int[] { 8, 8, 8, 8 }, 
                true, false, 
                ComponentColorModel.TRANSLUCENT, 
                DataBuffer.TYPE_BYTE);
        BufferedImage bufImg = new BufferedImage(colorModel,
                raster, false,
                null);

        Graphics2D g = bufImg.createGraphics();
        g.drawImage(initialImage, null, null);

        DataBufferByte imgBuf = (DataBufferByte) raster.getDataBuffer();
        byte[] bytes = imgBuf.getData();
        buffer = ByteBuffer.wrap(bytes);
        g.dispose();
    }
    gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, openGlInternalFormat, imgWidth, imgHeight, 0, openGlImageFormat, GL.GL_UNSIGNED_BYTE, buffer);
}

然而后来我发现JOGL有自己的助手工具,实际上我最终使用了它:
//this code should be called in init(), to load the texture:
    InputStream stream = new FileInputStream("d:\\temp\\projects\\openglTest1\\texTest.png");
    TextureData data = TextureIO.newTextureData(gl.getGLProfile(),stream, false, "png");
    Texture myTexture = TextureIO.newTexture(data);
//this code should be called in the draw/display method, before the vertices drawing call
    myTexture.enable(gl);
    myTexture.bind(gl);

@jcadam 我已经更新了我的问题,我尝试了你说的方法,但是我仍然得到了(不同的)错误结果。 - Shivan Dragon
@jcadm:实际上,如果我在paint.net中打开那个png文件并以100%质量的jpg格式保存它,然后使用GL_BGR加载它,它会正常显示,所有颜色都是应该的。只是对于png文件不起作用。 - Shivan Dragon
3个回答

5

在我看来,这似乎是 ABGR。如果你只看颜色:

png red       (A1,B0,G0,R1)  looks like
opengl red    (R1,G0,B0,A1)

png bluegreen (A1, B1, G1, R0)  looks like 
opengl white  (R1, G1, B1, A0)

png blue      (A1, B1, G0, R0)  looks like 
opengl yellow (R1, G1, B0, A0)

png clear     (A0, B?, G?, R?)  could be
ogl bluegreen (R0, B?, G?, A?)

如果禁用opengl透明度,则Alpha通道将不起作用。


是的,你说得对,透明通道并不重要。Java的图片格式在非alpha图像中按照与OpenGL的BGR相同的方式排序像素字节。只有当存在alpha通道时,Java的格式才不再与任何OpenGL格式匹配(例如BGRA或RGBA)。 - Shivan Dragon

1
嗯...看起来是像素格式的问题。您可以更具体地尝试使用GL_RGBA8、GL_RGBA16等。这是一个8位PNG而不是24或32位吗?没有alpha通道吗(这种情况下请使用GL_RGB而不是GL_RGBA)?

1

仅仅是通过快速搜索(我没有实际使用Java ImageIO的经验),似乎Java有本地的ARGB字节顺序,你可以查看这个源代码以获取灵感。


是的,看起来像是像素排序格式问题。Java的像素排序与OpenGL理解的任何格式都不匹配。在将它们传递给OpenGL之前,我需要重新排列字节数组缓冲区中的像素。 - Shivan Dragon
多么奇怪。这对我来说是新鲜事。我曾经使用过OpenGL和Java,但从未同时使用过 :) - James Adam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接