About Store Forum Documentation Contact



Post Reply 
RT sun shadows, RT specular reflections, Clustered/froxel lighting
Author Message
Fex Offline
Gold Supporter

Post: #1
RT sun shadows, RT specular reflections, Clustered/froxel lighting
RTGI+RT SHADOWS+RT Reflections

[Image: Screenshot-From-2026-07-09-22-28-11.png]


No GI, CSM Shadows,

[Image: Screenshot-From-2026-07-09-22-28-42.png]


AI written summary:

I've been extending the engine's Vulkan renderer with three new lighting features:
    []Ray-traced sun shadows []Ray-traced specular reflections
  • Clustered/froxel lighting

All three are opt-in and disabled by default. They are also pure runtime toggles, so no shader pak regeneration is needed to use them.

The two ray-tracing features share a single scene acceleration structure, meaning the cost of “going RT” is paid once.

Everything below was measured on an AMD Radeon RX 6650 XT.



The Shared Foundation: One Scene TLAS, Zero Registration

Both RT features use the ray-query acceleration structure originally introduced for dynamic GI.

The best part is that nothing needs to be manually registered. Enable:

GIRayTrace.auto_scene = true;
The renderer then feeds every opaque mesh it draws into the acceleration structure automatically. Streamed world areas enter the RT scene as they load, with no additional application code.

The RT world is the retained union of everything the camera has seen. Looking away from an object does not remove it from shadow, reflection, or GI rays. Meshes leave the acceleration structure only when they are actually destroyed.

Acceleration-structure rebuilds are recorded directly into the frame command buffer, so there is no per-rebuild GPU stall.

Animated characters are supported too.

GIRayTrace.addSkinnedOccluder(mesh, skel);
This GPU-skins the character into its current world pose every frame and builds a per-frame BLAS. The character then:
    []Casts a correctly posed ray-traced shadow []Appears fully textured in ray-traced reflections
  • Costs only a fraction of a millisecond per character

One deliberate design choice is that dynamic characters receive GI but do not contribute to it.

A deforming body inside a sparsely updated probe field causes the surrounding indirect lighting to strobe. To avoid this, the GI probes trace against a second, static-only TLAS.

This is standard DDGI practice, and it eliminated a very visible “the lighting on my character keeps jumping” artifact.



Ray-Traced Sun Shadows

For each pixel, the renderer traces one or more rays toward the sun against the scene TLAS:
  • []Hit = shadowed []Miss = illuminated

The detail that makes this a drop-in replacement is that the engine's existing sun-application stage already consumes a pre-resolved full-screen visibility mask.

The RT pass simply writes that mask. The light shaders remain completely unchanged.

When RT shadows are enabled, the renderer skips the entire conventional shadow-map pipeline:
    []CSM cascade rasterization []Cascade resolve
  • Shadow-mask blur

Advantages over shadow maps
  • No aliasing, cascade seams, or peter-panning.

    A thin floating bar can cast a clean, visibly detached shadow that a shadow map cannot reproduce reliably at any practical cascade resolution.
  • True contact-hardening soft shadows.

    Multiple rays are cone-sampled across the sun's disc using a Fibonacci spiral. The pattern is rotated per pixel and temporally jittered so TAA converges the penumbra.

    The result is a shadow that remains sharp at the contact point and becomes progressively softer as the distance between the occluder and receiver increases.
  • Cost comparable to CSM.

    Because RT shadows skip cascade rasterization entirely, the net lighting-phase cost can land near parity with conventional shadow maps in typical scenes.

    An eight-ray, half-resolution soft-shadow pass measured approximately 0.89 ms, roughly the same cost as the CSM path it replaced.

Full resolution is the default. Half resolution can undersample thin, moving occluders such as animated characters.

Ray count is the primary performance-versus-quality control.

RtShadows(true);

RtShadowQuality(0.03f, 4);
The first quality parameter controls sun-disc softness. A softness value of zero produces hard shadows. The second parameter controls the number of rays per pixel.

Demo: Tutorial_14_RTShadows
    []R switches live between ray-traced shadows and shadow maps []- / = adjusts shadow softness
  • An on-screen HUD displays GPU cost

The initial implementation supports the sun only.



Ray-Traced Specular Reflections

The reflection pass traces one:

reflect(eye, normal)
ray per pixel against the same scene TLAS.

Reflection hits are fully lit and textured using:
    []Direct sunlight []A secondary shadow ray []Sky ambient lighting []GI probe irradiance evaluated at the hit point
  • The hit material's actual albedo texture

The result is written using the engine's existing SSR-buffer format.

That allows the existing lit-color composite stage to fold the ray-traced result into the scene with no changes to the combine shader. The normal roughness-aware reflection fade is retained.

Where it beats SSR

Screen-space reflections can only reflect geometry that is already visible on screen.

The RT path can reflect:
  • []Content behind the camera []Objects around corners []Off-screen world geometry []The sky

It also avoids the characteristic smearing and disappearing reflections that occur when the camera moves or reflected geometry leaves the screen.

At a demo resolution of 3072 × 1296, the measured light-phase cost was approximately 0.17–0.24 ms.

That was inexpensive enough that I did not add a half-resolution mode.

RtReflections(true);
Demo: Tutorial_14_RTReflections

The scene contains a mirror floor and chrome ball. Press R to switch between ray-traced reflections and the traditional SSR-plus-cubemap path for a direct comparison.



Combined Ray-Tracing Showcase

Tutorial_14_RTShowcase runs all three ray-traced systems together:
    []Dynamic GI []Sun shadows
  • Specular reflections

All three systems share the same automatically constructed TLAS.

The demo runs in a streamed world containing an animated character. Each feature can be toggled independently:
    []G — dynamic GI []H — ray-traced shadows
  • R — ray-traced reflections



Clustered / Froxel Lighting

Clustered lighting addresses a different renderer bottleneck.

The classic deferred per-light path renders each light with a separate draw call. When many point lights overlap, the renderer may read the G-buffer and blend lighting into the same pixel once for every affecting light.

The clustered path targets unshadowed point lights such as:
  • []Torches []Projectiles []Muzzle flashes []Other scenes containing large numbers of dynamic lights

A compute pass bins lights into a froxel grid containing:
  • []32 × 18 screen-space tiles []24 exponential depth slices

A full-screen pass then shades all lights affecting a pixel in one operation using the exact same BRDF as the classic deferred-light path.

The clustered output was verified for pixel parity against the per-light path.

Measured GPU_TIMER_LIGHT results with overlapping, unshadowed point lights:
    []64 lights: approximately 5% faster []128 lights: approximately 13% faster
  • 256 lights: approximately 19% faster

The performance advantage grows with light count.

The honest framing is that the existing per-light renderer is already depth-bounded. It is not a naive full-screen-per-light baseline.

Clustered lighting is therefore a scalability feature rather than a free performance win in every scene, which is why it remains opt-in.

Cases that are not handled by the clustered path automatically fall back to the classic renderer:
    []Shadowed lights []The sun []Water rendering []MSAA []Clear-coat materials []Mirrors
  • Sub-rectangle views

This keeps rendering parity from being placed at risk.

ClusteredLights(true);
Demo: Tutorial_14_ClusteredLights

The demo presents a twilight field containing 256 overlapping lights.
    []C switches between clustered and per-light rendering []Up / Down changes the light count, up to 512
  • The light-phase GPU timer is displayed on screen



Additional Notes
  • All three systems are currently Vulkan-only.
  • The two ray-tracing features require ray-query-capable hardware.
  • On other rendering backends, every API call is a safe no-op. The tutorials still run, but the renderer remains on the classic paths.
  • The complete stack recently went through an adversarial, multi-pass code review and a synchronization-validation soak.
  • Synchronization validation reported zero hazards.
  • Sub-render re-entrancy, resource lifetime across streamed worlds, and thread safety of the retained RT scene received particular attention.

Design Documents

The repository contains detailed design documents for each system:
  • []docs/rt-shadows-design.md []docs/rt-reflections-design.md []docs/rt-skinned-occluders-design.md []docs/clustered-froxel-lights-design.md

In this repo:
https://github.com/DrewGilpin/EsenthelEngine
Yesterday 03:39 AM
Find all posts by this user Quote this message in a reply
Post Reply