openglgeometryvertex-buffergeometry-instancing

OpenGL -- Render multiple meshes, with individual transformations, at once


I'm looking for a way to render many meshes at once, so that I don't have to issue a draw call for each mesh. I'm dealing with a 2D rendering here, and a typical object such as a square may have only two triangles in it. However, an object may also be quite complex and have thousands of triangles.

Now each object can move around by itself. Conceptually it's perfectly reasonable to have a VBO (or VBO/IBO pair) for each "object": As long as the object does not change, all I have to upload to the GPU each frame is the transformation information: a position vector and an orientation value. Or, equivalently, a transformation matrix.

But the problem with that approach is with a scene of 1000 square objects I'd have 1000 VBO's and 1000 IBO's to initialize, and 1000 draw calls setting 1000 sets of uniforms each frame in order to render 2000 triangles.

Okay. If all of those objects are identical, I can have one VBO/IBO to describe them, set up a Uniform Buffer Object (or perhaps a uniform array is more appropriate -- I still need to learn how to use these) with transformation data for each of them, and issue one instancing draw call, to have the vertex shader pull from the UBO the transformation data by using the instance number it receives. Great.

I just want to go one step further. I want to do what amounts to instancing on non-identical meshes: I have 1000 different objects, which I am happy to describe in either 1000 separate vertex/index buffer pairs, or one single gigantic pair of vertex/index buffers. I want to send their tranformation data to the GPU in one call. It's simply a matter of letting the driver/GPU bind or select the proper vertices.

Can this be done? Can it be done without using SM4 geometry shaders?

Update: I just thought of a potential method to accomplish this. I use a vertex attribute as my "instancing" value with which to index into a UBO that contains transformations. Is this the way to do it?


Solution

  • You don't need one VBO per object. Just concatenate all the objects into one single VBO or just a small set of VBOs. You can address into that VBO either adding a offset to the data parameter of the gl*Pointer functions, or use glDrawElementsBaseVertex to add the offset upon drawing time. Instead of just 4 indecies in the index array concatenate the index-arrays of all the small objects. If you're using some strip or fan primitive, you can set a special index with glPrimitiveRestartIndex that when encountered will start a new primitive.

    That way you need to split down your rendering calls only by used material settings and the overall transformations and shader parameters (i.e. textures, shaders, shader uniforms).