7
votes

I've been reading a bit about how VAOs work with VBOs. As far as I understand, the VAO only stores state information about a VBO when we call glVertexAttribPointer. My question is, when does it store state about an EBO (for indexed drawing), does it save state for both when glVertexAttribPointer is called?

//Vertices of the rectangle
std::vector<GLfloat> vertices
{
    0.5f,  0.5f, 0.0f,  // Top Right
    0.5f, -0.5f, 0.0f,  // Bottom Right
    -0.5f,  0.5f, 0.0f,  // Top Left 
    -0.5f, -0.5f, 0.0f,  // Bottom Left
};

//Indices of the triangle
std::vector<GLuint> indices
{
    0, 1, 3,
    0, 3, 2
};


GLuint VAO, VBO, EBO; 
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);

glBindVertexArray(VAO); //Bind the VAO

//Bind the buffers
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);

glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GLfloat), &vertices[0], GL_STATIC_DRAW);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);

glBindVertexArray(0); //Unbind the VAO

//Supply Index Buffer information
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices[0], GL_STATIC_DRAW);

glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

//Custom shader class that stores the program to be used at render time
Shader shader("..path_to_shader\\shaders\\vertexshader.vert", "..path_to_shader\\shaders\\fragmentshader.frag");

while (!glfwWindowShouldClose(window))
{
    glUseProgram(shader.Program());
    glBindVertexArray(VAO); //Bind the VAO to draw.
    glClearBufferfv(GL_COLOR, 0, &color[0]);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);

    glfwPollEvents();
    glfwSwapBuffers(window);

    glBindVertexArray(0);
    glUseProgram(0);
}
2
You don't have the element array buffer bound when you call glBufferData() for the indices. Since the binding is part of the VAO state, it is not bound anymore after you unbind the VAO. - Reto Koradi
So unlike a VBO, to modify an element array buffer, the VAO must be bound? - Jeff
Not the VAO, but the buffer you want to modify. Since your bindings are saved in you VAO, in your case binding the VAO or binding your GL_ELEMENT_ARRAY_BUFFER is the same for glBufferData. However sometimes it's not the case. The point is when you call glBufferData(GL_ELEMENT_ARRAY_BUFFER, ....) then the GL_ELEMENT_ARRAY_BUFFER you want to modify should be bound. The same is true for other targets like GL_ARRAY_BUFFER. - plasmacel
So, to sum up, when unbinding the VAO, the element array buffer is also unbound. But not the VBO. Is this correct? - Jeff
Actually when you unbind the VAO, you also unbind all it's states. The only thing because your example works as you stated is because you already did a glBindBuffer(GL_ARRAY_BUFFER, ...) which modifies the global state, not the VAO's state. - plasmacel

2 Answers

15
votes

The GL_ELEMENT_ARRAY_BUFFER binding becomes part of the VAO state when you bind it with:

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ...);

while a VAO is bound.

Note that it's also legal to bind the element array buffer without a VAO bound. This is only really useful to fill it with data, using glBufferData() or similar calls. Since in the Core Profile you need a VAO bound for any draw calls, you need to bind the element array buffer you want to use for rendering while the VAO is bound.

Regarding your code, the sequence of calls you are using will not give the desired result. Here are the critical calls you have, with comments explaining the result:

glBindVertexArray(VAO);
// VAO is now bound. If this is the first time, it will have the default VAO state.

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
// The element array buffer is bound, and this binding becomes part of the VAO state.

glBindVertexArray(0);
// The VAO is unbound. Since the element array buffer binding was part of the
// VAO state, the element array buffer is also unbound.

glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint),
             &indices[0], GL_STATIC_DRAW);
// This will not work, since you do not have an element array buffer bound.

To get this working, the easiest solution is that you change the order of the last two calls:

glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint),
             &indices[0], GL_STATIC_DRAW);
// The currently bound element array buffer is filled with data.

glBindVertexArray(0);
// The VAO is unbound. Since the element array buffer binding was part of the
// VAO state, the element array buffer is also unbound.

Just to explore this some more, the following sequence would also work:

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
// The element array buffer is bound, Since no VAO is bound, the binding becomes
// part of the global state (or more precisely, of the default VAO 0).
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint),
             &indices[0], GL_STATIC_DRAW);
// The currently bound element array buffer is filled with data.

glBindVertexArray(VAO);
// VAO is now bound. If this is the first time, it will have the default VAO state.
// Since the element array binding is part of the VAO state, the previously bound
// element array buffer is not bound anymore.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
// The element array buffer is bound, and this binding becomes part of the VAO state.
glBindVertexArray(0);
// The VAO is unbound. Since the element array buffer binding was part of the
// VAO state, the element array buffer binding is restored to the binding that
// was current before the VAO was bound.

Note that in this sequence, you have to bind the element array buffer again after binding the VAO, because the element array buffer binding in the VAO state will override the current binding as soon as you bind the VAO.

12
votes

From opengl.org:

A Vertex Array Object (VAO) is an OpenGL Object that stores all of the state needed to supply vertex data (with one minor exception noted below). It stores the format of the vertex data as well as the Buffer Objects (see below) providing the vertex data arrays.

below, at Index Buffers

The index buffer binding is stored within the VAO.

For GL_ARRAY_BUFFER the VAO will save the binding when you call glVertexAttribPointer. It is basically because the GL_ARRAY_BUFFER binding is not part of the VAO's state. So calling glBindBuffer(GL_ARRAY_BUFFER, vertexBufferHandle) won't do anything with the VAO's state.

For GL_ELEMENT_ARRAY_BUFFER it's not the case: the VAO (if bound) will save your index buffer binding to it's state when you call glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferHandle).

There are great tutorials about OpenGL indexed buffering and another basic stuff on opengl-tutorial.org.