docs(render): Campaign V slice V3 - clip-space and sRGB audit
V3 exists to verify the assumptions the whole Vulkan design rests on, before V6 builds on them. The central claim held. One plan assumption did not, and catching it here is the slice paying for itself. Confirmed: every projection reaching a shader is built by Matrix4x4.CreatePerspectiveFieldOfView, so NDC z is already [0,1] - Vulkan's own convention - and no projection rework is needed. There are no orthographic projections in production at all; the retained UI converts pixel coordinates straight to NDC with a constant z, so V4a has no matrix to convert. Phase U.3's clip planes are derived and compared entirely in clip space with plane.z always zero, making them insensitive to both the depth convention and the viewport Y flip. SkyProjection.WithDepthRange is the only hand-written matrix edit and it re-derives the same D3D-convention mapping rather than a GL-style depth scale. Corrected: the plan specified a B8G8R8A8_SRGB swapchain "matching the GL FramebufferSrgb contract." That contract does not exist. FramebufferSrgb is enabled only inside the throwaway capability probe and disabled immediately, never on the real backbuffer; no texture uses an sRGB internal format; no shader converts gamma. The renderer is UNORM end to end, so the correct swapchain format is B8G8R8A8_UNORM. Shipping _SRGB would have applied an encode to already-display-space values - a global brightening on every frame that nothing before V7 would have caught. Two acceptance items carried forward to V6/V7: the Vulkan encoder must flip scissor rectangles itself, because vkCmdSetScissor is top-left-origin and the negative viewport height does not affect it; and the V7 differential must launch both backends with ACDREAM_MSAA_SAMPLES=0, since MSAA is fixed at window creation and cannot be toggled mid-session. Filed #248 for FrustumCuller's near-plane extraction, which uses the GL [-1,1] Gribb-Hartmann formula against [0,1] matrices. It is provably over-inclusive rather than over-culling, and it is pure CPU math untouched by the backend swap, so it is tracked rather than fixed inside this campaign. No code changed, so the pixel gate is trivially satisfied. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
22b5abba57
commit
ec414d60cd
2 changed files with 108 additions and 6 deletions
|
|
@ -57,6 +57,11 @@ What does NOT go here:
|
|||
multi-session host, shared immutable content, deterministic bot API,
|
||||
1/5/10/30-session and two-hour simulated gates, connected portal/movement
|
||||
parity, resource ceilings, and graceful zero-debt teardown.
|
||||
- **Active rendering campaign:** Campaign V, OpenGL → Vulkan, executes from
|
||||
[`plans/2026-07-27-vulkan-campaign.md`](plans/2026-07-27-vulkan-campaign.md).
|
||||
V0 pinned the RHI contract, V1 landed the GL backend, V2 migrated the shaders
|
||||
and CPU batch data to texture-table indices, and V3 audited clip space and
|
||||
sRGB. `#248` came out of the V3 audit.
|
||||
- **Deferred Linux graphical track:** L0 completed at `66f114b2`; L1's backend
|
||||
selection/capability implementation checkpoint landed at `11501d52`.
|
||||
Native Windows passes and WSLg correctly rejects its missing mandatory
|
||||
|
|
@ -92,6 +97,42 @@ Copy this block when adding a new issue:
|
|||
|
||||
---
|
||||
|
||||
## #248 — FrustumCuller extracts the near plane with the GL-convention formula
|
||||
|
||||
**Status:** OPEN
|
||||
**Severity:** LOW (correctness hygiene; not currently exploitable)
|
||||
**Filed:** 2026-07-27
|
||||
**Component:** rendering / culling
|
||||
|
||||
**Description:** `FrustumCuller.FromViewProjection`
|
||||
(`src/AcDream.App/Rendering/FrustumCuller.cs:34-55`) extracts the near plane as
|
||||
`Normalize(col4 + col3)` — the classic Gribb-Hartmann formula for OpenGL's
|
||||
`[-1,1]` NDC z range. Every acdream projection is built by
|
||||
`Matrix4x4.CreatePerspectiveFieldOfView`, whose NDC z range is `[0,1]`, for which
|
||||
the correct near-plane extraction is `col3` alone.
|
||||
|
||||
**Why it is not currently a bug:** the mismatched formula places the effective
|
||||
near threshold at `-n·f/(2f-n)` — roughly 0.5 m instead of 1.0 m for the retail
|
||||
chase camera — which makes the AABB test strictly *more* permissive near the eye.
|
||||
It can keep something the true frustum would drop, never the reverse, so it
|
||||
produces no missing geometry. The far plane (`col4 - col3`) is identical under
|
||||
both conventions and is unaffected.
|
||||
|
||||
**Provenance:** found by the Campaign V slice V3 clip-space audit. This is the
|
||||
same class of mistake as the `PortalProjection` near-test bug documented at
|
||||
`src/AcDream.App/Rendering/PortalProjection.cs:12-19`, which *was* user-visible
|
||||
(it clipped a doorway the camera stood close to, culling the cell behind it).
|
||||
Filed rather than fixed inline because it is pure CPU math, untouched by the
|
||||
OpenGL → Vulkan migration, and therefore outside Campaign V's scope.
|
||||
|
||||
**Fix:** extract `near = Normalize(col3)`.
|
||||
|
||||
**Acceptance:** culling behaviour unchanged in the offline pixel gate and the
|
||||
connected route; a unit test pinning the extracted near-plane distance to the
|
||||
camera's actual near value.
|
||||
|
||||
---
|
||||
|
||||
## #247 — Loot ordering and local dropped-item projection regressed
|
||||
|
||||
**Status:** DONE — 2026-07-26; connected loot/drop/selection gate passed
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue