Skip to main content

Shadows

This page provides detailed information about shadows in lighting and rendering including terminology, light entity settings, performance tips, and shadow caching.

Terminology​


Shadow Slice​

A shadow slice is a single square shadow map. Depending on light type, lights have different amounts of slices.

  • Spots (sometimes called projectors) have 1 slice.
  • Point lights have 6 slices (cube map faces).
  • Parallel lights have 4 slices (cascades).

Shadow Mip​

A shadow mip level is used throughout the shadow code and tools to denote the size of shadow slices. A slice's width/height is (r_shadowAtlasTileSize >> mip).

Static Shadow​

Static shadow can refer to either cached shadow map slices in the shadow cache or pre-compiled geometry for shadows, depending on the context.

Throttling/Time-Slicing​

Throttling or time-slicing refers to reduced update frequency.

Dirty/Stale Shadow​

A dirty/stale shadow is a shadow that has become invalid due to shadow caster changes in its frustum.

Light Entity Settings​


castShadows​

Enables shadows.

backFaceShadows​

Renders shadow maps using back-facing geometry (triangles facing away from the light source).

maxShadowVisibleRange​

Distance where the shadows are completely faded out. This distance is measured from the camera to the center of the world bound box of the light.

shadowMipBias​

A shadow mip adjustment applied after mip level has been computed based on light size, distance, and camera FOV. This value can be fractional and negative. This should be used in moderation since it has a performance impact. Too large negative values can completely disable distance-based level of detail.

shadowResolutionQuality​

  • 0: Default, max shadow resolution is mip 1 (i.e. 1024 if the project uses r_shadowAtlasTileSize = 2048).
  • 1: Max resolution is mip 0. This should be used very carefully since these shadows will take up to 4 times as much shadow cache space. In particular, be very careful with multi-slice light types (points and parallels). For example, a close-up point light will take up 2048 * 2048 * 6 = 24MPixels cache space (and twice that if static caching is enabled).

shadowRate​

Override for throttling:

  • ADAPTIVE: Use global throttling rules.
  • HIGH: Minimum rate is every other frame (30hz for 60hz titles, max stale frames = 1).
  • EVERYFRAME: Update every frame (Max stale frames = 0).

shadowFlags​

  • NO_ADAPTIVE_MIP: Disable dynamic shadow resolution.
  • NO_STATIC_CACHE: Disable static caching.

shadowSlopeScaleDepthBias​

Override for depth bias when drawing the shadow maps used to combat z-fighting.

Performance Tips​


  • Dynamic Shadow Effects:
    • Moving shadow casters is generally much cheaper than moving lights (e.g., rotating fans are good, rotating warning lights are expensive).
  • Point Lights:
    • A point light is the same as six spot lights as far as shadows are concerned.
    • Each shadow slice is updated independently. A point light can be cheap if most of its slices aren't touched by dynamic models.
  • Consider disabling shadow casting on moving props if their shadows contribute little to nothing to the scene's appearance. These things can cause many shadow updates that have no meaningful effect.
  • Parallel Lights:
    • Be careful with very large sun-lights - Even if the static cached cascades are updated at a very low frequency, they eventually have to be drawn, and they cause huge GPU time spikes when lights cover multi-million triangles.
  • Avoid penetrating adjacent level areas with light volumes where they have no effect due to being entirely in shadow.

Shadow Caching​


Shadow cache has two modes: static-cached and not-static-cached. Both modes cache shadows so they're not redrawn unless a shadow caster moves. In static cache mode, static geometry is rendered to a separate cache slot and reused to create the composite shadow. This is achieved by copying the static shadow slot to the composite shadow slot and rendering all dynamic models on top, making dynamic model shadows cost-effective even with expensive static world geometry.

Static Cache Use Selection​

Static caching is enabled per slice using global rules settings and can also be disabled per light. Dynamic lights never use static caching. This is due to a trade-off between cache-space usage and performance, as static-cached slices take up twice as much space.

r_shadowStaticMode determines the base rule for static caching usage:

  • Mode 0: All slices with mip >= r_shadowMinStaticMip are static cached.
  • Mode 1: r_shadowStaticMipTriThresholds is a list of triangle-thresholds per mip. Static caching is enabled when a shadow slice's static triangle count exceeds the threshold for its current mip level.

Static Cache Behavior​

Each shadow slice can have multiple mip-level versions in the cache. Lower static cached mips are force evicted unless kept by r_shadowKeepStaticMipsMask.

Static cache slot updates vary for spot/point and parallel lights. Spot/points updates are triggered by light intersection testing when models change their dynamic state. Parallel light statics update only when the player moves a certain distance from the last drawing, treating all models as dynamic for cascades indicated by r_shadowParallelCompositeAllEntitiesMaxSlice to avoid glitches.

r_shadowParallelMoveMargin sets the distance threshold for a parallel static cache slot update, with the distance per cascade as (r_shadowParallelMoveMargin << cascade).