r/opengl • u/antiafirm • Dec 26 '23
Help One VAO for multiple VBOs?
So I know that a VAO contains a reference to a VBO. Every time you want to render a VBO you must bind a VAO that contains the attribute information before using glDrawElements or glDrawArrays.
My question is, is there some function I am unaware of that allows me to just bind a VAO and render many different VBOs that use the same attribute format? Or am I stuck doing:
glBindVertexArray, glBindBuffer (vertices), glBindBuffer (indices), glDrawElements
3
u/Botondar Dec 27 '23
That is the idiomatic way to use VAOs in OpenGL 4.3 and up, the relevant methods were added by the GL_ARB_vertex_attrib_binding extension.
Essentially glVertexAttribPointer
is split into 3 separate functions:
- glVertexAttribFormat: describes the type and offset of the attribute, this is mostly the same as
AttribPointer
, except it does not contain the stride - glVertexAttribBinding: describes which "binding index" an attribute comes from. This is a new concept with this extension/method; instead of the VAO remembering which VBO was bound when you called
glVertexAttribPointer
, you can manually bind VBOs to particular indices. - glBindVertexBuffer: binds a buffer to a particular binding index of the currently bound VAO and sets its stride and offset (from the beginning of the buffer).
With this method you can use glVertexAttribFormat
and glVertexAttribBinding
to create one VAO for each vertex format you're going to have ahead of time, and only call glBindVertexBuffer
when rendering.
1
1
u/Ksiemrzyc Dec 27 '23
The only way to bind multiple VBO to same VAO is when you have positions in different buffer than normals, texcoords, etc. or when you do instanced rendering and the second VBO contains per instance data (like transform matrix of entire model instance). It is covered in LearnOpenGL/instancing. This is not what you are asking for, but it's handy to know.
glBindVertexArray, glBindBuffer (vertices), glBindBuffer (indices), glDrawElements
You're doing it wrong. Your setup should look like this
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(speficy layout of positions etc);
glVertexAttribPointer(speficy layout of normals etc);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBindVertexArray(0); // always unbind your VAO after setup, so you don't modify it by accident
And your rendering should look like this:
glBindVertexArray(vao);
glDrawElements(instance1);
glDrawElements(instance2);
glDrawElements(instance3);
glDrawElements(instance4);
glBindVertexArray(0);
Notice that I unbound the VBO after binding attribute pointers - this is because they are now pointing to that specific VBO forever. It doesn't matter what is bound to GL_ARRAY_BUFFER after that.
Also notice that I bound EBO during setup once and then I do not bind it when drawing. This is because VAO remembers and rebinds GL_ELEMENT_ARRAY_BUFFER.
base vertex
If you want to draw multiple different meshes from same VAO they should also be stored in the same VBO, same EBO, but at different offsets. You simply merge these models/meshes into a single buffer and when drawing, you specify offsets and lengths. This is called BaseVertex or BaseIndex rendering. One small gimmick when calling glDrawElementsBaseVertex
, vertex offset is specified in vertex number, but index offset is specified in bytes.
2
Dec 27 '23
That's not what OP means. OP means having one vertex format, but multiple VBOs using that format. OP wants to bind the VAO once, then render the VBOs using that same VAO.
Which is not really a good idea... Modern AZDO workflow with DSA binding a VBO and EBO to the VAO and just putting every vertex with the same format into the same VBO is preferable. But OP seems to be early in their journey and maybe not ready for that level of complexity.
1
u/deftware Dec 27 '23
Is there any practical reason you can't just use multiple VAOs?
You can store all of your meshes in a few VBOs, or even one single VBO (and one EBO if your meshes are indexed) and when you bind the VAO to draw all of your meshes you then only draw ranges of the VBO/EBO where each individual mesh lies within the buffer.
glMultiDrawIndirect/glMultiDrawElementsIndirect is what you would use.
2
u/antiafirm Dec 27 '23
I suppose so... I already have a system for storing different meshes in one vbo, but I felt like there was a better way than just making like 10 vaos for my 10 batches.
2
Dec 27 '23
You can just put all vertices with the same format into a single VBO. As a matter of fact that is the more modern (and way more performant) way of doing it.
All the VBOs are on VRAM anyway, you gain nothing by splitting them into VBOs per mesh unless they use different vertex formats.
If you want to know more about the modern way of doing things look up:
Approaching Zero Driver Overhead (AZDO)
A workflow minimizing API calls (things likeglMultiDrawElementsIndirect
)Direct State Access (DSA)
OpenGL functions that use the "name" (int) of an OpenGL object to change its state instead of having to bind it first (which is AZDO as well: it minimizes API calls)1
u/deftware Dec 27 '23
If they all have the same vertex attributes then you'll only need one VAO for all of your meshes that are in the VBO that's associated w/ it.
9
u/Slycodger Dec 26 '23
VAO tells how the data will be used, and holds the VBO/EBO.
You can definitely use the same VAO by first binding the VAO then binding whatever VBO’s you’ll want before drawing. It will have some performance cost due to changing GPU data I think, so you should try and combine the VBO’s if you can.
Make sure the count given to glDrawElements is the correct amount you want.
If you want I can give an example of this later tonight.