GLSL Tutorial – Attribute Variables
Prev: Comm. App=>Shader | Next: Uniform Variables |
As mentioned in the previous section, vertices have attributes that must be fed to the graphics pipeline. To draw an object we need to specify its vertices, and how they are connected to define faces. Furthermore, vertices have attributes, besides position, namely normals and texture coordinates.
To send the vertex data to the shader we have to know the locations of the attributes in the vertex shader. OpenGL allows us to define specific locations for each attribute. Another possibility is to let OpenGL define the locations and query them afterwards.
To specify a location for an attribute we must do it prior to linking the program, or, if the program is already linked, to link it again afterwards.
Consider the following vertex shader excerpt:
#version 330 in vec3 position; in vec3 normal; in vec2 texCoord; ...
The function glBindAttribLocation
can be used to bind a location to an attribute.
[stextbox]void glBindAttribLocation( GLuint program, GLuint index, const GLchar *name);
Params:
- program: the program object handle
- index: the index to bind the attribute to
- name: the name of the vertex attribute
[/stextbox]
Considering the above excerpt, and a program object handle p
, we could bind attribute “position” to location 0 with the following line:
glBindAttribLocation(p, 0, "position");
Don’t forget to link the program after binding the location to the attribute.
Another option is to let OpenGL choose an appropriate location and query it after the program is linked. The query function is glGetAttribLocation
[stextbox]GLint glGetAttribLocation( GLuint program, const GLchar *name);
Params:
- program: the program object handle
- name: the name of the vertex attribute
Return Value:
- the location to which the attribute is bound
[/stextbox]
Example: assuming p
as the program object handle,
GLint vertexLoc; glLinkProgram(p); vertexLoc = glGetAttribLocation(p,"position");
then vertexLoc
will store the location of attribute “position”.
Now that we know where the attributes are located, and that we have defined our arrays, we’re going to see how to send the values for these attributes to the graphics pipeline.
To define these attributes, we initially place the data in an array. In here we are going to consider an array for each attribute, although these can also be interleaved in a single array.
// Data for a set of triangles float pos[ ] = {-1.0f, 0.0f, -5.0f, 1.0f, 1.0f, 0.0f, -5.0f, 1.0f, 0.0f, 2.0f, -5.0f, 1.0f, …}; float textureCoord[ ] = { … }; float normal[ ] = { … };
Considering the index i for all arrays we get the set of attributes for vertex i.
Defining an array of indices allows us to connect these vertices arbitrarily to create primitives. So we can add the following array with indices:
unsigned integer index[ ] = {0, 1, 2, …};
Without the index array, the shaders will consider the vertices sequentially.
Each attribute will be placed in an OpenGL buffer. The set of buffers will be part of an OpenGL Vertex Array Object, or VAO.
First we create a VAO and bind it:
GLuint vao; glGenVertexArrays(1, &vao); glBindVertexArray(vao);
For each individual attribute, we then proceed as follows: first we create a buffer, bind it, fill it with data, and finally associate it with an input attribute of the graphics pipeline.
For instance, considering the array pos
, and the attribute “position”, we could proceed as follows:
GLuint buffer;
glGenBuffers(1, &buffer);
// get the location of attribute "position" in program p
vertexLoc = glGetAttribLocation(p,"position");
// bind buffer for positions and copy data into buffer
// GL_ARRAY_BUFFER is the buffer type we use to feed attributes
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// feed the buffer, and let OpenGL know that we don't plan to
// change it (STATIC) and that it will be used for drawing (DRAW)
glBufferData(GL_ARRAY_BUFFER, sizeof(pos), pos, GL_STATIC_DRAW);
// Enable the attribute at that location
glEnableVertexAttribArray(vertexLoc );
// Tell OpenGL what the array contains:
// it is a set of 4 floats for each vertex
glVertexAttribPointer(vertexLoc , 4, GL_FLOAT, 0, 0, 0);
For the other arrays we would proceed in a similar manner, the only difference being that the normal
has three floats per vertex, and textureCoord
has only two.
For the index array, the approach is simpler, the main difference being in the type of the buffer:
GLuint buffer; glGenBuffers(1, &buffer); // bind buffer for positions and copy data into buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(index), index, GL_STATIC_DRAW);
In the rendering function, we will ask OpenGL to draw the above defined geometry. To achieve this we can use the following, for VAOs containing an index buffer:
glUseProgram(p); glBindVertexArray(vao); glDrawElements(GL_TRIANGLES, index.size(), GL_UNSIGNED_INT, NULL);
or, considering VAOs without indices:
glUseProgram(p); glBindVertexArray(vao); glDrawArrays(GL_TRIANGLES, 0, count);
Prev: Comm. App=>Shader | Next: Uniform Variables |
4 Responses to “GLSL Tutorial – Attribute Variables”
Leave a Reply Cancel reply
This site uses Akismet to reduce spam. Learn how your comment data is processed.
Hi,
Great tutorials. Thank you so much. I have a few questions. An easy one is: is “count” simply the size of the vertices in pos? A more complicated one would be: I notice that Vertex Array Object is created, but it’s never linked directly to the buffer object that you feed the vertex attributes in. How does the program know to bind buffer object that we want to our Vertex Array Object? (Also, do I understand correctly that VAO is a superset of VBOs — the buffer objects that you feed pos, texture coords, normals?) Thanks!
Hi,
1. count is the number of vertices to draw.
2. The VAO “constains” the VBOs. It works because the VAO is bound when we create and bind the VBO.
Hey. Notice you put sizeof(pos) in :
glBufferData(GL_ARRAY_BUFFER, sizeof(pos), pos, GL_STATIC_DRAW);
Be careful, pos is a pointer to the first element in the array it points to; sizeof a pointer will always return 4 (pointers are 32 bit unsigned integers). Turns out a sizeof a float will also return 4, so this call will give you correct result, but you should still change it to avoid confusion for us readers. Thanks ^^
Hi,
You’re right considering that pos would be a parameter of a function, in which case it would be considered a pointer. Otherwise pos is an array, and sizeof works as expected, providing the size in bytes of the array.