Vertex Array Object Question

I am learning OpenGL and the concept of VAO’s are confusing me. What I know about them is that VAO’s are there to allow you to use a single identifier to refer to multiple buffers that conceptually represent one “object.” So if you have 2 buffers, one for vertex positions and another for vertex colors, then if you wanted to use these 2 buffers again to load into a vertex shader, all you would have to do is bind this VAO using glBindVertexArray. Do I have this right?

I have had trouble getting some examples working, but this is what I understand. When you create a VAO and bind it to make it active, any subsequent buffers you bind and load data into are automatically associated with the VAO that you just bound. Is this right? So if you bind another VAO identifier, then all subsequent buffer generations and binds are associated with this new VAO identifier.
Pseudocode example:

  1. generate 2 vao’s
  2. bind vao[0]
  3. gen buffer b1 and bind to a target and load data to it
  4. bind vao[1]
  5. gen buffer b2 and bind to a target and load data to it
  6. bind vao[0] and call a draw command -> if b1 contained vertex data it would be drawn
  7. bind vao[1] and call a draw command -> if b2 contained vertex data it would be drawn

Appreciate your help

When you create a VAO and bind it to make it active, any subsequent buffers you bind and load data into are automatically associated with the VAO that you just bound. Is this right?

No. VAOs are not modified by calls to glBindBuffer() (buffer bindings are context state, not VAO state). They are modified by calls to glVertexAttribPointer (and glBindVertexBuffer, glVertexAttribFormat) and when that function is called they also record the buffer currently bound to GL_ARRAY_BUFFER. That way when you later bind a VAO again it has the same effect as if you would issue a bunch of glBindBuffer(), glVertexAttribPointer() calls to match the vertex attribute setup recorded in the VAO. For your example:


1. generate 2 VAOs vao0, vao1
2. generate 2 buffers buf0, buf1
3. bind buf0, fill with data
4. bind buf1, fill with data
5. bind vao0
6. bind buf0, glVertexAttribPointer
7. bind buf1
8. bind vao1
9. glVertexAttribPointer
10. bind vao0, draw command -> using contents of b0
11. bind vao1, draw command -> using contents of b1

Note that buf1 is bound while vao0 is still active without disturbing it and vao1 records that buf1 is the source of vertex data for the attribute used in the glVertexAttribPointer call (just like vao0 remembers this for buf0).

[QUOTE=carsten neumann;1252107]No. VAOs are not modified by calls to glBindBuffer() (buffer bindings are context state, not VAO state). They are modified by calls to glVertexAttribPointer (and glBindVertexBuffer, glVertexAttribFormat) and when that function is called they also record the buffer currently bound to GL_ARRAY_BUFFER. That way when you later bind a VAO again it has the same effect as if you would issue a bunch of glBindBuffer(), glVertexAttribPointer() calls to match the vertex attribute setup recorded in the VAO. For your example:


1. generate 2 VAOs vao0, vao1
2. generate 2 buffers buf0, buf1
3. bind buf0, fill with data
4. bind buf1, fill with data
5. bind vao0
6. bind buf0, glVertexAttribPointer
7. bind buf1
8. bind vao1
9. glVertexAttribPointer
10. bind vao0, draw command -> using contents of b0
11. bind vao1, draw command -> using contents of b1

Note that buf1 is bound while vao0 is still active without disturbing it and vao1 records that buf1 is the source of vertex data for the attribute used in the glVertexAttribPointer call (just like vao0 remembers this for buf0).[/QUOTE]

Thank you for the clear answer with a good example

I’m assuming you’re not using GL4.3. In GL4.2, a VAO mainly stores properties of multiple vertex arrays, i.e. memory layout definitions of data stored in an ARRAY_BUFFER object. It also stores a debug label and and any ELEMENT_ARRAY_BUFFER_BINDING done after binding the VAO. Initially the element array buffer binding is 0.

What I know about them is that VAO’s are there to allow you to use a single identifier to refer to multiple buffers that conceptually represent one “object.”

The VAO itself refers to no buffer object other than an element array buffer, if any. The VAO stores a list of vertex attribute indices and a corresponding ARRAY_BUFFER object name. If you call glVertexAttribPointer(index, …) and no ARRAY_BUFFER is bound at the time, this associtaion will be (index, 0) and thus drawing operations will be undefined. If an ARRAY_BUFFER is bound at the time, the association will be simply (index, GL_ARRAY_BUFFER_BINDING). Therefore, you can associate different vertex array with different ARRAY_BUFFER objects which will be sourced when you call a draw command like glDrawArrays.

GL4.3 relaxes this very static binding mechanism with a more flexible approach. Check out this extension for more info.

Note that no ARRAY_BUFFER needs to be bound when a VAO is bound for drawing (i.e. GL_ARRAY_BUFFER_BINDING will be 0) and a VAO will not change GL_ARRAY_BUFFER_BINDING - it will change, however, the value of GL_ELEMENT_ARRAY_BUFFER_BINDING.

Furthermore, you can delete associated buffer objects and look at the funny results when drawing (if the application does not crash). Unless you want to be welcomed to undefined-behavior-land, please don’t delete

When you create a VAO and bind it to make it active

True. Although “make it active” should probably be “make its state current” or something like that. :wink:

any subsequent buffers you bind and load data into are automatically associated with the VAO that you just bound. Is this right? So if you bind another VAO identifier, then all subsequent buffer generations and binds are associated with this new VAO identifier.

Nope. Especially buffer object name generation (glGenBuffers) has nothing to do with binding and absolutely nothing to do with VAOs. “Generating” a buffer merely means returning a currently unused, non-zero name for a buffer object.

As I said before, a VAO does merely store the current GL_ELEMENT_ARRAY_BUFFER_BINDING. You can change whatever buffer binding other than that and it will not affect the VAO. Only when you call glVertexAttribPointer is the current state of GL_ARRAY_BUFFER_BINDING important. Any other buffer is simply bound to it’s respective target. Refer to Section 6.2 / Table 6.6 of the GL4.2 core spec for the VAO state table.

  1. generate 2 vao’s
  2. bind vao[0]
  3. gen buffer b1 and bind to a target and load data to it
  4. bind vao[1]
  5. gen buffer b2 and bind to a target and load data to it
  6. bind vao[0] and call a draw command -> if b1 contained vertex data it would be drawn
  7. bind vao[1] and call a draw command -> if b2 contained vertex data it would be drawn

You’re forgetting stuff: you need to setup actual buffer layouts with glVertexAttribPointer and enable vertex arrays before anything will be drawn. Also, it makes no sense to draw if you’re only using a VAO associating vertex attrib arrays with vertex colors. It could look like this, re-structured to emphasize the disconnect between ARRAY_BUFFER objects and VAOs:

  1. generate 2 VBOs // lets say buffer names 1 and 2
  2. generate 1 VAO
  3. bind VAO // only one VAO, multiple buffers, multiple vertex array with
  4. bind VBO[0] and upload data // positions
  5. specify vertex attrib arrays sourcing from VBO0 (index 0, buffer name 1)
  6. bind VBO[1] and upload data // colors
  7. specify vertex attrib array sourcing from VBO1 (index 1, buffer name 2)
  8. enable array 0 and 1
  9. unbind both VBOs

This is a corresponding implementation (don’t get confused by the use of the gl namespace. it’s still the same function only more scoped):

void setupBuffers()
{
    GLfloat points[] = {-1.f, 0.f, -1.f, 1.f,
                        -1.f, 0.f,  1.f, 1.f,
                         1.f, 0.f,  1.f, 1.f,
                         1.f, 0.f,  1.f, 1.f,
                         1.f, 0.f, -1.f, 1.f,
                        -1.f, 0.f, -1.f, 1.f};


    GLfloat colors[] = { 1.f, 0.f,  0.f, 1.f,
                         0.f, 1.f,  0.f, 1.f,
                         0.f, 0.f,  1.f, 1.f,
                         0.f, 1.f,  1.f, 1.f,
                         1.f, 0.f,  1.f, 1.f,
                         1.f, 1.f,  0.f, 1.f};

    GLuint vbos[2];
    GLuint vao;

    gl::GenBuffers(2, vbos);
    gl::GenVertexArrays(1, &vao);

    gl::BindVertexArray(vao);

    gl::BindBuffer(gl::ARRAY_BUFFER, vbos[0]); 
    gl::BufferData(gl::ARRAY_BUFFER, sizeof(points), points, gl::STATIC_DRAW);
    gl::VertexAttribPointer(0, 4, gl::FLOAT, gl::FALSE_, 0, 0); // associate (index 0, buffer name vbos[0])
    gl::EnableVertexAttribArray(0);

    gl::BindBuffer(gl::ARRAY_BUFFER, vbos[1]);
    gl::BufferData(gl::ARRAY_BUFFER, sizeof(colors), colors, gl::STATIC_DRAW);
    gl::VertexAttribPointer(1, 4, gl::FLOAT, gl::FALSE_, 0, 0); // associate (index 1, buffer name vbos[1])
    gl::EnableVertexAttribArray(1);

    gl::BindBuffer(gl::ARRAY_BUFFER, 0); // no ARRAY_BUFFER_BINDING anymore ( == 0)
}

EDIT: Damn you carsten! :smiley: