fix(render): jitter the volumetric march per pixel (IGN); file #421 for the shadow transform buffer (Campaign VM VM5)

Fixed step-count volumetric ray marches (Low/Medium presets, e.g. 40 steps)
sample the same t values at every pixel, so a shadow-cascade edge crossing
lands at the same march step across a whole neighbourhood of pixels and
reads as visible concentric banding around light shafts. Offset each
pixel's march phase with interleaved gradient noise (Jimenez 2014,
fract(52.9829189 * fract(dot(gl_FragCoord.xy, vec2(0.06711056,
0.00583715))))) instead of the fixed 0.5 sub-step center: t = (step + ign)
/ steps. This decorrelates the step boundaries across pixels, turning the
banding into fine per-pixel noise that the existing quarter/half-res
upsample already averages away. Pack-on only — atmospheric_volumetric is
never used on the retail default path; extinction, colour, and early-outs
are unchanged.

Regenerated SPIR-V via tools/compile-shaders.ps1: only
atmospheric_volumetric.frag.spv and shaders.manifest.json's source hash
for that stage changed.

Also files #421 (docs/ISSUES.md) for Campaign AR review finding F7: the
directional-shadow pass's DirectionalShadowTransformBufferSet duplicates
the main pass's N.5 instance SSBO instead of binding it, to be resolved
once the planned GPU-culling step for shadow cascades lands. Updates the
VM5 ledger row and outcome note in
docs/plans/2026-08-22-visualmaster-campaign.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-22 22:56:03 +02:00
parent 6e76314b08
commit b1e9912535
5 changed files with 42 additions and 3 deletions

View file

@ -24,6 +24,33 @@ What does NOT go here:
- Every session: scan OPEN issues at start; promote/close anything we touched during the session before ending.
- Promoting to a Phase: mark as `DONE (promoted to Phase X)` + commit SHA where the Phase entry landed.
## #421 — Directional-shadow pass uploads its own transform buffer instead of binding the main pass's instance SSBO
**Status:** OPEN — filed 2026-08-22 at Campaign VM VM5 (post-M7 / with GPU culling).
**Component:** rendering / render packs (Campaign AR Tier 2)
**Description:** `DirectionalShadowTransformBufferSet`
(`src/AcDream.App/Rendering/DirectionalShadowTransformBufferSet.cs`)
re-composes every caster's world matrix from the same
`MeshRef.PartTransform x LocalToWorld` inputs the main pass uses (same
`WbDrawDispatcher.ComposePartWorldMatrix`, so there is no second pose —
Campaign AR constraint 5 is honoured in spirit) and uploads them into a
second GPU buffer each frame for the animated subset. This is where the
65,536-matrix binding ceiling regression lived (fixed in Campaign AR
Stage 1). Cost is measured small (0.110.21 ms CPU incremental on the
reference matrix) but it is a duplicate of the N.5 instance SSBO the main
pass already binds.
**What to do:** when the planned GPU-culling step for shadow cascades
lands (Campaign AR plan, "If that is too expensive, the next permitted
step is GPU culling"), bind the main pass's instance transform SSBO for
the caster pass instead of maintaining `DirectionalShadowTransformBufferSet`,
and delete the set. Not before — the shared buffer's lifetime/flight
rules differ (main pass ring vs retained caster topology) and unifying
them without the culling redesign would just move the ceiling.
Reference: Campaign AR review F7,
`docs/research/2026-08-22-campaign-ar-review.md`.
## #420 — Client crashes on the character-select screen (`UiButton.OnDraw` null media-state key)
**Status:** ✅ FIXED 2026-08-19, root cause proven by a reverting test.