Search code examples
javac++opengllwjgl

Why do the textured image colors are not the same as the origin?


I'm using LWJGL (OpenGL for Java) library for texture mapping. Here is the code to read Image from file:

BufferedImage image = ImageIO.read(new File(url));

The code for getting Data Raster (Pixels of image) as byte array:

DataBufferByte imageByteBuffer = ((DataBufferByte)image.getRaster().getDataBuffer());
byte[] bytePixels = imageByteBuffer.getData();

Now the code of creating and putting the "bytePixels" array in byte buffer:

pixels = BufferUtils.createByteBuffer(bytePixels.length);
pixels.put(bytePixels);
pixels.flip();

Here for binding all of that to buffer:

id = GL11.glGenTextures();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, image.getWidth(), image.getHeight(), 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, pixels);

The problem is, the colors of image textured aren't as the original image colors!!

Original Picture:

enter image description here

Textured image:

enter image description here


This answer to OpenGL Renders texture with different color than original image?, can't solve this issue, because GL_BGR is not valid in lwjgl Class GL11!


Solution

  • The issue is that the red and blue color channel are swapped.

    At OpenGL there is the possibility, to use the GL_BGR format, which specifies a internal format where the color channels are swapped (in compare to GL_RGB).

    See OpenGL 4 Refpages - glTexImage2D
    and OpenGL Renders texture with different color than original image?.

    At OpenGL ES you have to manually swap the red and blue color channel, because the internal format GL_BGR is missing.

    See OpenGL ES 3.0 Refpages - glTexImage2D
    and lwjgl - class GL11.

    pixels = BufferUtils.createByteBuffer(bytePixels.length);
    pixels.put(bytePixels);
    pixels.flip();
    for (int i = 0; i < pixels.length; i += 3) {
        byte t = pixels[i];
        pixels[i] = pixels[i+2];
        pixels[i+2] = t;
    }
    

    Another possibility would be given in OpenGL ES 3.0 or by OpenGL extension EXT_texture_swizzle:

    Since OpenGL ES 3.0 you can use the texture swizzle parameters to swap the color channels. See glTexParameter:

    GL_TEXTURE_SWIZZLE_R

    Sets the swizzle that will be applied to the r component of a texel before it is returned to the shader. Valid values for param are GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA, GL_ZERO and GL_ONE. If GL_TEXTURE_SWIZZLE_R is GL_RED, the value for r will be taken from the first channel of the fetched texel. If GL_TEXTURE_SWIZZLE_R is GL_GREEN, the value for r will be taken from the second channel of the fetched texel. ...

    This means the color channels will be swapped when the texture is looked up, by setting the following texture parameters to the texture object:

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
    


    The relevant part in the specification can be found at OpenGL ES 3.0.5 Specification; 3.8.14 Texture State; page 162

    To check if an OpenGL extension is valid, glGetString(GL_EXTENSIONS) can be use, which returns a space-separated list of supported extensions.

    See alos android bitmap pixel format for glTexImage2D.