Table of Contents
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 withprShadowVolume
, or simply on first call toRenderShadowVolume
). 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:
fix the model to be 2-manifold.
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.
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 changingShadowVolumesPossible
is actually allowed, but it may cause costly recalculation once the models are actually loaded. Also, projection may need to be reapplied (only whenShadowVolumesPossible
, 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 https://castle-engine.io/x3d_extensions.php#section_ext_shadows
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.