Almost anyone out there wants to output as many meshes/triangles/details as possible. We know that individual draw calls can hurt performance, so it would be nice if there was a way to draw lots of meshes using a single draw call. Well, that is possible, and it’s named geometry instancing. A good starting point can be found here.
Basically, we chose our mesh to be rendered (hundred, thousands of copies), and we fill an additional array to hold the per-instance data. This data usually is the world transform of each individual instance of that mesh, and some other information like color, texture offset (to access a different part of a texture atlas, i.e.) etc. In the rendering step, we set this additional array as a secondary vertex buffer and call a single draw call using the number of copies as an argument. It’s REALLY easy (more than I thought it would be). Take a look at this code snippet:// Create a vertex declaration according to the per-instance data layout. // In my case, I’m only using a world transform, so 4 vector4 (or a float4x4) is enough VertexDeclaration _instanceVertexDeclaration = new VertexDeclaration ( newVertexElement(0, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 0), newVertexElement(16, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 1), newVertexElement(32, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 2), newVertexElement(48, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 3) ); … // Set our per-instance data to the additional vertex buffer. In this example, it’s just an array of matrixes _instanceVertexBuffer.SetData(_instanceTransforms, 0, _subMeshes.Count, SetDataOptions.Discard); … // Bind both mesh vertex buffer and our per-instance data graphicsDevice.SetVertexBuffers( newVertexBufferBinding(meshPart.VertexBuffer, meshPart.VertexOffset, 0), newVertexBufferBinding(_instanceVertexBuffer, 0, 1) ); … // Use a different Draw* method graphicsDevice.DrawInstancedPrimitives( PrimitiveType.TriangleList, 0, 0, meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount, _subMeshes.Count);
In my renderer, all you need to do is call “mesh.SetInstancingEnabled(true)”, and the code will take care of grouping the visible meshes (I named this instancing groups) according to their sub-meshes. The instancing technique is used in all the 3 stages: shadow generation, render to GBuffer and reconstruct lighting stage. The main shader was changed because when we use instancing, we get the world transform (and any other additional per-instance data) from an input and not from the default shader parameter.
Instancing offers a huge improvement in speed, and you can enable/disable the instancing in the code to check the difference. By the way, the code is here. Use it at your own risk!
That is it, see you next time!