Realtime Shadow Techniques Used By Grit
Depth Shadow MappingGrit has full dynamic shadows that are calculated in real time on the GPU. The basic technique is called depth shadow mapping http://en.wikipedia.org/wiki/Shadow_mapping. It involves rendering the scene from the light (the sun or the moon) into a texture, called a 'shadow map'. The shadow map is updated every frame, because objects move and so does the light. However the colour of the scene is ignored, we are only interested in the depth of everything visible from the light, i.e. the distance from the light to its nearest occluder in all directions. When the scene is rendered from the player's point of view, this shadow map is used as a reference to help decide if a given pixel is the closest one to the light (in which case it is not in shadow) or whether there is something else that is closer (in which case it is rendered darker because it is in shadow).
Perspective TransformThere is a perspective transform applied in order to concentrate as many of the shadow map's texels as possible in the area nearest the player. There are many techniques but the one used in Grit is called LiSPSM (LIght Space Perspective Shadow Mapping) http://www.cg.tuwien.ac.at/research/vr/lispsm/. The worst case is when the sun is directly behind you, in which case no perspective transform can be applied, and the shadow is very low detail and noisy. If you look 90 degrees to the sun direction however, the shadows will be a lot crisper due to the use of LiSPSM. Note that increasing the resolution of the shadow map texture will also make the shadows crisper, but will cost memory and performance.
The perspective transform changes every frame depending on the light direction and the chase cam's direction. Sometimes the changes can be quite severe. This causes an unavoidable 'crawling' effect in the shadows.
Covering Longer DistancesThere are in fact 3 shadow maps used. One for the area closest to the player, one to cover the area further away, and the 3rd one for the furthest reach of the shadow (200 metres). They are all the same size textures, but the one nearest to the camera covers a much smaller area and thus the shadows are better defined. Another way of looking at this is that it allows shadows to appear much further from the player, without compromising the quality of shadows near the player. The exact technique used in Grit is called PSSM (Parallel Split Shadow Mapping) http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html. Sometimes you can see the transition from one shadow map to the next, as a sudden decrease in shadow quality.
Soft ShadowsIf each screen pixel was merely tested for being in shadow or not, the shadows would be very hard-edged because of the sudden transition from 'in shadow' to 'not in shadow'. To avoid this, we soften the shadows using a technique called PCF (Percentage Closer Filtering) http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html. This boils down to testing the shadow map several times per screen pixel, and taking the average. The appearance is that several faint shadows have been overlaid in slightly different positions, to produce a blurred effect. It can get very slow but there is hardware support that we are currently not using that can help. This will be implemented eventually http://code.google.com/p/grit/issues/detail?id=125.
Possible ArtefactsThere are certain things that can go wrong with dynamic shadow implementations like the ones used in Grit. There are some things to avoid when modelling objects, in order to avoid problems.
Holes in shadowsSince the shadows are calculated by rendering the scene from the sun (or moon) you have to make sure that your geometry, when viewed from this direction, appears to be opaque. This means cliffs must have polys around the back facing the sun, in order to the sun shining through them to the front. Another alternative is to turn on the rendering of backfaces in the material.
If your map is an island that drops below sealevel in all directions, you don't have to worry about this.
Shadow Texture StretchSince the shadow texture is projected onto the scene from the light, surfaces that are perpendicular to the light (e.g. flat ground at sunset) will experience very bad texture stretch. This causes aliasing artifacts. Because of the LiSPSM perspective transformation, the artifacts have a very nasty sawtooth appearance.
To visualise the aliasing, we can use:
lua> debug_cfg.falseColour = "SHADOWYNESS"
This shows the shadow mask projected onto the scene with equal intensity on all triangles.
Luckily these areas should not be receiving very much light due to the diffuse lighting equation. E.g. if the light is incident at 15 degrees then the amount of lighting would only be 25% (i.e. sin(15)) of the amount of light that it would receive at 90 degrees. This means the shadow is much less distinct in these areas. This shows the actual shadow, i.e. incorporating the diffuse lighting as well:
lua> debug_cfg.falseColour = "SHADOW_MASK"
Normal BendingIf your mesh has sharp edges between polys (an angle of more than 20 degrees for example) and is smooth shaded, then the normals interpolated across that mesh will be considerably different to the 'true' normal of that face (i.e. the normal you would calculate using the positions of the 3 vertexes). For example, if you model a cube and use smooth shading then the normal of each face will be orthogonal, but the normals will be smoothly interpolated around the cube causing a huge amount of normal bending at the edges of each face.
Normal bending is usually OK, but causes a problem with shadows. This kind of artefact often occurs on sharp terrain like cliffs. It causes areas to be subject to lighting that would not ordinarily be lit, and therefore causes shadow artefacts to appear that would ordinarily be hidden in the darkness. If the face is in-line with the light, e.g. cliffs at noon, and there is significant normal bending, then the interpolated normal may point slightly towards the sun even though the polygon is at 90 degrees to the sun.
The best fix for this is to model in a way that does not have such extreme smoothing of the normals. However to avoid explosion of polycount, there is also an alternative — the shadowObliqueCutOff material property.
Note, however, that the shadows are less distinct when using this feature — it is better to avoid the normal bending as much as possible when modelling the terrain in the first place.
AcneImprecision in the shadow map, which records the distance of each occluder from the light, causes the shadow to fluctuate, causing unpleasant high frequency transitions from 'in shadow' to 'not in shadow' on every surface. The engine will avoid shadow acne by adding a certain amount of bias to the shadow. Thus the shadow is pushed away from the light by enough in order to avoid the noise being an issue.
Here is a diagram illustrating the problem:
You don't need to worry about this as a modeller, as the engine handles it internally. However, you should be aware that there is a bias applied to the shadow by the engine in order to avoid it.
Additional BiasYou can add additional bias yourself, in the material, in order to get rid of other artefacts. For example here there are unwanted shadows on the tank. There is simply not enough fidelity in the dynamic shadows to do this well enough. We would rather there were no shadows at all.
One way to avoid this is to avoid these kind of nooks and crannies in the geometry of the object. However since these contribute greatly to the appearance of objects, we would prefer some other solution. We can add another 0.1m to the bias in order to push the shadow far enough away from the object to hide shadows on the high detail parts of the mesh.
Shadow DisconnectionToo much bias can cause a problem in itself though. If the bias is increased enough, the shadow will move so far from the object that there will be a 'gap' where the object meets the ground. This gives the unwelcome appearance that the object is 'floating' above the ground. This is also illustrated in the above diagram.
The bias automatically used by the engine is carefully chosen to be as small as it can be. However as a modeller you must also make sure your additional bias is not too large as well.