About OpenGL APIs glVertexAttribPointer, glBindBuffer, glBufferData, glBindAttribLocation

OpenGL : Terminology Demystified

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.

⚠ For those transitioning from immediate mode may find more reasons for confusion.

  • 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.

  1. 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).
  2. The client vertex array is defined in standard C/C++ language in the application CPU memory.
  3. Generate Vertex Buffer Objects (VBOs). Note that this only generates the “buffer names” which is just a GLunit.
  4. Bind the buffer with glBindBuffer and copy the vertex data from the client array to the VBO with glBufferData.
  5. Create a generic vertex attribute with glVertexAttribPointer to the bound VBO to define the data layout. Additionally it does something very subtle. The index of this generic vertex attribute should match the appropriate attribute variable location of the shader glGetAttribLocation, 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.

Advertisements