13
\$\begingroup\$

On all the tutorials I can find about VAO's (Vertex Array Objects), they show on how to use them by configuring vertex attributes and binding a VBO (Vertex Buffer Object). But I want to create a VAO that will be configured for a set of VBO's in combination with a fixed shader, where each buffer uses the same data pattern (vertex, uv, color, etc). So, I want to create one VAO for multiple VBO's that will be drawn using one shader.

I couldn't find any demo on this, so I decided to just give it a try. But it doesn't work and crashes on the glDrawArray call. It looks like the VBO isn't bound. Here is the code I'm using:

Rendering:

/* Prepare vertex attributes */
glBindVertexArrayOES(vao);

/* Upload the buffer to the GPU */
glBindBuffer(GL_ARRAY_BUFFER, pool->next());
glBufferSubData(GL_ARRAY_BUFFER, 0, parts * buffer.stride() * 6, buffer.getBuffer());

/* Draw the triangles */
glDrawArrays(GL_TRIANGLES, 0, parts * 6);

glBindVertexArrayOES(0);

VAO Creation:

glBindVertexArrayOES(vao);

glEnableVertexAttribArray(ls.position);
glVertexAttribPointer(ls.position, 2, GL_FLOAT, GL_FALSE, buffer.stride(), 0);

glEnableVertexAttribArray(ls.color);
glVertexAttribPointer(ls.color, 3, GL_FLOAT, GL_FALSE, buffer.stride(), GL_BUFFER_OFFSET(buffer.dataTypeSize * 2));

glBindVertexArrayOES(0);

Where ls is a simple struct that holds the attribute locations.

In the Rendering part, swapping the glBindBuffer and the glBindVertexArrayOES around didn't work either.

So, the question is: Is it even possible to do so, or will I have to create for each buffer a VAO? And if I have to create a VAO for each VBO, is it possible to update the VBO's data using glBufferSubData in combination with a VAO?

\$\endgroup\$

2 Answers 2

18
\$\begingroup\$

VAOs do not contain "glBindBuffer" state, with the exception of GL_ELEMENT_ARRAY_BUFFER's binding state. What you're not understanding is that glBindBuffer(GL_ARRAY_BUFFER) doesn't do anything. Well, it doesn't do anything as far as rendering is concerned. Try it; right before calling glDraw*, call glBindBuffer(GL_ARRAY_BUFFER, 0); your rendering will work just fine.

The way glVertexAttribPointer works is that it looks at what is bound to GL_ARRAY_BUFFER at the time glVertexAttribPointer is called. Not at render time. Not later. Right at that exact moment. It takes whatever buffer object was there and stores it in another piece of OpenGL state, which is encapsulated within the VAO.

In general, you have two options, one of them that's new and probably shouldn't be used at this point in time.

Your first option is to put all objects that use the same vertex format (ie: everything except the buffer object and offset) in the same buffer object. Basically, build a giant array that contains all the vertices for all object that use the same format.

When it comes time to render, you can render a portion of the vertices. glDrawArrays takes a range of elements to render, and you can adjust your indices in glDrawElements to do the same. Alternatively, you can use glDrawElementsBaseVertex to bias the vertices, so that an offset is added to each index. This offset is the number of vertices before that vertex in the big array.

The other alternative is to use the relatively new format attribute separation system added in GL 4.3. It would allow you to change buffers without resetting the format. Thus you would bind a single VAO, then just substitute in different buffers as needed with glBindVertexBuffer. This is also available in ES 3.1.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ The NV GL4.3 drivers are now full release. \$\endgroup\$ Jan 2, 2013 at 11:38
  • \$\begingroup\$ Thank you very much for explaining the way glVertexAttribPointer works. That was enlightening and explains why and how it works. \$\endgroup\$ Jan 2, 2013 at 15:14
5
\$\begingroup\$

The VAO stores the glVertexAttribPointer state. Changing the VAO does not affect the current glBindBuffer, nor does changing the glBindBuffer affect the VAO. Only the glVertexAttribPointer call affects the VAO, by recording the buffer in use at the call.

So the answer to your question is no.

One option if you want to reduce the number of objects is to put all your mesh data into one large VBO and specify where the mesh data lives in the VBO in the glDrawArrays call using the 'first' and 'count' arguments.

\$\endgroup\$
3
  • \$\begingroup\$ All the articles on the internet on VAO's show that binding a VAO automatically binds the VBO. So, I think a VAO is a configuration that holds both glVertexAttribPointer states and the glBindBuffer state. And yes, binding a VBO when a VAO is bound changes the VAO. I'm telling this because I tested this and it effectively does. From where does this information come from? \$\endgroup\$ Jan 1, 2013 at 23:56
  • 3
    \$\begingroup\$ @MartijnCourteaux: "All the articles on the internet on VAO's show that binding a VAO automatically binds the VBO." No, they don't. The OpenGL Wiki on vertex specification does not. Indeed, show me a single article on the internet that says that any glBindBuffer state except GL_ELEMENT_ARRAY_BUFFER is changed by binding a VAO. \$\endgroup\$ Jan 2, 2013 at 4:27
  • \$\begingroup\$ The other answer explained good how the glVertexAttribPointer method works. It uses the VBO bound at that time. You are right! Thanks, I understand right now. \$\endgroup\$ Jan 2, 2013 at 15:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .