Search code examples
opengllwjgl

LWJGL texture with shaders produced skewed image


I'm trying to do 2D graphics in orthogonal mode. The code loads a picture of a cat and 2 simple shaders, which just pass through their input, unmodified. I expect the program to display the picture of the cat (or at least a part of it) in the middle of the screen, without any rotation or skew.

The program executes successfully, but I can't figure out why the result looks like this:

Screenshot

An OpenGL guru might spot the problem quickly, but I can't find it. I have the feeling that the problem might be at the "Create buffer for vertex and texture coordinates" part, but everything looked okay.

The cat image:

cat

Vertex shader:

#version 150 core

in vec4 in_Position;
in vec2 in_TextureCoord;

out vec2 pass_TextureCoord;

void main(void) {
    gl_Position = in_Position;

    pass_TextureCoord = in_TextureCoord;
}

Pixel shader:

#version 150 core

uniform sampler2D texture_diffuse;
in vec2 pass_TextureCoord;
out vec4 out_Color;

void main(void) {
    out_Color = texture(texture_diffuse, pass_TextureCoord);
}

Java (LWJGL) code:

package lwjgl_test1;

import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.*;

import javax.imageio.ImageIO;

import org.lwjgl.*;
import org.lwjgl.opengl.*;

import static org.lwjgl.glfw.GLFW.*;
import java.util.concurrent.TimeUnit;
import static org.lwjgl.opengl.GL11.*;


public class Main {
    public static void main(String[] args) {
        try {
            if (!glfwInit()) {
                throw(new Exception("Can't init glfw."));
            }

            /*
             * Create Window
             */
            glfwWindowHint(GLFW_RESIZABLE, 0);
            long windowGlID = glfwCreateWindow(1024, 768, "Example OpenGL App", 0, 0);

            glfwSetWindowPos(windowGlID, 50, 50);
            glfwMakeContextCurrent(windowGlID);
            glfwShowWindow(windowGlID);

            /*
             * Initialize OpenGL
             */
            GL.createCapabilities();

            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            glOrtho(0, 1024, 768, 0, 0, 1);
            glMatrixMode(GL_MODELVIEW);

            /*
             * Load texture
             */
            int cat = loadTexture("cat.png");

            /*
             * Load shaders
             */
            int vertexShader = loadShader("vertex_shader.txt", GL20.GL_VERTEX_SHADER);
            int pixelShader = loadShader("pixel_shader.txt", GL20.GL_FRAGMENT_SHADER);

            int pId = GL20.glCreateProgram();
            GL20.glAttachShader(pId, vertexShader);
            GL20.glAttachShader(pId, pixelShader);

            // Position information will be attribute 0
            GL20.glBindAttribLocation(pId, 0, "in_Position");
            // Textute information will be attribute 1
            GL20.glBindAttribLocation(pId, 1, "in_TextureCoord");

            GL20.glLinkProgram(pId);
            GL20.glValidateProgram(pId);

            exitOnGLError("Compiling shaders failed.");

            /*
             * Create buffer for vertex and texture coordinates
             */
            float size = 120.0f;

            FloatBuffer vertex_data = BufferUtils.createFloatBuffer(20);
            vertex_data.put(new float[] { -size, -size, 0f, 0f, 0f });  // (Vx, Vy, Vz, Tx, Ty)
            vertex_data.put(new float[] { size, -size, 0f, 0f, 1f });
            vertex_data.put(new float[] { size, size, 0f, 1f, 1f });
            vertex_data.put(new float[] { -size, size, 0f, 1f, 0f });
            vertex_data.flip();

            int vbo_vertex_handle = GL15.glGenBuffers();
            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo_vertex_handle);
            GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertex_data, GL15.GL_STATIC_DRAW);

            GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 2 * 4, 0);   // mark vertex coordinates
            GL20.glVertexAttribPointer(1, 2, GL11.GL_FLOAT, false, 3 * 4, 3 * 4);   // mark texture coordinates

            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
            GL30.glBindVertexArray(0);

            exitOnGLError("Creating buffers failed.");

            /*
             * Main rendering loop
             */
            while(true) {
                /*
                 * Clear screen
                 */
                glClearColor(0.0f, 1.0f, 1.0f, 0.0f);
                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

                /*
                 * Apply shader program
                 */
                GL20.glUseProgram(pId);

                // Bind the texture
                GL13.glActiveTexture(GL13.GL_TEXTURE0);
                GL11.glBindTexture(GL11.GL_TEXTURE_2D, cat);

                /*
                 * Draw (use buffers)
                 */
                GL20.glEnableVertexAttribArray(0);
                GL20.glEnableVertexAttribArray(1);

                GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo_vertex_handle);
                GL11.glDrawArrays(GL11.GL_QUADS, 0, 4); // Draw an entity with 4 vertices
                GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

                exitOnGLError("Draw failed.");

                GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
                GL20.glUseProgram(0); // deselect

                /*
                 * Swap buffers
                 */
                glfwSwapBuffers(windowGlID);

                /*
                 * Events
                 */
                glfwPollEvents();
                if (glfwWindowShouldClose(windowGlID)) {
                    break;
                }

                TimeUnit.MILLISECONDS.sleep(10);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static int loadTexture(String path) throws Exception {
        int[] pixels = null;
        BufferedImage image = null;

        image = ImageIO.read(new FileInputStream(path));
        int width = image.getWidth();
        int height = image.getHeight();
        pixels = new int[width * height];
        image.getRGB(0, 0, width, height, pixels, 0, width);

        int[] data = new int[width * height];
        for (int i = 0; i < width * height; i++) {
            int a = (pixels[i] & 0xff000000) >> 24;
            int r = (pixels[i] & 0xff0000) >> 16;
            int g = (pixels[i] & 0xff00) >> 8;
            int b = (pixels[i] & 0xff);

            data[i] = a << 24 | b << 16 | g << 8 | r;
        }

        IntBuffer intBuffer1 = ByteBuffer.allocateDirect(data.length << 2).order(ByteOrder.nativeOrder()).asIntBuffer();
        intBuffer1.put(data).flip();

        int result = glGenTextures();
        glBindTexture(GL_TEXTURE_2D, result);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, intBuffer1);
        glBindTexture(GL_TEXTURE_2D, 0);

        exitOnGLError("Loading texture '"+ path +"' failed.");

        return result;
    }

    private static int loadShader(String filename, int type) {
        StringBuilder shaderSource = new StringBuilder();
        int shaderID = 0;

        try {
            BufferedReader reader = new BufferedReader(new FileReader(filename));
            String line;
            while ((line = reader.readLine()) != null) {
                shaderSource.append(line).append("\n");
            }
            reader.close();
        } catch (IOException e) {
            System.err.println("Could not read file.");
            e.printStackTrace();
            System.exit(-1);
        }

        shaderID = GL20.glCreateShader(type);
        GL20.glShaderSource(shaderID, shaderSource);
        GL20.glCompileShader(shaderID);

        if (GL20.glGetShaderi(shaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("Could not compile shader.");
            System.exit(-1);
        }

        return shaderID;
    }

    private static void exitOnGLError(String errorMessage) throws Exception {
        int errorValue = GL11.glGetError();

        if (errorValue != GL11.GL_NO_ERROR) {
            throw new Exception(errorMessage);
        }
    }
}

Solution

  • The problem lies in the stride parameter in this lines:

    GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 2 * 4, 0);
    GL20.glVertexAttribPointer(1, 2, GL11.GL_FLOAT, false, 3 * 4, 3 * 4);
    

    Stride tells OpenGL how many bytes apart from each other the begin of two consecutive entries are. Since you are using 5 floats per vertex, this has to be 5 * 4 in both lines:

    GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 5 * 4, 0);
    GL20.glVertexAttribPointer(1, 2, GL11.GL_FLOAT, false, 5 * 4, 3 * 4);