Shadow Volumes

Contents:

1. Intro

Fountain level model, with shadow volumes.
The same fountain level model, with shadow volumes. After some interactive fun with moving/rotating stuff around :)
Werewolves with shadows
Castle "fountain" level with shadows

Shadow volumes are another method of rendering dynamic shadows in our engine.

Demo 3D models that use dynamic shadow volumes are inside our demo models, see subdirectory shadow_volumes/. Open them with view3dscene and play around!

2. Features

Featues of shadows by shadow volumes (especially when compared with shadow maps):

  • Shadow volumes produce hard shadows. That's both an advantage (they are as sharp as your geometry, no problems with texture resolution like in shadow maps) and disadvantage (when simulating large area lights, hard shadows may look unrealistic).

  • Shadow volumes are much easier to activate... To see the shadows, just choose one light in the scene (probably the brightest one) and set it's fields shadowVolumes and shadowVolumesMain both to TRUE. That's it. Compared to shadow maps, you don't need to tweak anything to deal with shadow maps resolution, bias etc.

  • ... but shadow volumes require your models to be 2-manifold. So you need to be more careful when modeling. Shadow volumes usually don't work if you try to activate them on a random 3D scene. More about this below.

  • It's difficult to compare the speed of "shadow volumes" vs "shadow maps". Both techniques are expensive in totally different ways. On one hand, shadow volumes are more expensive as they require extra rendering passes (additional rendering of "shadow quads" to the stencil buffer, and then additional render to color buffer, for each shadow-casting light). On the other hand, there is no need for a costly per-pixel shader over every shadow receiver (as in shadow maps).

  • Our current shadow volumes implementation allows for only one light casting shadow volumes. (This may be improved some day. Give us a shout at the forum if needed and support the engine development to make this happen sooner.)

  • Note that it's perfectly fine to use both shadow volumes and shadow maps in a single scene.

  • Shadow volumes do not take transparency by alpha-testing textures into account. E.g. you cannot use them to make shadows from leaves on a trees (where a leaf is typically modeled as a simple quad, covered with a leaf texture).

3. Requirements: geometry must be 2-manifold

For shadow volumes, all shapes of the model that are shadow casters must be 2-manifold. This means that every edge has exactly 2 (not more, not less) neighbor faces, so the whole shape is a closed volume. Also, faces must be oriented consistently (e.g. CCW outside). This requirement is often quite naturally satisfied for natural objects. Note that the consistent ordering allows you to use backface culling (solid=TRUE in VRML/X3D), which is a good thing on it's own.

You can inspect whether your shapes are detected as a 2-manifold by view3dscene: see menu item Help -> Manifold Edges Information. To check which edges are actually detected as "border edges" use View -> Fill mode -> Silhouette and Border Edges, manifold silhouette edges are displayed yellow and border edges (you want to get rid of them!) are blue.

You can also check manifold edges in Blender: you can easily detect why the mesh is not manifold by Select non-manifold command (in edit mode). Also, remember that faces must be ordered consistently CCW — in some cases Recalculate normals outside (this actually changes vertex order in Blender) may be needed to reorder them properly.

Remember that each shape must be 2-manifold (since Castle Game Engine 6.0.0). It's not enough (it's also not necessary) for the whole scene to be 2-manifold. This has advantages and disadvantages:

  • Advantage: We prepare and render shadow volumes per-shape, so we work efficiently with dynamic models. Transforming a shape (move, rotate...), or changing the active shapes (e.g. by changing Switch.whichChoice) has zero cost, no data needs to be recalculated.

  • Advantage: We can avoid rendering per-shape. We can reject shadow volume rendering for shape if we know shape's shadow will never be visible from the current camera view.

  • Advantage: Not the whole scene needs to be 2-manifold. If a shape is 2-manifold, it casts shadow. Your scene can have both 2-manifold and non-2-manifold shapes, it will work Ok, just only a subset of shapes will cast shadows.

  • Disadvantage: The whole shape must be 2-manifold. If you depended (in engine < 6.0.0) on the fact that you can build 2-manifold model from multiple non-2-manifold shapes — it will not work anymore.

Note that shadow casters may be transparent (have material with transparency > 0), this is handled perfectly.

4. Specify what lights cast shadows for shadow volumes (fields shadowVolumes and shadowVolumesMain for light nodes)

To all VRML/X3D light nodes, we add two fields:

*Light {
  ... all normal *Light fields ...
  SFBool  [in,out]  shadowVolumes     FALSE
  SFBool  [in,out]  shadowVolumesMain  FALSE  # meaningful only when shadowVolumes = TRUE
}

The idea is that shadows are actually projected from only one light source (with shadow volumes, number of light sources is limited, since more light sources mean more rendering passes; for now, I decided to use only one light). The scene lights are divided into three groups:

  1. First of all, there's one and exactly one light that makes shadows. Which means that shadows are made where this light doesn't reach. This should usually be the dominant, most intensive light on the scene.

    This is taken as the first light node with shadowVolumesMain and shadowVolumes = TRUE. Usually you will set shadowVolumesMain to TRUE on only one light node.

  2. There are other lights that don't determine where shadows are, but they are turned off where shadows are. This seems like a nonsense from "realistic" point of view — we turn off the lights, even though they may reach given scene point ? But, in practice, it's often needed to put many lights in this group. Otherwise, the scene could be so light, that shadows do not look "dark enough".

    All lights with shadowVolumes = TRUE are in this group. (As you see, the main light has to have shadowVolumes = TRUE also, so the main light is always turned off where the shadow is).

  3. Other lights that light everything. These just work like usual VRML lights, they shine everywhere (actually, according to VRML light scope rules). Usually only the dark lights should be in this group.

    These are lights with shadowVolumes = FALSE (default).

Usually you have to experiment a little to make the shadows look good. This involves determining which light should be the main light (shadowVolumesMain = shadowVolumes = TRUE), and which lights should be just turned off inside the shadow (only shadowVolumes = TRUE). This system tries to be flexible, to allow you to make shadows look good — which usually means "dark, but not absolutely unrealistically black".

In view3dscene you can experiment with this using Edit -> Lights Editor.

If no "main" light is found (shadowVolumesMain = shadowVolumes = TRUE) then shadows are turned off on this model.

Trick: note that you can set the main light to have on = FALSE. This is the way to make "fake light" — this light will determine the shadows position (it will be treated as light source when calculating shadow placement), but will actually not make the scene lighter (be sure to set for some other lights shadowVolumes = TRUE then). This is a useful trick when there is no comfortable main light on the scene, so you want to add it, but you don't want to make the scene actually brighter.

5. Optionally specify shadow casters (Appearance.shadowCaster)

Everything by default is a shadow caster (as long as it's 2-manifold). To stop some objects from casting shadows, set their Appearance.shadowCaster field to false. See Appearance.shadowCaster documentation.