Search code examples
c++opengltexturesshader

Multiple [in] attributes in shader - OpenGL


I'm to learn OpenGL 4 and now I working with textures. I would like to apply a texture to a sphere, and my vertex shader looks like this:

#version 410

in vec4 vPosition;
in vec2 texCoord;

uniform mat4 mvpMatrix;
uniform mat4 mvMatrix;
uniform mat3 normalMatrix;
uniform vec3 vLightPosition;

smooth out vec3 vVaryingNormal;
smooth out vec3 vVaryingLightDir;
smooth out vec2 uvCoord;

void main (void)
{
    uvCoord = texCoord;
    vec3 vNormal = vPosition.xyz / vPosition.w;
    vVaryingNormal = normalMatrix * vNormal;
    vec4 vPosition4 = mvMatrix * vPosition;
    vec3 vPosition3 = vPosition4.xyz / vPosition4.w;
    vVaryingLightDir = normalize(vLightPosition - vPosition3);
    gl_Position = mvpMatrix * vPosition;
}

After defining the coordinates and triangulation indexes of a sphere, I build a VAO with the following code:

void SphereShaderProgram::BuildVAO()
{
    // Generate and bind the vertex array object
    glGenVertexArrays(1, &_vao);
    glBindVertexArray(_vao);

    // Generate and bind the vertex buffer object
    glGenBuffers(1, &_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, _vbo);
    glBufferData(GL_ARRAY_BUFFER, _sphereCoordinates.size() * sizeof(float), &_sphereCoordinates[0], GL_STATIC_DRAW);

    // Generate and bind the index buffer object
    glGenBuffers(1, &_ibo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, _sphereIndexes.size() * sizeof(unsigned int), &_sphereIndexes[0], GL_STATIC_DRAW);

    // Generate and bind texture
    _texture = LoadTexture("ball.bmp");

    LoadAttributeVariables();

    glBindVertexArray(0);
}

GLuint ProgramManager::LoadTexture(const char* imagepath)
{    
    unsigned int width, height;
    unsigned char * data = LoadBMP(imagepath, &width, &height);

    GLuint textureID;
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_2D, textureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, data);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    return textureID;
}

To load the attributes, I did the following (before using texture):

void SphereShaderProgram::LoadAttributeVariables()
{
    // Vertex Attributes
    GLuint VertexPosition_location = glGetAttribLocation(GetProgramID(), "vPosition");
    glEnableVertexAttribArray(VertexPosition_location);
    glVertexAttribPointer(VertexPosition_location, 3, GL_FLOAT, GL_FALSE, 0, 0);
}

Now, however, I have an array (_sphereTexCoords) which contains the texture coordinates and I don't really know how to pass it to the vertex shader as the vertex coordinates are passed. Do I have to create another VBO? I though of doing something like this:

GLuint TextureCoord_Location = glGetAttribLocation(GetProgramID(), "texCoord");
glEnableVertexAttribArray(TextureCoord_Location);
glVertexAttribPointer(TextureCoord_Location, 2, GL_FLOAT, GL_FALSE, 0, 0);

But I don't really know how to specify that this is related to the _sphereTexCoords array.

EDIT

I tried this, but it didn't work.

void SphereShaderProgram::BuildVAO()
{
    // Generate and bind the vertex array object
    glGenVertexArrays(1, &_vao);
    glBindVertexArray(_vao);

    // Generate and bind the vertex buffer object
    glGenBuffers(2, _vbo);
    glBindBuffer(GL_ARRAY_BUFFER, _vbo[0]);
    glBufferData(GL_ARRAY_BUFFER, _sphereCoordinates.size() * sizeof(float), &_sphereCoordinates[0], GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, _vbo[1]);
    glBufferData(GL_ARRAY_BUFFER, _textureCoordinates.size() * sizeof(float), &_textureCoordinates[0], GL_STATIC_DRAW);

    // Generate and bind the index buffer object
    glGenBuffers(1, &_ibo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, _sphereIndexes.size() * sizeof(unsigned int), &_sphereIndexes[0], GL_STATIC_DRAW);

    // Generate and bind texture
    _texture = LoadTexture("ball.bmp");

    LoadAttributeVariables();

    glBindVertexArray(0);
}

void SphereShaderProgram::LoadAttributeVariables()
{
    // Vertex Attributes
    GLuint VertexPosition_location = glGetAttribLocation(GetProgramID(), "vPosition");
    glEnableVertexAttribArray(VertexPosition_location);
    glVertexAttribPointer(VertexPosition_location, 3, GL_FLOAT, GL_FALSE, 0, 0);

    GLuint TextureCoord_Location = glGetAttribLocation(GetProgramID(), "texCoord");
    glEnableVertexAttribArray(TextureCoord_Location);
    glVertexAttribPointer(TextureCoord_Location, 2, GL_FLOAT, GL_FALSE, 0, 0);
}

I get an error when binding the program.


Solution

  • You're very close. The only thing I see missing is that you do not bind the correct buffer before calling glVertexAttribPointer(). When you call glVertexAttribPointer(), you specify that the data for the attribute will be pulled from the buffer that is currently bound to GL_ARRAY_BUFFER.

    Your LoadAttributeVariables() method should therefore be:

    GLuint VertexPosition_location = glGetAttribLocation(GetProgramID(), "vPosition");
    glEnableVertexAttribArray(VertexPosition_location);
    glBindBuffer(GL_ARRAY_BUFFER, _vbo[0]);
    glVertexAttribPointer(VertexPosition_location, 3, GL_FLOAT, GL_FALSE, 0, 0);
    
    GLuint TextureCoord_Location = glGetAttribLocation(GetProgramID(), "texCoord");
    glEnableVertexAttribArray(TextureCoord_Location);
    glBindBuffer(GL_ARRAY_BUFFER, _vbo[1]);
    glVertexAttribPointer(TextureCoord_Location, 2, GL_FLOAT, GL_FALSE, 0, 0);