javaopengllwjgl

How do I render a square 2D texture correctly to set of 4 vertices in OpenGL


(Obligatory this is not a homework assignment for a class). I am currently working with LWJGL and the obstacle I face right now on my project is rendering textures to a cube, e.x a set of 8 vertices with position, color, and UV information. Here, I start off with a single 2D face however it's not exactly correctly rendered. I believe I am close but after playing around with it a bunch I'm not sure how to manipulate the information in a way that resembles a full, correctly oriented texture. Allow me to show you what I have so far:

This is the 16x16 texture (scaled to 256x256 so its visible in this post) I am trying to render, red lines for debugging purposes. The diagonal separates the 2 triangles that make up the GL_TRIANGLE_STRIP my code is trying to draw:

Block texture with red debug lines

This is how my code displays it right now:

You can see that it almost looks right, but not quite. This leads me to believe it may be an issue with ordering of vertices or elements.

Here is my code that is responsible for displaying that texture:

    private void renderMenu() {
        // Define the vertices and colors of the cube

        float[] vertices = {
                //Position              Color                       Texture

                // Front face
                -0.5f, -0.5f,  0.5f,    1.0f, 0.0f, 0.0f, 1.0f,     1, 1,
                0.5f, -0.5f,  0.5f,     0.0f, 1.0f, 0.0f, 1.0f,     0, 0,
                -0.5f,  0.5f,  0.5f,    1.0f, 0.0f, 1.0f, 1.0f,     1, 0,
                0.5f,  0.5f,  0.5f,     1.0f, 1.0f, 0.0f, 1.0f,     0, 1};
                

        int[] elementArray = {
                2, 1, 0,
                0, 1, 3 
        };

        //Loading texture image
        String path = "src/main/resources/textures/test_texture16.png";
        IntBuffer width = BufferUtils.createIntBuffer(1);
        IntBuffer height = BufferUtils.createIntBuffer(1);
        IntBuffer channels = BufferUtils.createIntBuffer(1);
        ByteBuffer image = stbi_load(path, width, height, channels, 0);

        //Binding texture ID
        int texId = glGenTextures();
        glEnable(GL_TEXTURE_2D);
        glEnable(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texId);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        glGenerateMipmap(GL_TEXTURE_2D);

        if (image != null) {
            //Loads image to GPU
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width.get(0), height.get(0),
                    0, GL_RGBA, GL_UNSIGNED_BYTE, image);

            //Frees memory containing image
            stbi_image_free(image);
        } else {
            logger.warning("Texture could not be loaded from " + path);
        }

        /*==================================
        Buffer binding and loading
        ====================================*/

        // Create VAO
        int vaoID = glGenVertexArrays();
        glBindVertexArray(vaoID);

        // Create a float buffer of vertices
        FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(vertices.length);
        vertexBuffer.put(vertices).flip();

        // Create VBO upload the vertex buffer
        int vboID = glGenBuffers();
        glBindBuffer(GL_ARRAY_BUFFER, vboID);
        glBufferData(GL_ARRAY_BUFFER, vertexBuffer, GL_STATIC_DRAW);

        // Create the indices and upload
        IntBuffer elementBuffer = BufferUtils.createIntBuffer(elementArray.length);
        elementBuffer.put(elementArray).flip();

        // Create EBO upload the element buffer
        int eboID = glGenBuffers();
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboID);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementBuffer, GL_STATIC_DRAW);

        // Bind the VAO that we're using
        glBindVertexArray(vaoID);

        /*=====================================
        Vertex attribute definitions for shader
        ======================================*/
        int posSize = 3;
        int colorSize = 4;
        int uvSize = 2;
        int floatSizeBytes = 4;
        int vertexSizeBytes = (posSize + colorSize) * floatSizeBytes;


        //Position
        glVertexAttribPointer(0, posSize, GL_FLOAT, false, vertexSizeBytes, 0);
        glEnableVertexAttribArray(0);

        //Color
        glVertexAttribPointer(1, colorSize, GL_FLOAT, false, vertexSizeBytes, posSize * Float.BYTES);
        glEnableVertexAttribArray(1);

        //Texture
        glVertexAttribPointer(2, uvSize, GL_FLOAT, false, vertexSizeBytes, (posSize + colorSize) * Float.BYTES);
        glEnableVertexAttribArray(2);

        /*==================================
        View Matrix setup
        ====================================*/

        // Set up the model-view matrix for rotation
        modelViewMatrix = new Matrix4f();
        modelViewMatrix.translate(new Vector3f(0.0f, 0.0f, -2.0f));
        modelViewMatrix.rotate(angle, new Vector3f(0.0f, 1.0f, 0.0f));


        // Set up the model-view-projection matrix
        Matrix4f modelViewProjectionMatrix = new Matrix4f(projectionMatrix).mul(modelViewMatrix);

        // Pass the model-view-projection matrix to the shader as a uniform
        int mvpMatrixLocation = glGetUniformLocation(shaderProgram.getProgramId(), "modelViewProjectionMatrix");
        glUniformMatrix4fv(mvpMatrixLocation, false, modelViewProjectionMatrix.get(new float[16]));


        // Enable the vertex attribute pointers
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);

        /*==================================
        Drawing
        ====================================*/
        glDrawElements(GL_TRIANGLE_STRIP, elementArray.length, GL_UNSIGNED_INT, 0);


        //Unbind everything
        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);
        glBindVertexArray(0);
        glBindTexture(GL_TEXTURE_2D, 0);

    }

Vertex Shader:

#version 330 core

layout(location = 0) in vec3 position;
layout(location = 1) in vec4 aColor;
layout (location = 2) in vec2 aTexCoords;

uniform mat4 modelViewProjectionMatrix;


out vec4 fColor;
out vec2 fTexCoords;



void main() {
    gl_Position = modelViewProjectionMatrix * vec4(position, 1.0);
    fColor = aColor;
    fTexCoords = aTexCoords;
}

Solution

  • The problem ended up being my stride. I was excluding my texture coordinates.

    This: int vertexSizeBytes = (posSize + colorSize) * floatSizeBytes;

    Should have been this: int vertexSizeBytes = (posSize + colorSize + uvSize) * floatSizeBytes;