Point Light Shadow Mapping – Part 3

Following the two previous articles following how Shadow Mapping would be implemented in Kourjet (See part 1 and part 2), I would like to finish on a third technique that has been suggested on gamedev (you can follow the discussion on this gamedev.net forum thread). So here I am with the third algorithm which involves this time instanciation. To sum up the previous article, I had already implemented two algorithms for cube shadow map generation:

  • The first one simply renders the cube map face per face
  • The second one renders the whole object directly to the 6 faces using a geometry shader that output 6 times the triangles (once to each face of the cube)

I have today implemented the third algorithm that makes use of geometry instancing.

Geometry Instancing?

Geometry instancing is a technique that allows to submit a set of vertices (we will call it the mesh) to the DirectX API only once and have it drawn several times by describing only what changes between each instance of the mesh. A common use of this technique is to draw small geometry such as particles or grass a very big amount of times. You can check out this Intel tutorial if you want more info about instancing.

So the idea is simply, given our base mesh, to append an instance buffer that will tell which face of the cube to draw to. The DirectX API will automatically submit the vertices 6 times to the vertex shader.

In practice

Globally, the algorithm is very similar to the single pass algorithm. This is how it is done (the change is only in the last 4 lines):

L = current light
BaseObjectSet = Objects within light range
Set cube shadow map as 6 render targets at once
Foreach O in BaseObjectSet
    For each face of the cube map
        Test O with the current face frustum
        If O is visible, set the geometry shader flag for this face
    End
    Append our custom instance buffer to O
    Change the vertex layout corresponding to O
    Render 6 instances of O
    Remove our custom instance buffer from O
End

The instance buffer is simply a buffer containing the face index: {0, 1, 2, 3, 4, 5}. All this is pretty simple in theory, but that implies a few complications in practice:

  • We need to append the instance buffer and remove it after we are finished updating the shadow map (that’s the easy one)
  • We need to change dynamically the vertex layout corresponding to the object. Indeed, as we don’t know in advance the vertex layout (this should work with any vertex layout), we need to create on the fly a clone of the original vertex layout and append it the element that describes how to find our cube face index. To add a bit to the complication, a geometry in Kourjet can be divided into groups… A cache belonging to the render path is filled with these newly created layouts and hopefully the overhead is not too big.

Conclusion

Performance wise, I have test the 3 algorithms on my small test scene and the numbers are very close (all around 1 millisecond). I think it would need some more testing on bigger scene and more vertices. Multi-pass rendering seems however a bit slower than the two single-pass algorithms though (as expected).

Light 1 Light 2 Light 3 Average
Multiple Pass Point Light Shadow Map 0.5926 ms 1.1520 ms 1.0890 ms 0.9445 ms
Instancing Point Light Shadow Map 0.5100 ms 1.0980 ms 1.1330 ms 0.9137 ms
Single Pass Point Light Shadow Map 0.5588 ms 1.0620 ms 1.1170 ms 0.9126 ms

You can find the source code on the SVN repository and in particular the effect file and the render path for instancing.

  1. No comments yet.

  1. May 6th, 2013