Batch Rendering of Instanced Objects

I have a conceptual question regarding batch rendering. Say, for example, I want to render static objects such as a tree and a rock to my scene and I have many rocks and trees. I create a batch (a VAO with a large VBO) filled with the vertex data of both models. Is there a feasible way to render both with multiple instances or must every tree and rock be added to the batch? The latter would seem to negate the benefits of batch rendering since having some large number of trees and rocks would require submitting duplicate vertex data to the batch and make for poor memory management. Moreover, rendering via two separate instanced drawcalls seems to make using a batch meaningless. I believe there is a gap in my understanding of batch rendering techniques. Could someone comment on their approach to this?

glMultiDrawElementsIndirect() performs the equivalent of multiple glDrawElementsInstancedBaseVertexBaseInstance() calls, but it requires at least OpenGL 4.3.

Without that, you can use one draw call per instanced group, and another draw call for all non-instanced geometry. With instancing, the instances don’t need to be identical, but they do need to have the same topology.

Ultimately, it isn’t necessary to optimise rendering down to a single draw call per frame. Even highly-optimised games may use a few hundred draw calls per frame. Where it really becomes a problem is using one draw call per “object” (e.g. for the sake of a textbook “object-oriented” design which fails to consider what’s good for the GPU) when you might have thousands of objects in the scene.

You’ve got quite a few options:

  1. Separate batch per rock
  2. Pseudo-instancing: add the vertex data for multiple rocks to a single “rock batch”.
  3. GPU instancing:
    3a) via Instanced Arrays
    3b) via Instanced Draw Calls

You know about #1 and are smartly trying to avoid it.

#2 works, but you end up redundantly specifying all of the vertex attributes except position for each instance. The positions are of course different because the instances lie at different locations in the world. If your instances are simple (e.g. few verts) this isn’t a bad option.

#3 gives you ways to specify the vertex attributes for an instance “once”, and then at shading time apply other data to each instance to add per-instance variation (e.g. different position, color, material attributes, etc.).

#3a allows you to provide this “variation data” in other vertex attributes.
#3b allows you to fetch this “variation data” from GPU memory (and/or compute it on-the-fly) from an “instance ID” which is passed into the vertex shader for each instance.