It almost seems like the OpenGL consortium were given a limited set of words (array
, attribute
, buffer
, pointer
, vertex
, index
) to define modern OpenGL technology and API. It is no wonder that many spend so much time trying to figure of these nuanced (cryptic) API and descriptions. The concept itself is not that difficult but the API makes it more challenging than it should be.
Basically OpenGL requires the following to render.
- Vertex data like coordinates, connectivity, color, normal, texture etc.
- Memory on the GPU to store the vertex data
- Shader program to process the vertex data
Before looking at how these are specified, a few terms have to be understood as it can lead to confusion.
Attribute
The above mentioned vertex data are called vertex attributes. It includes coordinates and connectivity (shape) and color, normal, texture, etc. (properties).
Generic Vertex Attribute
In legacy OpenGL there were specific functions like glVertexPointer
, glColorPointer
etc. to specify vertex attributes and its data layout. These are replaced with generic vertex attributes with an identifier (called index
in function glVertexAttribPointer
) that associates to a shader variable (by its attribute location
in the program
for coordinates, color etc.) that process the attribute.
Binding
Setting something as current (or active) in OpenGL is often referred to as binding (e.g. glBindBuffer
). This implies that subsequent function calls will operate on the bound object. Sometimes it could also mean a relation or association (e.g. glBindAttribLocation
).
State Machine
OpenGL is a state machine. You put it into various states (or modes) that then remain in effect until you change them. All subsequent API calls are based on, or applied to this state. For example,
- in legacy OpenGL, the current color is a state which applies to all rendering primitives until changed.
- in modern OpenGL, the current buffer (VBO) is a state to which all buffer operations are applied.
Name and Index
Name and Index are used quite interchangeably. Name does not mean a string identifier. Index does not mean that it has to be in a sequence. All these are just a means to uniquely identifying certain objects with a GLunit
. Many calls to bind or associate are done using the name/index but is very subtle.
- Vertex is used quite generically but in the past sometimes meant vertex coordinates (e.g. `glVertexPointer`)
- Vertex Array and Vertex Array Object are not the same (not even related)
- APIs with the same signature get “reused” for different setups. E.g. `glVertexPointer` is used for both client arrays and Vertex Buffer Objects but the 4th parameter pointer has a different meaning depending on the context
Putting it all together
The initial steps are reasonably straight forward. Step (5) is key and the most subtle and confusing.
- Initialize the shader. The program has attribute variables which process the vertex data to produce the desired output. Each of the attribute variables are associated to a generic vertex attribute index with
glBindAttribLocation
. The actual data for these variables are specified in (5). - The client vertex array is defined in standard C/C++ language in the application CPU memory.
- Generate Vertex Buffer Objects (VBOs). Note that this only generates the “buffer names” which is just a
GLunit
. - Bind the buffer with
glBindBuffer
and copy the vertex data from the client array to the VBO withglBufferData
. - Create a generic vertex attribute with
glVertexAttribPointer
to the bound VBO to define the data layout. Additionally it does something very subtle. Theindex
of this generic vertex attribute should match the appropriate attribute variablelocation
of the shaderglGetAttribLocation
, as this sets up the relation for the program to access the attribute data.
Code snippet for the above steps are taken from OpenGL-VBO, Shader, VAO which discusses modern OpenGL in detail.
// Define a generic attribute id for coordinates GLuint VERTEX_ATTR_COORDS = 1; // Shader code "attribute vec3 aCoords" // Associate vertex attribute id to the attribute variable during shader init glBindAttribLocation(program, VERTEX_ATTR_COORDS_ID, "aCoords"); // Generate VBOs vboIds = new GLuint[4]; glGenBuffers(4, vboIds); // Copy data from client memory to VBO glBindBuffer(GL_ARRAY_BUFFER, vboIds[0]); // coordinates glBufferData(GL_ARRAY_BUFFER, sizeof(ave), ave, GL_STATIC_DRAW); // Get the id associated in the shader attribute variable // The association is set by glBindAttribLocation during shader init GLint coordsId = glGetAttribLocation(program, "aCoords"); // Create a vertex attribute that connects shader attribute variable to VBO glVertexAttribPointer(coordsId, nCoordsComponents, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(coordsId);
Conclusion
Whatever the reasons are for the poor and confusing terminology, we just have to deal with it. Hope this helps clarify some of those nuances. If you have any questions please post a comment.