-
Notifications
You must be signed in to change notification settings - Fork 77
Instancing Tutorial
This article will give a short explanation about instancing in Ardor3D. Instancing is not available for the fixed function pipeline. This means knowledge of GLSL is required.
Instancing is useful were you want to render one mesh multiple (a lot!) of times. Currently, instancing will only generate a performance boost on small meshes (<150 triangles on Nvdia/Intel, AMD will also speed up larger meshes).
- First of all, we need to enable instancing in ardor3d (it is disabled by default):
System.setProperty("ardor3d.enableInstancedGeometrySupport", "true" );
If you are currently rendering the same mesh a lot of times, you are probably using:
for(int i = 0; i < 100; i++)
{
Node newMesh = baseMesh.getCopy(true);
root.attachChild(newMesh);
}
This will share memory between meshes but still leads to separate draw calls for each mesh. Instead, we now do:
for(int i = 0; i < 100; i++)
{
Node newMesh = baseMesh.makeInstanced());
root.attachChild(newMesh);
}
Now all visible instances of the same type will be drawn in one(or at least only a few) draw calls. But before we can see any result, we need to modify our shader. Previously, the new vertex position in the vertexshader was calculated along the lines of this:
...
void main(void)
{
gl_Position = ModelViewProjMatrix * gl_Vertex;
...
With instancing, we no longer apply the mesh/vertex transform in code, we apply them in the shader. The tranforms are passed to the shader by uniform values:
#extension GL_ARB_draw_instanced: enable
...
uniform int nrOfInstances = -1;
uniform vec4[30] transforms; //See maxBatchSize below, increase this number together with the batch size (or use a define)
void main(void)
{
if(nrOfInstances > 0)
{
// load our current transfrom matrix from uniform array
mat4 transform = mat4(transforms[gl_InstanceID * 4],
transforms[gl_InstanceID * 4 + 1],
transforms[gl_InstanceID * 4 + 2],
transforms[gl_InstanceID * 4 + 3]);
gl_Position = ModelViewProjMatrix * transform * gl_Vertex;
}
else
{
gl_Position = ModelViewProjMatrix * gl_Vertex;
}
...
The gl_InstanceID is provided by Opengl and gives us an index for the current instance. With this, we can load the correct transform from our uniform array and apply it to our vertex position. The if(nrOfInstances>0)
check is done to make sure our shader will also work when instancing is disabled or we fall back to normal rendering.
So how does ardor3d knows what meshes to draw as one instanced batch? It's simple, they all share the same InstancingManager (its in the MeshData). So if you do not want to use the makeInstanced(), you can also make sure your meshes are rendered with instancing by setting the same InstancingManager object on all of them.
The instancing manager has two settings that are worth a look:
-
MaxBatchSize: The max number of meshes to be drawn in one draw call (default 30). The larger this is, the more meshes per draw call, the faster. Since we use uniform arrays to send the transforms to the shader, settings this value to a number too large will overflow the uniform space in the shader on the graphics card.
-
MinBatchSize: Instancing creates a slight overhead. When the number of visible instances is smaller than MinBatchSize (default 2), the InstancingManager decides to use normal rendering for the objects (it will set the nrOfInstances to -1, hence the fallback code in the if statement in the shader).