Search code examples
c++openglglsltextures

OpenGL doesn't sample 2D texture correctly


I use this code for all OpenGL preparations. I took these pieces from my sources, so there can be omitted parts and uninitialized or undeclared variables, etc. but this works. I tried to post only the meaningful parts of the code.

// Texture reading
int w{}, h{}, channels{};
unsigned char * bytes = stbi_load(path.c_str(), &w, &h, &channels, 0);
// produce Texture object

// GL texture code

glCall(glGenTextures(1, &m_textureId));
glCall(glBindTexture(GL_TEXTURE_2D, m_textureId));

glCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT));
glCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT));
glCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
glCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));

glCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_texture->width, m_texture->height, 0, GL_BGR, GL_UNSIGNED_BYTE, m_texture->bytes));
glCall(glGenerateMipmap(GL_TEXTURE_2D));

// Create shader (I omitted this code) and bind texture

glCall(glActiveTexture(GL_TEXTURE0));
glCall(glBindTexture(GL_TEXTURE_2D, m_textureId));

glCall(glUseProgram(m_programId)); // using the shader
// pass texture index into shader
glCall(location = glGetUniformLocation(m_programId, "u_tex0"));
glCall(glUniform1i(location, 0));


// code that sets up uv VBO
/* UV0 positions */
glCall(glGenBuffers(1, &m_VBO[2]));
glCall(glBindBuffer(GL_ARRAY_BUFFER, m_VBO[2]));
glCall(glBufferData(GL_ARRAY_BUFFER, m_model->uv0SizeBytes(), m_model->uv0.data(), GL_STATIC_DRAW));
glCall(glEnableVertexArrayAttrib(m_VAO, 2));
glCall(glVertexAttribPointer(2, m_model->uv0Channels /* 2 here */, GL_FLOAT, GL_FALSE, m_model->uv0Channels * sizeof(m_model->uv0.front()), nullptr));

Shader code:

#version 460 core
layout (location = 0) in vec3 a_pos;
layout (location = 1) in vec3 a_norm;
layout (location = 2) in vec2 a_uv0;

uniform mat4 u_mMVP;
uniform sampler2D u_tex0;

out vec4 v_vertexColor;

void main()
{
    gl_Position = u_mMVP * vec4(a_pos, 1.0);
    v_vertexColor = texture(u_tex0, a_uv0);
}

Output image:
enter image description here

If I draw uv's instead of texture (v_vertexColor = vec4(a_uv0, 0.0, 1.0);), then it looks correct to me:
enter image description here

The target image is this:
enter image description here


Solution

  • You have to look up the texture in the fragment shader. The vertex shader is just executed for each vertex coordinate of a primitive. However the fragment shader is executed for each fragment. The vertex shader outputs are interpolated along the primitives and passed to the fragment shader. Therefore your code lookup the texture just for the 4 corners of the quad.

    Pass the texture coordinates to the fragment shader:

    #version 460 core
    layout (location = 0) in vec3 a_pos;
    layout (location = 1) in vec3 a_norm;
    layout (location = 2) in vec2 a_uv0;
    
    uniform mat4 u_mMVP;
    out vec2 v_uv;
    
    void main()
    {
        gl_Position = u_mMVP * vec4(a_pos, 1.0);
        v_uv = a_uv0;
    }
    

    Use the interpolated texture coordinate in the fragment shader to lookup the texture for each fragment:

    #version 460 core
    
    in vec2 v_uv;
    out vec4 FragColor;
    uniform sampler2D u_tex0;
    
    void main()
    {
        vec4 vertexColor = texture(u_tex0, v_uv);
        FragColor = vertexColor;
    }