
Originally Posted by
JeffEvarts
...I think there are some "traditional" and/or "well known" things about VAOs and VBOs that aren't quite making it into the tutorials I can find.
Sounds like you're doing great and not far off of understanding this.
I do understand where you're coming from. Sometimes the naming of a concept gets in the way of understanding quickly 1) what it really is and 2) why it exists.
Let's start basic. I'll simplify things just a little to avoid needless rabbit holes. If we focus just on vertex and index data need to issue draw calls...
In terms of a references diagram:
Draw call -> VAO -> Buffer Objects
- Draw call = Launches a draw command using the bindings stored in the bound VAO.
- VAO = Captures the vertex and index buffer object bindings and other vertex/index binding state needed to issue a draw call
- Buffer objects = Holds the vertices and vertex index data used to draw your shapes.
In this case where Buffer Objects contain just vertex and/or index data for draw calls, people call these Buffer Objects "Vertex Buffer Objects", or VBOs. But this is just English naming; to GL, they're just Buffer Objects -- memory blocks of arbitrary data that you fill up yourself.
By contrast, the purpose of VAOs (Vertex Array Objects) is to function as a shorthand way for you to communicate draw command setup state to OpenGL. It's just a container (a struct) of other state. At the GL API level, you can either say: 1) "Activate VAO 5", OR 2) (equivalently) you can say "Activate the states A, B, C, D, E, F, G, H, I, J, ..." which is specified by VAO 5). #2 is just a lot more verbose and as you might expect takes more CPU time (which is the reason VAOs exist in the first place).
While you're getting started, if you want to just skip VAOs for now and come back to them later, you can. After your app starts up, just create one, bind it, forget they exist, and then go on about your program. You can come back to them later once you've got the basics down and have successfully drawn some things.
In particular, the "bound" nature of a lot of the OpenGL stuff seems programmatically foreign to me. I suspect that it's a result of dealing with a single shared hardware resource in a multithreaded environment, but it has definitely confused me at some points.
"Binding" an object just says: "OpenGL, go get this object! You're about to need it." Once the object is bound, then you issue GL calls which operate on it.
For instance, binding is like the "getObject()" lines in the code snippet below:
Code cpp:
obj = getObject(1);
doStuff( obj );
obj = getObject(2);
doStuff( obj );
...
A lot of the tutorials represent VBOs as simple arrays of floats or ints...
Yep. Or in general, just a list of bytes. Think of it as the GPU analog to a CPU malloc()ed block of memory you can fill however you want. You can populate it with ints, floats, structures, random bytes you generate, whatever. In the case of VBOs, you fill it with the data defining vertices (and indices if you're using glDrawElements).
...but due to the fact that glBindBuffer takes a "purpose" flag (e.g. GL_ELEMENT_ARRAY_BUFFER)
Right. Think of this like OpenGL having a predefined set of N object pointers or references. For instance:
Code cpp:
BufferObject *obj[ 10 ];
doStuff( obj[0] )
doStuff( obj[1] )
except that the elements of this array have names (e.g. GL_ELEMENT_ARRAY_BUFFER). The names are how you refer to them.
The "target" parameter (e.g. to glBindBuffer() just tells OpenGL which one of these predefined object references that you're "binding" an object to.
...I think the VBOs also carry additional information along with them.
No, nothing so strange as that. VBOs are just buffer objects, which are each just blocks of GPU memory you can fill however you want. Just think of them as malloc()ed blocks of memory on the GPU side and you're really not far off.
You can bind any buffer object to any buffer object bind target (glBindBuffer()). Which one(s) you bind to when is completely determined by what you're about to direct OpenGL to do with that buffer object next.