I’ve finally finished my lab course last week – thanks to my supervisor Matthäus G. Chajdas – you can read his blog here -, it wasn’t your usual lab course with work sheets and boring homework, instead I’ve been allowed to implement a nice paper about a Global Illumination approximation algorithm called (Cascaded) Light Propagation Volumes. It’s been developed by Crytek and you can find more information (including some presentations and videos) on their server. (Note: this is an implementation of the I3D paper, not the earlier SIGGRAPH one.)
The algorithm approximates global illumination by rendering the light into a reflective shadow map, injecting it into a volume (using a spherical harmonics representation) and propagates the light flux in this volume (hence the name of the algorithm) and taking into account occlusion as possible extension.
The whole algorithm is physically motivated but corners everywhere, of course, to be more efficient. The paper also contains a few errors and doesn’t explain everything needed to implement it in great detail (like eg the solid angles of the side faces), so I’ve written two documents detailing the mistakes I’ve found and the additional calculations I’ve performed.
You can find the mistakes here (including suggested corrections) and the full annotations document here.
Finally I’ve also uploaded the whole prototype (including my code licensed under the FreeBSD license and the media files) here – it’s 68 MB big (and it’s been compressed with 7zip with a compression mode that might not be supported by WinZIP. The Sponza model is from Crytek, too. You can download the original model and textures here.
The project uses DirectX 10.1 and by default it won’t run in DirectX 10, because it uses a texture format that is deprecated in D3D10 but supported again 10.1 (BGRA). See the comment by FatGarfield for the location that needs to be changed for it work in DX10, too. (However red and blue will be swapped then.)
I haven’t implemented cascaded LPVs and I also use only one light/RSM and only inject its depth into the occlusion volume, but the results already look very nice in my opinion.
Stay tuned for more ![]()
Cheers,
Andreas














of the clamped cosine lobe around the z axis, then we can determine the lighting in direction v for the cosine lobe around the normal n by transforming it into the space where the normal coincides with the z axis (ie rotate n onto the z axis):
is a rotation matrix that rotates n onto z.
and rewrite it in terms of
.
,
,
are the row vectors of the matrix,
and
, then:

and
:

, we can immediately see that its third column has to be n, because
by construction. Since rotations are orthogonal matrices, the inverse is the same as the transposed, so we can deduce that the third row of
we get:





there is an
, so that for all
some property … holds” stuff you find in textbooks. Some textbooks actually shorten it to “for every
“.
should mean “for almost all
holds”, which suggests that there are only finitely many elements for which it does not hold.
stands for “there exist infinitely many 

and
usually. Note however than
don’t interchange and neither do
.
might be easier to understand than
, and
might be better than
, then the following equation by Pascal holds: 
