Skip to content

Light tiles: the lists follow the camera - #88

Open
yujiaxu-ops wants to merge 12 commits into
mainfrom
light-tiles-reproject
Open

yujiaxu-ops wants to merge 12 commits into
mainfrom
light-tiles-reproject

Conversation

@yujiaxu-ops

@yujiaxu-ops yujiaxu-ops commented Nov 14, 2026 •

Copy link
Copy Markdown
Collaborator

Follows #87. A camera hit read its light list at its own pixel: with the camera turning, the list of another part of the scene. Now it reads the list of the tile it was seen in when the lists were built: its point, moved back by its motion, through that frame's camera (Params::lt_cam); the nearest tile's when it was off screen.

New shot: bench-window rooms-turn (lamp-rooms-wide, the camera turning 2 degrees a frame for 24 frames, then still).

Measured (M5 Pro, dE / p99 against the offline reference)

frame before #87 #87 this
last moving 2.00 / 8.57 0.86 / 4.82 0.80 / 3.54
6 frames after 2.24 / 8.75 0.80 / 4.09 0.75 / 3.09
30 frames after 1.96 / 7.43 0.69 / 2.80 0.67 / 2.42
settled 1.00 / 3.56 0.67 / 1.93 0.67 / 1.92

(The reference is the cached light-tree render #87's text mentions.) Still shots as #87.

Cost: 0.19 ms of the trace in a scene with lists (lamp-rooms 3.57 -> 3.76 ms). Bistro 8.43 ms on both.

Checks

  • cargo test --release: 143 passed. test-render: 192 / 192, with the loop guard.
  • Loop guard at 5 limits on Bistro and both lamp-rooms scenes: no stop.

Not done

  • No depth test: a surface newly in view reads the list of what was there before (a guide only: noise, not bias).
  • The 0.19 ms is more than the arithmetic explains: the lookup now sits in the path loop's first iteration. Not reduced.

🤖 Generated with Claude Code


Note

Medium Risk
Changes live light sampling and shader uniforms on camera motion; wrong projection would add noise (not bias per design), with a small trace cost increase on list scenes.

Overview
Light tile resampling now follows the camera instead of the current pixel. Paths look up the 8×8 tile list where each motion-compensated hit point appeared when lists were built, by reprojecting through a new lt_cam uniform (last frame’s position, forward, right, up). Live frame setup stores that camera with the list metadata; lt_list_start takes a world position rather than gid.xy and runs in the path loop after motion is known.

Docs (RUNTIME.md, ROADMAP.md) describe the behavior and bench results for a turning camera on lamp-rooms-wide. bench-window gains a rooms-turn shot (2°/frame for 24 frames, then settle) to regression-test moving views.

Reviewed by Cursor Bugbot for commit f587c00. Bugbot is set up for automated code reviews on this repo. Configure here.

yujiaxu-ops and others added 12 commits November 15, 2026 02:07
scenes/lamp-rooms.ts (4 x 3 rooms, 96 spot and point lamps with ranges, no
sky) and lamp-rooms-wide.ts (12 x 8, 768), and their bench-window shots
still-rooms / still-rooms-wide: the case the other shots lack, where a light
sampler has to find the lamps a point can see.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Each camera pixel reports the local light its resampled shadow ray found; a
pass makes each 8 x 8 tile's reports a list; the next frame's paths take 4
candidates from their tile's list (2 past the camera hit) beside the uniform
ones, which then stand for the lights the list lacks. Unbiased whatever a
list holds. Off in scenes whose local lights are summed exactly.

still-rooms dE / flicker 0.67 / 0.138 -> 0.63 / 0.068; still-rooms-wide (768
lamps) 1.01 / 0.126 -> 0.58 / 0.081; Bistro and Kabukicho unchanged.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The lists' kernel code is compiled only for scenes with more than 8 local
lights (FEAT bit F_LISTS): others get main's kernel (Bistro trace 8.50 /
8.50 ms). Each list carries a 64-bit mask of its lights' low index bits, so
most uniform candidates skip the search. 2 list candidates by default.
light/many-shadowed(-live): 24 shadowed point lights, 10 hidden, analytic.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Lists are fed by the camera hit's resampling, which covers shadow-casting
lights only: a scene without more than 8 of them (Kabukicho: none) keeps
main's kernel. The mask saved nothing in the trace and cost the pass 0.11 ms.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
- The resampling's weight is one expression with or without a list (a
  list's candidate weighed relative to a uniform one's).
- RIS_EXACT is a constant in gpu.rs, held to the shader's by a test.
- THREERT_LT_LIST is gone (LIST_CANDIDATES).
- The lists' place in the direct buffer comes from layout::light_lists.
- light/many-shadowed at 1024 spp (64 left ~2 % of noise in its patch).

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The lists were read at the pixel's own position: with the camera turning
they belonged to another part of the scene. Now the camera hit's point,
moved back by its motion, goes through the camera the lists were built
under (Params lt_cam); off screen, the nearest tile's.

bench-window rooms-turn (lamp-rooms-wide, 2 degrees a frame), last moving
frame, dE / p99: main 2.00 / 8.57, lists at the pixel 0.86 / 4.82, this
0.80 / 3.54.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
A small integer's bits are a denormal float, which a driver may flush to
zero (the lists would read empty and the guiding stop, silently). Found in
review.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant