VAOs and Element Buffer Objects

opengl

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);
}

Best Answer

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.

Related Topic