Chapter 8. Shadow Volumes

You can easily render shadow volume for any TCastleScene by TCastleScene.RenderShadowVolume method. Some features (see any article about shadow volumes to know what they mean) :

  • Silhouette edge detection is done, of course the model must be 2-manifold for this to work.

    ManifoldEdges structure is prepared once during pre-processing step (by PrepareRender call with prShadowVolume, or simply on first call to RenderShadowVolume). This allows rendering shadow quads with silhouette detection in O(n+m) time, where n is a number of edges and m is a number of triangles (these are roughly equal since on a perfect 2-manifold 3 * m = 2 * n). Without calculated ManifoldEdges, this would have to take square time, O(m2).

    To account also models that are not completely 2-manifold, we have BorderEdges list with edges that have only one neighbor triangle. Actually, it lists edges with any odd number of neighbors (each neighbor pair makes one edge in ManifoldEdges, and then one left neighbor makes one BorderEdges item). All BorderEdges are always considered part of the silhouette. This is not a perfect solution, further in this chapter I present when this fails. When it fails, there are two solutions:

    1. fix the model to be 2-manifold.

    2. or use the much slower algorithm version that doesn't do silhouette edge detection.

  • Both Z-pass and Z-fail approaches are done. We automatically detect when Z-fail is needed, and in 99% of the cases we can use faster Z-pass approach.

  • Both positional and directional lights are supported.

  • Using homogeneous coordinates tricks: we render shadow quads vertexes in real infinity, and we can use perspective projection that has no far clipping plane.

  • We do shadow volume culling for scenes (that is, we try to avoid rendering shadow quads when it's obvious the scene shadow can't be visible within current camera frustum). Implemented in TGLShadowVolumeRenderer.InitScene. It's not fully implemented, we could take more conservative convex hull between light position and frustum. But it seems that this wouldn't improve culling significantly, current approach gives us almost as much as we can get from frustum culling.

    More drastic improvements can only come from the use of portals.

8.1. Quick overview how to use shadow volumes in our engine

Actually, our TCastleSceneManager does pretty much everything for you. Just set ShadowVolumesPossible and ShadowVolumes to true. That's it — we will take care to render with shadow volumes.

  • You can change ShadowVolumes dynamically during the game (for example, if user changes video preferences).

  • ShadowVolumesPossible should remain constant and reflect whether we have stencil buffer available. Dynamically changing ShadowVolumesPossible is actually allowed, but it may cause costly recalculation once the models are actually loaded. Also, projection may need to be reapplied (only when ShadowVolumesPossible, we force infinite far plane, which is needed for z-fail, when camera near plane is inside the shadow volume).

You should also take care to initialize OpenGL context requiring stencil buffer (8-bit should be enough for practical uses). This is something that has to be requested outside of scene manager. The simplest way to do this is to use TCastleWindow.OpenOptionalMultiSamplingAndStencil method instead of TCastleWindow.Open, see examples/vrml/simplest_vrml_browser_with_shadow_volumes.lpr.

To actually define what lights are used for shadow volumes, set shadowVolumes and shadowVolumesMain to true on some VRML/X3D light node. See for details. Alternatively, you can control the main light source by overriding TCastleSceneManager.MainLightForShadows.

If you define your own T3D descendant, be sure to override T3D.RenderShadowVolume method. See API reference for details now to handle it.

You can change ReceiveShadowVolumes and CastShadowVolumes properties of every T3D descendant.

The whole approach is quite flexible and is used throughout my whole engine, and it will use all implemented shadow volume optimizations under the hood. For example, see "The Castle" game, where almost everything may have a shadow rendered by shadow volumes — creatures, level scene, level objects. And everything goes through this same approach, getting all optimizations.