Hero image: a long colonnade lit by the sun, with crisp, stable shadows on the walkway

Shadows are one of the details that make a 3D scene feel grounded and believable, and also one of the easiest to get visibly wrong. This update makes the sun’s shadows steadier, cleaner and much easier to tune, and brings the same improvements to spot, point and area lights.

In short:

In this article

Why shadows matter

Take the shadows out of a scene and everything seems to float. Objects lose their connection to the ground, depth is harder to read, and the lighting looks flat. A good shadow tells you where things are, how big they are and where the light comes from.

A bad shadow is just as noticeable. Edges that crawl when the camera moves, stripes across a surface, or a shadow that detaches from the object casting it all break the illusion immediately. That is what this release goes after.

How does a real-time shadow work?

Real shadows come from light being blocked, but a real-time renderer doesn’t trace light. It uses a cheaper and very effective trick called a shadow map:

  1. Look from the light. We render the scene from the light’s point of view, but we only store the distance to the nearest surface for each pixel. The result is the shadow map.
  2. Look from the camera. For each pixel on screen, we work out where it sits from the light’s point of view and compare its distance with what the shadow map recorded.
  3. Decide. If something closer to the light was recorded, the pixel is in shadow. Otherwise, it is lit.

Diagram: a shadow map in three steps

The catch is that a shadow map is a texture with a fixed resolution. Think of it as a low-resolution photo of the scene taken from the light. Almost every shadow technique is a way of getting the most out of that limited resolution.

What about ray traced and path traced shadows?

Shadow maps are an approximation, so it is fair to ask why we don’t simply trace the light. On GPUs with hardware ray tracing we can: fire a ray from each visible point towards the light, and if something gets in the way, the point is in shadow. There is no texture resolution to run out of, so there is no blockiness, no acne and no peter-panning, and soft shadows come out accurately. Path tracing goes further and follows many random light paths as they bounce around the scene, so shadows, reflections and indirect lighting all come from the same process. Evergine’s path tracer sample shows what this looks like.

Diagram: shadow maps compared with ray traced shadows

The price is performance. A clean image needs several rays per pixel every frame, usually followed by a denoiser. The scene needs an acceleration structure that must be rebuilt as objects move. And rays scatter in every direction, which is much harder on GPU memory than rasterizing triangles into a texture. Even high-end desktop GPUs budget their rays carefully, and most phones, tablets and low-end devices have no hardware ray tracing at all.

That is why shadow maps are still the workhorse of real-time shadows: they are cheap, predictable and run on virtually every device. Ray tracing is a great option where the hardware allows it, but shadow maps are what keep a scene running everywhere else, which is why we keep investing in them.

The sun problem, and cascades

A directional light stands in for the sun, and the sun lights everything. A single shadow map would have to cover the whole visible world, so a texture stretched over hundreds of meters would give blocky shadows right in front of the player, where detail matters most.

The classic solution is cascaded shadow maps. We split the view into slices by distance from the camera: a small slice close to you, a larger one behind it, and so on. Each slice gets its own shadow map, so nearby shadows are sharp and distant ones are coarser. Far-away things are small on screen, so you rarely notice.

Diagram: the camera frustum split into four cascades, each rendered into a shadow map of the same resolution

Evergine already used cascades. The new release is about making them behave better.

Where the old approach struggled

Three problems came up again and again:

What’s new

Cascades that no longer shimmer

The shadow space now depends only on the orientation of the light, not on where the camera is looking. Each cascade is also snapped to the shadow map’s texel grid, and its size only changes in small steps of about 2%. As a result, moving or rotating the camera no longer changes which texel covers which part of the world, and the shimmering is gone.

Diagram: the shadow edge across three frames, before and after snapping the cascades to the texel grid

Bias that understands your scene

Bias is now applied in world space and measured in shadow map texels. Two new properties, ShadowDepthBias and ShadowNormalBias, automatically scale with the size of each cascade’s texels and with the softness of the shadow filter. A single setting now behaves sensibly from the closest cascade to the farthest one, which removes the acne bands at grazing angles and the floating shadows in the distance.

Diagram: too little bias, balanced bias and too much bias

Spot, point and area lights

The texel-based bias model is not limited to the sun. Spot, point and area lights now use it too, so ShadowDepthBias and ShadowNormalBias mean the same thing on every light type. Along the way we fixed two problems:

Diagram: the guard band that makes cube faces overlap around a point light

Cascades you can tune

Directional lights now expose the trade-offs between quality and performance:

Diagram: Stable and Tight cascade fit modes

Note: The update interval has no effect when a light is rendered by several cameras.

Better debugging

Light Debug Mode now shows the color of each cascade (and of each cube face for point lights), plus a checkerboard of 8x8 shadow map texels, so you can see exactly how dense your shadows are on screen. Spot lights get their own DebugMode too.

We also fixed related issues: cascade split distances now use the correct formula, orthographic cameras with a zero near plane no longer produce invalid splits, and rendering several cameras with multiple shadow-casting directional lights no longer makes the lights overwrite each other’s shadow maps. Backends handle depth clamping consistently, so objects in front of a cascade are no longer clipped.

See the difference

Drag the slider to compare the previous shadow system with the new one in the same scene and camera position.

Before: Colonnade seen from the walkway, with the previous shadow system After: Colonnade seen from the walkway, with the new shadow system
Comparison 1
Before: Test scene, view 2, with the previous shadow system After: Test scene, view 2, with the new shadow system
Comparison 2
Before: Fence and column base, with the previous shadow system After: Fence and column base, with the new shadow system
Comparison 3
Before: Test scene, view 4, with the previous shadow system After: Test scene, view 4, with the new shadow system
Comparison 4
Before: Test scene, view 5, with the previous shadow system After: Test scene, view 5, with the new shadow system
Comparison 5
Before: Colonnade and obelisks in the distance, with the previous shadow system After: Colonnade and obelisks in the distance, with the new shadow system
Comparison 6

Getting started

You can adjust everything from the editor or from code. The values below are illustrative starting points, so tune them for your scene:

// Get the directional light of your sun entity
var sun = sunEntity.FindComponent<DirectionalLight>();

sun.IsShadowEnabled = true;
sun.ShadowDistance = 100;                   // How far from the camera shadows are rendered
sun.ShadowCascadeCount = 3;                 // Between 1 and 4

// Keep cascades stable, and fade shadows out smoothly
sun.CascadeFitMode = DirectionalLight.CascadeFitModes.Stable;
sun.ShadowFadeFraction = 0.1f;
sun.CascadeBlendFraction = 0.1f;

// Bias is measured in shadow map texels
sun.ShadowDepthBias = 1;
sun.ShadowNormalBias = 1;

This enables a shadow-casting sun with three stable cascades, smooth blending between them and a gentle fade at the shadow distance. If you see acne, raise the biases slightly. If shadows look detached from their casters, lower them.

Upgrading from the previous version

A few changes affect existing projects:

Try it and tell us what you think

Ready to give your scenes steadier, cleaner shadows? Update Evergine, open a scene with a directional light and try the new options.

We’d love to hear what you think.

José Antonio Collado Araque
Author
José Antonio Collado Araque
Graphics Engineer - Researcher