PDA

View Full Version : Confirmation that I actually understand VAOs, VBOs, and EBOs



Brenn_
07-28-2015, 10:11 PM
After spending a bunch of time attempting to figure out why I kept getting errors when drawing with an element buffer, I want to just double check, before moving on, that I actually understand what these constructs are and how they are used, in an attempt to avoid future errors. So essentially, I'm posting what I think each thing does (which may or may not be correct), and I'd appreciate it if someone wouldn't mind checking to make sure I'm right.

VAOs: Store the data that dictates how VBO information is passed into a shader. One VAO can be used for drawing multiple VBOs, by simply binding the proper VAO prior to drawing. All that is required to initialize a VAO is knowledge of how the VBO and shader are laid out, but not the actual data in the VBO or shader. VAOs don't actually store references to any VBOs, and thus can be initialized at any point prior to drawing, regardless of when the VBOs are created.

VBOs: Simply a list of numbers. If two VBOs have the same format, the bound VAO can remain unchanged. The order of binding VBOs and VAOs is irrelevant, as long as they are both bound prior to drawing.

EBOs: Simply a list of numbers which correspond to certain vertices, used in order to avoid repeating lots of vertices. They must be bound after binding a VAO, lest runtime errors pop up when attempting to glDrawElements (Initializing & binding the VAO after the EBO, in my test program, seemed to be the cause of runtime errors, though I may have been misusing the EBO... If anyone can explain some of the more technical details of EBOs, that would be much appreciated).

Is my understanding correct, and if not, what am I misunderstanding?

Thanks for your time!

Dark Photon
07-29-2015, 05:39 AM
You're getting close, but none of your definitions is completely correct:

VAO: Stores bindings and enables for vertex attributes as well as the binding for the index list. Does not store any data (content) within the bound arrays/buffers, but "does" (different from what you said) store references to the arrays/buffers. So strictly speaking the arrays/buffers must be created first (so you have the addresses and/or buffer handles to put in the VAO).

VBO: Can contain a list of numbers, but even more generally, a buffer object is just a list of bytes. You tell the GPU how to pull data out of this buffer (offsets, data types, interleaving, etc.). The order of binding VBOs and VAOs "is" relevant, as binding VBOs changes the bound VAO.

EBOs: These are just buffer objects used differently. So see VBO: above for everything you need to know. The only reason there's a distinction is that you bind these buffer objects to a different bind point so the GPU pulls DrawElements index data from it rather than vertex attributes.

GClements
07-29-2015, 06:01 AM
VAOs don't actually store references to any VBOs, and thus can be initialized at any point prior to drawing, regardless of when the VBOs are created.
Any call to glVertexAttribPointer() or similar will store a reference to the buffer which is currently bound to the GL_ARRAY_BUFFER target in the VAO, along with the parameters to that call.

You can create the VAO before or after the VBOs, but both must be created before actually setting the attribute data.

A VAO is simply a container for related state (i.e. the data sources for each attribute, plus an element array). Once you've set up a VAO, you can change all of the related state with a single glBindVertexArray() call, rather than having to make multiple calls for each attribute.



VBOs: Simply a list of numbers.
"VBO" isn't a particularly useful term. It's better to think in terms of buffer objects (which are just blocks of storage, but with fairly abstract semantics in order to allow their implementation to be optimised) and attribute arrays (in the compatibility profile, attribute arrays can be in buffer objects or client memory). The first application of buffer objects was for attribute arrays (when they were first introduced, the only targets were GL_ARRAY_BUFFER and GL_ELEMENT_ARRAY_BUFFER), so buffer objects became perceived as something used specifically for storing vertex attribute data, hence "VBO".



If two VBOs have the same format, the bound VAO can remain unchanged. The order of binding VBOs and VAOs is irrelevant, as long as they are both bound prior to drawing.
This isn't quite correct.
When an attribute array is specified with glVertexAttribPointer(), the buffer currently bound to the GL_ARRAY_BUFFER target is stored in the currently-bound VAO. It doesn't matter which buffer is bound to GL_ARRAY_BUFFER at the time of a draw call. Attribute values are taken from the buffer associated with the attribute in the currently-bound VAO.


EBOs: Simply a list of numbers which correspond to certain vertices, used in order to avoid repeating lots of vertices. They must be bound after binding a VAO, lest runtime errors pop up when attempting to glDrawElements (Initializing & binding the VAO after the EBO, in my test program, seemed to be the cause of runtime errors, though I may have been misusing the EBO.
Unlike GL_ARRAY_BUFFER, a VAO store the current binding for GL_ELEMENT_ARRAY_BUFFER. Calling glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) stores a reference to the specified buffer in the currently-bound VAO. glDrawElements() etc take the vertex indices from the buffer stored in the currently-bound VAO. So switching between VAOs switches between element arrays.


Is my understanding correct, and if not, what am I misunderstanding?
It may help to take a look at tables 23.3, 23.4 and 23.5 in section23 of the specification (https://www.opengl.org/registry/doc/glspec45.core.pdf).

Brenn_
07-29-2015, 11:20 AM
First off, thanks for the helpful replies. I think I understand how VAOs and VBOs interact a bit more.

So if a VAO stores an internal reference to the previously bound buffer objects, that means that I need a VAO for each VBO, and then to draw something I need to bind the VBO as well as the connected VAO?

Alfonse Reinheart
07-29-2015, 12:15 PM
You do not need a VAO for every buffer object. You need a VAO for every separate object you want to render*. And sometimes, not even then. You can put the mesh data for multiple objects in the same buffer, and use different regions of the index buffer (along with glDrawElementsBaseVertex (https://www.opengl.org/wiki/Vertex_Rendering#Base_Index)).

You most assuredly do not need to bind the buffer objects after the VAO, unless your intent is to change the VAO. If your VAO is already setup with all of the buffers (array and index) that it needs, then you shouldn't need to bind buffers along with the VAO.

* Unless you separately specify your buffer objects from your vertex formats (https://www.opengl.org/wiki/Vertex_Specification#Separate_attribute_format). In which case you only need a VAO for every vertex format you use.

Brenn_
07-30-2015, 10:55 PM
You do not need a VAO for every buffer object. You need a VAO for every separate object you want to render*. And sometimes, not even then. You can put the mesh data for multiple objects in the same buffer, and use different regions of the index buffer (along with glDrawElementsBaseVertex (https://www.opengl.org/wiki/Vertex_Rendering#Base_Index)).

You most assuredly do not need to bind the buffer objects after the VAO, unless your intent is to change the VAO. If your VAO is already setup with all of the buffers (array and index) that it needs, then you shouldn't need to bind buffers along with the VAO.

* Unless you separately specify your buffer objects from your vertex formats (https://www.opengl.org/wiki/Vertex_Specification#Separate_attribute_format). In which case you only need a VAO for every vertex format you use.

Ok, I think I have it. (And reading about the separate attribute format was definitely interesting, as that is similar to what I was originally attempting to do.)

So setting up a VAO, VBO, and EBO to draw would go something like:

ObjectToBeRendered Constructor{
Generate VAO, bind VAO.
Generate VBO, bind VBO, fill VBO.
Generate EBO, bind EBO, fill EBO.
Set up vertex attributes.
}

And drawing them would be:

ObjectRenderer Draw function (taking an "ObjectToBeRendered"){
glUse the proper shader program which matches the vertex format in the VAO
Bind VAO from ObjectToBeRendered
glDrawElements, with the final argument referencing the (not necessarily bound) EBO, and the vertices themselves are accessed via the bound VAO.
}

If that is correct, then I've figured it out, and I thank all of you for your help!