Search code examples
opengllibgdxbuffertextureslwjgl

Loading generated texture data is inconclusive in Libgdx/Lwjgl


The following data format:

final int width = 256;
final int height = 256;
final float[][][] data = new float[width][height][4];
FloatBuffer dataBuf;
int textureHandle;
FrameBuffer testFrame;
@Override
public void create () {
    for (int i = 0; i < width; i++) {
        for (int j = 0; j < height; j++) {
            data[i][j][0] = 0.4f; /* r */
            data[i][j][1] = 0.38f; /* g */
            data[i][j][2] = 0.2f; /* b */
            data[i][j][3] = 0.9f; /* a */
        }
    }
    dataBuf = ByteBuffer.allocateDirect( Float.BYTES * 4 * width * height ).asFloatBuffer();
    for (float[][] dat : data) { /* Float Byte size * RGBA * width * height */
        for (float[] floats : dat) {
            dataBuf.put(floats, 0, 4);
        }
    }
    dataBuf.position(0); /* reset the caret position to the beginning of the array */
    textureHandle = Gdx.gl.glGenTexture();
    Gdx.gl.glActiveTexture(GL20.GL_TEXTURE1);
    Gdx.gl.glBindTexture(GL30.GL_TEXTURE_2D, textureHandle);
    Gdx.gl.glTexParameteri(GL30.GL_TEXTURE_2D, GL30.GL_TEXTURE_MIN_FILTER, GL30.GL_NEAREST);
    Gdx.gl.glTexParameteri(GL30.GL_TEXTURE_2D, GL30.GL_TEXTURE_MAG_FILTER, GL30.GL_LINEAR);
    Gdx.gl.glTexImage2D(
        GL30.GL_TEXTURE_2D, 0, GL30.GL_RGBA32F,
        width, height, 0, GL30.GL_RGBA, GL30.GL_FLOAT, dataBuf
    );
}

The Shaders are behaving correctly, as they were tested with Framebuffer objects, and they display the correct contents of the Framebuffers.

However when the generated texture is rendered, it seems to deviate from the original color/value. In most cases the values provided by the FloatBuffers result in a black texture, sometimes there is an unexpected color(e.g. lime-green instead of beige).

Unfortunately I couldn't play with glPixelStorei much, because the interface is missing most of its parameters. But in any case glGetError is always returning with 0, so I suspect the data is somehow incorrectly compiled into the dataBuf byte-stream.

What could be the problem here?

Edit: Some debugging details:

  • glGetError() is always zero
  • the individual components seem to have a rough idea of the data, but most of the values produce a black texture:
    • r: 1.0f, g: 1.0f, b: 1.0f, a: 1.0f --> black screen
    • r: 0.9f, g: 0.9f, b: 0.9f, a: 0.9f --> white screen
    • r: 0.9f, g: 0.0f, b: 0.0f, a: 0.9f --> red screen
    • r: 0.0f, g: 0.9f, b: 0.0f, a: 0.9f --> green screen
    • r: 0.0f, g: 0.0f, b: 0.9f, a: 0.9f --> blue screen
    • r: 0.5f, g: 0.5f, b: 0.5f, a: 0.5f --> black screen
    • r: 0.4f, g: 0.32f, b: 0.2f, a: 0.9f --> green screen
  • The above I suspect is because there is a conversion error between the floating point values in dataBuf and openGL-s GL_FLOAT when uploading the texture
  • Otherwise the shaders and setup works correctly, as it was tested with a Framebuffer's color attachement and works as expected with all of the above values. The differente there was that the color texture was not generated by hand, but rendered into the framebuffer via glClear(GL_COLOR_BUFFER_BIT);
  • Using Integer buffers are also working as expected(integer array, values 0-255) :
        dataBuf.position(0);
        Gdx.gl.glTexImage2D(
            GL30.GL_TEXTURE_2D, 0, GL30.GL_RGBA,
            width, height, 0, GL30.GL_RGBA, GL30.GL_UNSIGNED_INT, dataBuf
        );
  • Same behavior is present with LWJGL3 backend

Solution

  • I am able to replicate the issue with the given code, and it can be fixed with a one-line change. Your FloatBuffer defaults to big-endian byte order, and it looks like libGDX, LWJGL, and/or OpenGL expect little-endian. I made your example into an executable test case here: https://github.com/yellowstonegames/SquidLib/blob/master/squidlib/src/test/java/squidpony/gdx/tests/issues/Issue6516.java#L37 (the test case doesn't depend on the library as a whole, it just is handy to test libGDX issues in a project that already depends on libGDX and has assets available). My fix is to change:

    dataBuf = ByteBuffer.allocateDirect(Float.BYTES * 4 * width * height).asFloatBuffer();
    

    to:

    dataBuf = ByteBuffer.allocateDirect(Float.BYTES * 4 * width * height).order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer();
    

    It looks like everything else you had is correct; I get the intended muddy brown color when I draw that texture, instead of lime green.