diff --git a/docs/ISSUES.md b/docs/ISSUES.md index 0e951a27..c4376a55 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -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.11–0.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. diff --git a/docs/plans/2026-08-22-visualmaster-campaign.md b/docs/plans/2026-08-22-visualmaster-campaign.md index 72e96722..70b5571e 100644 --- a/docs/plans/2026-08-22-visualmaster-campaign.md +++ b/docs/plans/2026-08-22-visualmaster-campaign.md @@ -81,7 +81,7 @@ single-pass path darkens by ~10 %. Neither explains a scene-wide level. | VM2 | F3 — which retail detail path ran | **CLOSED 2026-08-22** — single-pass; see [cdb note](../research/2026-08-22-vm2-retail-detail-path-cdb.md) | | VM3 | F4 — linear-light post stack | automated + **owner visual** | | VM4 | F5 — overclaiming docs, incl. the reviewer's own | **CLOSED 2026-08-22** | -| VM5 | F8 — volumetric banding; F7 filed | automated | +| VM5 | F8 — volumetric banding; F7 filed | **CLOSED 2026-08-22** (#421) | | VM6 | Foliage wind | automated + **owner visual** | | VM7 | Closeout: full gates, register, roadmap, merge | **owner** | @@ -252,6 +252,9 @@ Add the VM0 numbers as the new baseline table. Docs only; no code. - File issue: the shadow pass uploads a second transform buffer; a future GPU-culling step should bind the main pass's instance SSBO. Not built here. +**OUTCOME (2026-08-22):** IGN jitter landed in atmospheric_volumetric.frag +(pack-on only); F7 filed as #421. + ## VM6 — Foliage wind (the feature) ### What "real movement" means here diff --git a/src/AcDream.App/Rendering/Shaders/atmospheric_volumetric.frag b/src/AcDream.App/Rendering/Shaders/atmospheric_volumetric.frag index 94cd28ab..843fd0c2 100644 --- a/src/AcDream.App/Rendering/Shaders/atmospheric_volumetric.frag +++ b/src/AcDream.App/Rendering/Shaders/atmospheric_volumetric.frag @@ -52,10 +52,19 @@ void main() vec3 sceneWorld = reconstructWorld(vUv, sceneDepth); int steps = clamp(int(uPackParams0.z + 0.5), 1, 64); float lit = 0.0; + // Interleaved gradient noise (Jimenez, "Next Generation Post Processing + // in Call of Duty: Advanced Warfare", 2014). Every pixel would otherwise + // sample the same t values along its march, so shadow-cascade edge + // crossings line up between neighbouring pixels and read as visible + // concentric banding. Jittering the march's starting phase per pixel + // decorrelates those step boundaries, turning the banding into fine + // noise that the existing quarter/half-res upsample already averages + // away. + float ign = fract(52.9829189 * fract(dot(gl_FragCoord.xy, vec2(0.06711056, 0.00583715)))); for (int step = 0; step < 64; ++step) { if (step >= steps) break; - float t = (float(step) + 0.5) / float(steps); + float t = (float(step) + ign) / float(steps); lit += directionalVisibility(mix(nearWorld, sceneWorld, t)); } diff --git a/src/AcDream.App/Rendering/Shaders/spv/atmospheric_volumetric.frag.spv b/src/AcDream.App/Rendering/Shaders/spv/atmospheric_volumetric.frag.spv index d782ffe1..5ac257b8 100644 Binary files a/src/AcDream.App/Rendering/Shaders/spv/atmospheric_volumetric.frag.spv and b/src/AcDream.App/Rendering/Shaders/spv/atmospheric_volumetric.frag.spv differ diff --git a/src/AcDream.App/Rendering/Shaders/spv/shaders.manifest.json b/src/AcDream.App/Rendering/Shaders/spv/shaders.manifest.json index 9e1b750d..45208862 100644 --- a/src/AcDream.App/Rendering/Shaders/spv/shaders.manifest.json +++ b/src/AcDream.App/Rendering/Shaders/spv/shaders.manifest.json @@ -92,7 +92,7 @@ }, { "stage": "frag", - "sourceSha256": "8d6177707a95cf0230881bbfb7467c0591632eb67dce3c758596c83cafb14ffc", + "sourceSha256": "0195fa5fdfb3850e090bed6f144981d454edfed526b79efe8dd5300b610b2f0f", "compiled": true } ]