Search code examples
javaopenglshaderlwjgl

Why is the texture coordinate in my fragment shader always (0, 0)?


I'm using LWJGL to draw "tiles", or textured 2D squares on the screen. However, the texture coordinate is always (0, 0) and therefore the textured square only uses the first pixel colour to fill it.

This is my vertex shader:

#version 330 core

in vec4 in_Position;
in vec4 in_Color;
in vec2 in_TextureCoord;

out vec4 pass_Color;
out vec2 pass_TextureCoord;

void main(void) {
    gl_Position = in_Position;

    pass_Color = in_Color;
    pass_TextureCoord = in_TextureCoord;
} 

And this is my fragment shader:

#version 330 core

uniform sampler2D texture_diffuse;

in vec4 pass_Color;
in vec2 pass_TextureCoord;

out vec4 out_Color;

void main(void) {
    out_Color = pass_Color;
    // Override out_Color with our texture pixel
    out_Color = texture(texture_diffuse, pass_TextureCoord);
} 

And this is essentially the code I'm using to draw the square:

ARBShaderObjects.glUseProgramObjectARB(shaderProgram);  

glBindTexture(GL_TEXTURE_2D, sprite.getId());

glBegin(GL11.GL_QUADS); 
glVertex2d(screenMinX, screenMinY);
glTexCoord2d(0.0, 0.0);
glVertex2d(screenMaxX, screenMinY);
glTexCoord2d(1.0, 0.0);
glVertex2d(screenMaxX, screenMaxY);
glTexCoord2d(1.0, 1.0);
glVertex2d(screenMinX, screenMaxY);
glTexCoord2d(0.0, 1.0);
glEnd();

// release the shader
ARBShaderObjects.glUseProgramObjectARB(0);

I cannot fix it because I don't know how the above code works in the first place. I have not told the shaders what in_Position, in_Color or in_TextureCoord are but the first two seem to work just fine. It is in_TextureCoord, which is eventually passed to the fragment shader, that seems to have a constant value of (0, 0) - I have determined that by setting the output colour of the fragment shader to have one of the channels equal to the X-coordinate of the texture coordinate. It remained a solid colour throughout the square, indicating that there was no change in texture coordinate.

The square produced with the code above should be textured but instead is painted a solid colour - the first pixel of the texture given. How can I change the code to make the texture coordinate change accordingly? If I appear to have some misunderstanding to how this all fits together, please correct me.

This is the tutorial I used to try to accomplish the above.

p.s. I am aware that the Java snippet is using deprecated immediate-mode, but I don't know how to use glDrawArrays or any other commonly suggested method to accomplish the same. Could you help me to change this?


Solution

  • I am aware that the Java snippet is using deprecated immediate-mode, but I don't know how to use glDrawArrays or any other commonly suggested method to accomplish the same. Could you help me to change this?

    Since you do not need the attribute in_Color anymore, you have to delete the attribute from the vertex shader (and of course also pass_Color from the vertex shader and the fragment shader).
    Otherwise, you have to expand my solution logically by the color attribute.

    Set up an array for the vertex positions an for the texture coordinates:

    float[] posData = {
        screenMinX, screenMinY, 0.0, 1.0,
        screenMaxX, screenMinY, 0.0, 1.0,
        screenMaxX, screenMaxY, 0.0, 1.0,
        screenMinX, screenMaxY, 0.0, 1.0 };
    
    float[] texData = { 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 };
    

    Generate a vertex array object:

    int  vaoObj = glGenVertexArrays();
    glBindVertexArray(vaoObj);
    

    Generate array buffers for the vertices and texture coordinates, enable the attribute indices and associate them buffers to the attribute indices:

    FloatBuffer posBuffer = MemoryUtil.memAllocFloat(posData.length);
    posBuffer.put(posData).flip();
    FloatBuffer texBuffer = MemoryUtil.memAllocFloat(texData.length);
    texBuffer.put(texData).flip();
    
    int vboPosObj = glGenBuffers();
    glBindBuffer(GL_ARRAY_BUFFER, vboPosObj);
    glBufferData(GL_ARRAY_BUFFER, posBuffer, GL_STATIC_DRAW);
    
    // index 0 to associate with "in_Position"            
    glVertexAttribPointer(0, 4, GL_FLOAT, false, 0, 0); 
    glEnableVertexAttribArray(0); // 0 = attribute index of "in_Position"
    
    int vboTexObj = glGenBuffers();
    glBindBuffer(GL_ARRAY_BUFFER, vboTexObj);
    glBufferData(GL_ARRAY_BUFFER, texBuffer, GL_STATIC_DRAW);
    
    // index 0 to associate with "in_TextureCoord"
    glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);
    glEnableVertexAttribArray(1); // 1 = attribute index of "in_TextureCoord"
    

    Release the vertex array object:

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
    


    You have to specify the attribute indices of the attributes in_Position and in_TextureCoord.

    Either you use explicit layout specifications in the vertex shader:

    layout (location = 0) in vec4 in_Position;
    layout (location = 1) in vec2 in_TextureCoord;
    

    Or you specify the attribute indices in the shader program, right before you link the shader program (glLinkProgram).

    glBindAttribLocation(shaderProgramID, 0, "in_Position");
    glBindAttribLocation(shaderProgramID, 1, "in_TextureCoord");
    


    If the object is to be drawn, it is sufficient to bind the Vertex Array Object:

    glBindVertexArray(vaoObj);
    glDrawArrays(GL_QUADS, 0, 4); // 4 = number of vertices 
    glBindVertexArray(0);
    

    Note, if a buffer objects or a vertex array object is not further used, it has to be deleted, to prevent memory leaks. Buffer objects are deleted by glDeleteBuffers and vertex array objects are deleted by glDeleteVertexArrays. Buffer objects are not "created under" vertex array objects, it is not sufficient to delete the vertex array object only (see OpenGL Vertex Array/Buffer Objects)