OpenGL texture colors are wrong
Asked Answered
M

3

5

I've made a simple program that cretes an Ortho perspective, and puts a texture containing a png on a quad However, I can't figure out why some of the colors are displayed all jumbled.

The png looks like this (the white rectangle in the middle is transparent):

enter image description here

The quad in my OpenGL program looks like this:

enter image description here

Below is the code for initializing OpenGL as well as what goes on in the method called by the OpenGL thread. I'm using 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);
}

This is puzzling to me because, while I'm not an OpenGL expert I tried to understand what all the above OpenGL commands do before using them. In fact, I've dont the same thing on Android, and everything is displayed ok, but when doing it in Java with JOGL I get this result described here. The only thing I'm doing different is the way I load the png image. On Adroid there's a helper method like this:

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

while with JOGL I'm doing my own loading via:

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);

as detailed above.

==UPDATE==

As per jcadam's comment, I've tried setting the format of the pixel data to GL_BGRA like so:

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

The colors are still jumbled, but it's a different jumble this time:

enter image description here

How can I find out what particular format my png image is in?

== UPDATE 2 - solution implementation ==

Ok, first, I want to thank jcadam, rotoglup and Tim for pointing me in the right direction.

In short, the issue was that the way in which Java is ordering the pixels when decoding an image is not always the good order for passing to OpenGL. More precisely, if you do not have an Alpha Channel in your image, then it's ok but if you do have an alpha channel the order is bad and some colors will be jumbled.

Now, I started off by making my own manual implementation which works ok for 32bit PNGs and 24 bit JPEGs:

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);
}

however I later found out that JOGL has its own helper tools for this, and this is in fact what I ended up using:

//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);
Millner answered 29/2, 2012 at 14:29 Comment(2)
@jcadam I've updated my question, I've tried what you said but I still get a (different) bad resultMillner
@jcadm: actually, if I open that png in paint.net and save it in jpg format, 100% quality, and then I load it using GL_BGR it is displayed ok, all colors are what they should be. It's just for the png that it doesn't work.Millner
T
5

It looks like ABGR to me. If you just look at the colors:

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?)

If opengl transparency is disabled then the alpha channel wouldn't matter.

Tefillin answered 29/2, 2012 at 19:46 Comment(1)
Yes, you're right about the transparency channel not mattering. Java's picture format orders pixels' bytes' in a non-alpha image the same way as OpenGL's BGR. It's when there's an alpha channel that Java's format no longer matches any of OpenGLs formats (such as BGRA or RGBA).Millner
U
1

Hmm... It looks like a pixel format problem. You could get more specific and try GL_RGBA8, GL_RGBA16, etc. Is this an 8-bit PNG rather than 24 or 32? Is there not an alpha channel (in which case use GL_RGB rather than GL_RGBA)?

Unsuspecting answered 29/2, 2012 at 14:41 Comment(0)
D
1

Just out of a quick search (I don't have any actual experience with Java ImageIO), it seems that Java has a native ARGB byte ordering, you may take a look at this source code for inspiration.

Dis answered 29/2, 2012 at 20:5 Comment(2)
Yes, looks like a pixel ordering format issue. Java's pixel ordering doesn't match any of the formats that OpenGL understands. I need to reorder the pixels in the byte array buffer before I can pass them to OpenGL.Millner
How strange. That's new to me. I've used OpenGL and I've used Java. But never at the same time :)Unsuspecting

© 2022 - 2024 — McMap. All rights reserved.