In our next Tiny Glade update we'll bump the light limit from 32 to 32k while keeping the cost more or less the same.
The small limit has been a cop out, as we didn't quite know how to render huge numbers of (screen-space) shadow-casting lights without severe performance degradation.
Initially, I tried ReSTIR, but I didn't quite enjoy that even with a single light source it still produced noise. For our specific case it was kinda silly; I'd do all this complicated machinery just to end up denoising a known quantity.
So I figures, why not just look up the light IDs stored in reservoirs and use them directly, ignoring the computed contribution weights? This way there's no noise from ReSTIR, and you don't need to be careful with resampling to avoid pretty terrible halo artifacts and/or crawling temporal noise.
Dec 5, 2024 22:11So I run ReSTIR at half resolution, but when it comes to lighting, randomly sample 8 reservoirs around every full-res pixel, and use their lights, discarding weights
To avoid tracing shadows to all, I run WRS (weighted reservoir sampling) using the unshadowed contribution, and pick just two to eval
Duplicate lights need to be removed before WRS/eval. I've tried using a Bloom filter, but the bang/buck wasn't there. I just brute-force check all lights discovered thus far in a given pixel.
If any lights get dropped by this, I also use QuadReadAcrossX/Y to discover lights from neighboring pixels.
This turns ReSTIR into stochastic (and lossy) light culling, exploiting the property that reservoirs contain a decent distribution of lights around each point.
Artifacts:
* Noise - especially with lights appearing & moving
* Darkening in areas affected by > 8 lights
This vs brute force when still:
To further accelerate this, I also bin lights into a 64x16x64 grid, and sample ReSTIR candidates from it.
The grid itself is stochastic too; there's a limit on the number of lights in each cell; if exceeded, lights get randomly replaced. It's kinda between ReGIR and light trees.
Of course this is just a silly hack and and likely has tons of issues in the general case. This ain't MegaLights, it's just BadReSTIR (TM).
Cost as seen in the video is 1.5-1.8ms at 1440p on a 6800XT in stable power state.
2.25 screen-space shadow rays per pixel: 2 in eval and 0.25 inside ReSTIR.
Oh and there's also a fast path if a light grid cell has <= 2 lights. But otherwise I think that just about covers it.
Here's a higher-quality version of the video:
youtu.be/VLLYcZgUQzI
Tiny Glade - many lights test
YouTube video by Tomasz Stachowiak
P.S. Why not a light tree? Because our lights are all same-ish and small-ish in size, and we can also just randomly feed more light grid samples into ReSTIR instead of doing the complex tree evaluation with splitting and stuff.
P.P.S Why not tiled light culling? Because Tiny Glade is a UGC game, anything goes, and we'd still end up with too many lights in each tile for shadowing. Could WRS that instead, but ReSTIR gives better candidates.
Oh yeah, and there's probably also darkening around depth discontinuities, especially on thin features, but I've been "lucky" in my testing so far. We'll see what happens once we unleash this on our players :)
(But I've also just realized that there's 16 spare bits in each reservoir, which could be used to encode depth, and cheaply reject poorly fitting reservoirs, reducing the bias)