LWJGL Texture is flipped upside down when displaye

2020-05-06 22:49发布

问题:

I followed a tutorial for reading a picture and creating a texture out of it, however, it shows up flipped upside down when rendered. The image is power of two.

Main class

public class Main {
public static void main(String args[]) throws IOException{
    Main quadExample = new Main();
    quadExample.start();
}

public void start() throws IOException {
    try {
    Display.setDisplayMode(new DisplayMode(1280,720));
    Display.create();
} catch (LWJGLException e) {
    e.printStackTrace();
    System.exit(0);
}

// init OpenGL
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, 1280, 0, 720, -1, 1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glClearColor(0, 1, 0, 0);

GL11.glEnable(GL11.GL_TEXTURE_2D);

BufferedImage image = TextureLoader.loadImage("C:\\test.png");
final int textureID = TextureLoader.loadTexture(image);

while (!Display.isCloseRequested()) {
    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);

    GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
    GL11.glBegin(GL11.GL_QUADS);
    GL11.glTexCoord2f(0, 0);
    GL11.glVertex2f(0, 0);

    GL11.glTexCoord2f(1, 0);
    GL11.glVertex2f(256, 0);

    GL11.glTexCoord2f(1, 1);
    GL11.glVertex2f(256, 256);

    GL11.glTexCoord2f(0, 1);
    GL11.glVertex2f(0, 256);
    GL11.glEnd();

    Display.update();
}

Display.destroy();
}

}

Texture Loader

public class TextureLoader {
private static final int BYTES_PER_PIXEL = 4;

public static int loadTexture(BufferedImage image) {
    int[] pixels = new int[image.getWidth() * image.getHeight()];
    image.getRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0,
            image.getWidth());

    ByteBuffer buffer = BufferUtils.createByteBuffer(image.getWidth()
            * image.getHeight() * BYTES_PER_PIXEL);

    for (int y = 0; y < image.getHeight(); y++) {
        for (int x = 0; x < image.getWidth(); x++) {
            int pixel = pixels[y * image.getWidth() + x];
            buffer.put((byte) ((pixel >> 16) & 0xFF));
            buffer.put((byte) ((pixel >> 8) & 0xFF));
            buffer.put((byte) (pixel & 0xFF));
            buffer.put((byte) ((pixel >> 24) & 0xFF));

        }
    }

    buffer.flip();

    int textureID = glGenTextures();
    glBindTexture(GL_TEXTURE_2D, textureID);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, image.getWidth(),
            image.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

    return textureID;
}

public static BufferedImage loadImage(String location) {
    try {
        return ImageIO.read(new File(location));
    } catch (IOException e) {
        System.out.println(Errors.IOException);
        e.printStackTrace();
    }
    return null;
}

Is there something wrong with the code or do I have to flip the image before creating the texture?

回答1:

Most image formats store the data top to bottom. Unless you reshuffle the data while loading the image, this is also the sequence in memory after reading the image.

When you create an OpenGL texture from the loaded image, this memory order is maintained unless you explicitly change the order. So the order in texture memory is still top to bottom.

OpenGL does not really have an image/texture orientation. But when you use texture coordinates, they address the texture in the order it's stored in memory. This means for the two extreme values of the t-coordinate:

  • t = 0.0 corresponds to the start of the image in memory, which is the top edge of the image.
  • t = 1.0 corresponds to the end of the image in memory, which is the bottom edge of the image.

Now, looking at your draw calls:

GL11.glTexCoord2f(0, 0);
GL11.glVertex2f(0, 0);

GL11.glTexCoord2f(1, 0);
GL11.glVertex2f(256, 0);

GL11.glTexCoord2f(1, 1);
GL11.glVertex2f(256, 256);

GL11.glTexCoord2f(0, 1);
GL11.glVertex2f(0, 256);

In the default OpenGL coordinate system, the y-coordinate goes bottom to top. So the first two vertices are the bottom vertices of the quad (since they have the smaller y-coordinate), the remaining two are the top two vertices.

Since you used t = 0.0 for the first two vertices, which are at the bottom of the quad, and t = 0.0 corresponds to the top of the image, the top of the image is at the bottom of the quad. Vice versa, you use t = 1.0 for the second two vertices, which are at the top of the quad, and t = 1.0 corresponds to the bottom of the image. Therefore, your image appears upside down.

By far the easiest way to fix this is to change the texture coordinates. Use t = 1.0 for the bottom two vertices, and t = 0.0 for the top two vertices, and the image orientation now matches the orientation of the quad on the screen:

GL11.glTexCoord2f(0.0f, 1.0f);
GL11.glVertex2f(0.0f, 0.0f);

GL11.glTexCoord2f(1.0f, 1.0f);
GL11.glVertex2f(256.0f, 0.0f);

GL11.glTexCoord2f(1.0f, 0.0f);
GL11.glVertex2f(256.0f, 256.0f);

GL11.glTexCoord2f(0.0f, 0.0f);
GL11.glVertex2f(0.0f, 256.0f);

Another option is that you flip the image while reading it in, for example by changing the order of your for loop from:

for (int y = 0; y < image.getHeight(); y++) {

to:

for (int y = image.getHeight() - 1; y >= 0; y--) {

But it's very common to have images in top-down order in memory, and you often don't have control over it if you're using system libraries/frameworks for reading them. So using the texture coordinates to render them in the desired direction is a frequently used approach, and IMHO preferable over shuffling the data around.