I am working on great arcsynthesis tutorials on creating a graphics engine and found that I don't understand VAOs as much as I thought.
From the textbook Chapter 5. Objects in depth
Association of buffer bindings and attributes
You may notice that glBindBuffer (GL_ARRAY_BUFFER) is not on this list, although it is part of setting the attribute for rendering. The binding to GL_ARRAY_BUFFER is not part of the VAO because the connection between the buffer object and the vertex attribute does not occur when you call glBindBuffer (GL_ARRAY_BUFFER). This association occurs when you call glVertexAttribPointer.
When you call glVertexAttribPointer, OpenGL takes any buffer at the time of the call associated with GL_ARRAY_BUFFER and associates it with the specified vertex attribute. Think of binding GL_ARRAY_BUFFER as a global pointer that glVertexAttribPointer reads. This way you can bind whatever you want, or do nothing at all with GL_ARRAY_BUFFER after calling glVertexAttribPointer; this will not affect the final rendering. In this way, VAOs store which buffer objects are associated with attributes; but they do not retain the GL_ARRAY_BUFFER binding.
First I skipped the last line "... but they donβt keep the GL_ARRAY_BUFFER binding." Before I noticed this line, I thought that the buffer currently bound was saved after calling glVertexAttribPointer. Without this knowledge, I built a mesh class and was able to get a scene with a certain amount of mesh rendering.
Part of this code is given below. Note that I do not call glBindBuffer in the draw function.
// MESH RENDERING /* ... */ /* SETUP FUNCTION */ /* ... */ // Setup vertex array object glGenVertexArrays(1, &_vertex_array_object_id); glBindVertexArray(_vertex_array_object_id); // Setup vertex buffers glGenBuffers(1, &_vertex_buffer_object_id); glBindBuffer(GL_ARRAY_BUFFER, _vertex_buffer_object_id); glBufferData(GL_ARRAY_BUFFER, _vertices.size() * sizeof(vertex), &_vertices[0], GL_STATIC_DRAW); // Setup vertex attributes glEnableVertexAttribArray(e_aid_position); glEnableVertexAttribArray(e_aid_normal); glEnableVertexAttribArray(e_aid_color); glEnableVertexAttribArray(e_aid_tex); glVertexAttribPointer(e_aid_position, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, pos)); glVertexAttribPointer(e_aid_normal, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, norm)); glVertexAttribPointer(e_aid_color, 4, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, col)); glVertexAttribPointer(e_aid_tex, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, tex)); /* ... */ /* DRAW FUNCTION */ /* ... */ glBindVertexArray(_vertex_array_object_id); glDrawArrays(GL_TRIANGLES, 0, _vertices.size());
Now that I am about to start lighting, I wanted to get debugging so that I can check if all my normals are correct. Currently, I just keep all the lines that will be displayed for the frame in the vector. Since this data is likely to change every frame, I use GL_DYNAMIC_DRAW and set the data right before I process it.
Initially, when I did this, I got garbage lines that would simply indicate infinity. Violation code below:
// DEBUG DRAW LINE RENDERING /* ... */ /* SETUP FUNCTION */ /* ... */ // Setup vertex array object glGenVertexArrays(1, &_vertex_array_object_id); glBindVertexArray(_vertex_array_object_id); // Setup vertex buffers glGenBuffers(1, &_vertex_buffer_object_id); glBindBuffer(GL_ARRAY_BUFFER, _vertex_buffer_object_id); // Note: no buffer data supplied here!!! // Setup vertex attributes glEnableVertexAttribArray(e_aid_position); glEnableVertexAttribArray(e_aid_color); glVertexAttribPointer(e_aid_position, 3, GL_FLOAT, GL_FALSE, sizeof(line_vertex), (GLvoid*)offsetof(line_vertex, pos)); glVertexAttribPointer(e_aid_color, 4, GL_FLOAT, GL_FALSE, sizeof(line_vertex), (GLvoid*)offsetof(line_vertex, col)); /* ... */ /* DRAW FUNCTION */ /* ... */ glBindVertexArray(_vertex_array_object_id); // Specifying buffer data here instead!!! glBufferData(GL_ARRAY_BUFFER, _line_vertices.size() * sizeof(line_vertex), &_line_vertices[0], GL_DYNAMIC_DRAW); glDrawArrays(GL_LINES, 0, _line_vertices.size());
After a little hunting, as well as searching for the details that I skipped above, I found that if I call glBindBuffer before glBufferData in the draw function, everything will be fine.
I am confused by why my mesh rendering worked primarily with this in mind. Do I need to call glBindBuffer again if I change the data in the buffer? Or undefined behavior if you don't bind the buffer and I just got out of luck and did it work?
Please note that I am targeting OpenGL version 3.0.