Confusion about drawing multiple shapes using VBOs and VAOs efficiently

I’m trying to draw several separate shape meshes (all triangle primitives) in the same image scene, for example imagine a terrain surface mesh and some shapes (triangulated sphere and cube) floating above it. Can I (should I) put each mesh into its own VBO and then bind them all to different vertex attribute array binding indices in a single VAO to draw with a single call to DrawArrays (or is it MultiDrawArrays I want)?
Can I have multiple VBOs with vertex positions bound to single VAO and will GL know how to find/use them correctly (and what order will they be accessed in)? Do I provide the total count of all the vertices in all the bound VBOs in the DrawArrays call?

1 Like

You can, but that doesn’t necessarily mean that you should. A single draw call doesn’t have to draw everything in a VBO (or group of VBOs).

If you want to draw multiple meshes with a single draw call, all of the data for a given attribute (e.g. position) should be in a single VBO. You can use different VBOs for different attributes; this may be advantageous if the data for some of the attributes changes frequently while the data for other attributes remains fixed.

Yes, but …

No. Each vertex would take data from every enabled attribute array. You could write a vertex shader which selected one of the attributes depending upon some factor, but that’s unlikely to be a sensible approach. Apart from anything else, it will be inefficient.

If all the meshes have the same fundamental structure (i.e. the same primitive type and the same set of vertex attributes), the same shader program and the same uniform state, then you can draw them all in a single draw call with e.g. glDrawElements or glMultiDrawElements (the latter is more efficient if you want to quickly enable/disables contiguous ranges). But this requires that all the meshes use the same group of VBOs (you can use different VBOs for different attributes, but not for the same attribute in different meshes).

Using separate VAOs makes sense when distinct sections of the scene inherently cannot be rendered in the same draw call, e.g. because they use different shader programs or have different sets of attributes.

Essentially, having large buffers and making individual operations use specific portions is more straightforward than trying to make a single operation aggregate data from multiple buffers.

Thank you for the explanations. Here are a bit more specifics that may help clarify what I’m trying to do and the best way to proceed with VBOs etc.
Suppose there are three distinct objects/meshes: terrain_surface, sphere, and cube.
They are all defined with tri-meshes, but have some other per-vertex attributes which vary for each,
for example
terrain_surface vertices also have a 1D texture coordinate,
the sphere vertices have normal vectors and 2D texture coords,
the cube vertices have cube-map texture coords (faceID + 2D tex coords).
Now suppose I wanted to handle them all from a single shader program (with specialized case handling for each object type),
could I pack all the vec3 triangle vertex position data for all objects into one VBO,
and the other vertex attributes into other VBOs in the same VAO?
Is it possible to change the stride and/or size of vertex attribute arrays part-way through a buffer?
Practically speaking, would it be better (more efficient, simpler, …) to have separate shader programs for each object mesh (and VAOs/VBOs) and then draw them with separate DrawArrays calls within a single render loop pass?

You can put everything into one buffer object if you want. Any call which directs the implementation to use data from a buffer allows the starting offset to be specified. If there are disadvantages to doing so, they relate to updating the data rather than using it.

Yes.

Yes. But don’t conflate VBOs with attribute arrays. Ultimately a buffer is just a block of memory (the implementation may actually make copies behind the scenes, but all of that is transparent; it’s just something that can store N bytes of data, any data). Attribute arrays treat a region of the buffer as an array of elements with a specific size and type.

It sounds like the cases are different enough that you should use a separate VAO and draw call for each of the three types. If you have multiple surfaces/spheres/cubes, you could use a single draw call for all instances of each type. But there’s unlikely to be an advantage to using a single draw call for all three types. The common advice to minimise the number of draw calls doesn’t have to be taken to the extreme; it’s mostly aimed at cases where you might have hundreds of “objects”, in which case you shouldn’t be using a naive OOP structure where each of those objects has its own VBOs, VAO and draw call.

How the data is split across buffer objects is a separate issue. If you’re using multiple draw calls anyhow, there isn’t likely to be any disadvantage to splitting the data across multiple buffers. There may or may not be advantages to doing so.

1 Like