fix(render): Phase A8 R3.5 v2 — gate depth-clear on cameraReallyInside too
R3.5 v1 only gated the stencil branch on `cameraReallyInside`; the depth-clear-if-inside at line ~7129 stayed on `cameraInsideCell`. During grace frames after exit: cameraInsideCell = true (grace, holds previous cell for 3 frames) cameraReallyInside = false (PointInCell on camera pos returns false) So depth-clear FIRED (writing depth = 1.0 globally) but the OUTDOOR branch ran (single Draw(All) on every entity). With depth cleared, terrain's depth = 1.0 — every entity below terrain (cellar geometry, basement GfxObjs, anything at world Z < terrain Z) won the depth test and rendered THROUGH the ground. User reported: "stand outside or pass outside → flicker where objects are visible through ground and walls of other buildings are missing." v2 fix: unify depth-related gates on `cameraReallyInside`. During grace frames depth-clear is now ALSO skipped; terrain depth survives; the outdoor pass renders normally with proper terrain occlusion. Sky / lighting / particles continue to use `cameraInsideCell` for smooth grace-aware transitions. The two-flag split is now explicit: cameraInsideCell → sky, lighting (smooth, grace-aware) cameraReallyInside → depth-clear, stencil branch (strict, no grace) Closes the persistent transition flicker observed in R4 visual verification after v1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
38d537491f
commit
2bfeafd358
1 changed files with 33 additions and 25 deletions
|
|
@ -7122,17 +7122,42 @@ public sealed class GameWindow : IDisposable
|
|||
_terrainCpuSampleCursor = (_terrainCpuSampleCursor + 1) % _terrainCpuSamples.Length;
|
||||
MaybeFlushTerrainDiag();
|
||||
|
||||
// Conditional depth clear: when camera is inside a building, clear
|
||||
// depth (not color) so interior geometry writes fresh Z values on top
|
||||
// of the terrain color buffer. Exit portals show outdoor terrain color
|
||||
// because we kept the color buffer. Matching ACME GameScene.cs pattern.
|
||||
if (cameraInsideCell)
|
||||
// Phase A8 R3.5 — transition-flicker fix. `cameraInsideCell`
|
||||
// stays true for ~3 grace frames after the camera physically
|
||||
// exits a cell (see CellVisibility._cellSwitchGraceFrames).
|
||||
// The grace mechanism prevents sky/lighting flicker at the
|
||||
// doorway threshold, but the render-frame mechanisms that
|
||||
// touch depth (depth-clear AND the stencil pipeline) MUST be
|
||||
// gated on the stricter PointInCell containment so they don't
|
||||
// fire during grace frames when the camera is actually outside.
|
||||
//
|
||||
// cameraInsideCell — lenient, grace-aware → sky, lighting
|
||||
// cameraReallyInside — PointInCell, no grace → depth-clear,
|
||||
// stencil pipeline branch
|
||||
//
|
||||
// R3.5 v1 only gated the stencil branch on `cameraReallyInside`;
|
||||
// depth-clear stayed on `cameraInsideCell`. Result: during grace
|
||||
// frames the depth-clear ran but the outdoor branch ran (because
|
||||
// !cameraReallyInside), so terrain depth was destroyed AND
|
||||
// everything below terrain (cellars, basement geometry) won the
|
||||
// depth test in the outdoor pass → "objects visible through
|
||||
// ground." R3.5 v2 unifies the depth-related gates on
|
||||
// `cameraReallyInside` so terrain depth is preserved during
|
||||
// grace, eliminating the through-ground artifact.
|
||||
bool cameraReallyInside = visibility?.CameraCell is not null
|
||||
&& CellVisibility.PointInCell(camPos, visibility.CameraCell);
|
||||
|
||||
// Conditional depth clear: when camera is ACTUALLY inside a cell
|
||||
// volume (not just in the grace window), clear depth (not color)
|
||||
// so interior geometry writes fresh Z values on top of the
|
||||
// terrain color buffer. Matches ACME GameScene.cs pattern.
|
||||
if (cameraReallyInside)
|
||||
_gl!.Clear(ClearBufferMask.DepthBufferBit);
|
||||
|
||||
// L-fix1 (2026-04-28): animated-entity id set. Required by both
|
||||
// the cameraInsideCell branch (to route them to LiveDynamic pass)
|
||||
// and the outdoor path (where it preserves visibility across
|
||||
// landblock frustum culling).
|
||||
// the cameraReallyInside branch (to route them to LiveDynamic
|
||||
// pass) and the outdoor path (where it preserves visibility
|
||||
// across landblock frustum culling).
|
||||
HashSet<uint>? animatedIds = null;
|
||||
if (_animatedEntities.Count > 0)
|
||||
{
|
||||
|
|
@ -7141,23 +7166,6 @@ public sealed class GameWindow : IDisposable
|
|||
animatedIds.Add(k);
|
||||
}
|
||||
|
||||
// Phase A8 R3.5 — transition-flicker fix. `cameraInsideCell`
|
||||
// stays true for ~3 grace frames after the camera physically
|
||||
// exits a cell (see CellVisibility._cellSwitchGraceFrames).
|
||||
// The grace mechanism prevents lighting/sky flicker at the
|
||||
// doorway threshold, but if the stencil pipeline runs during
|
||||
// grace frames it marks/punches at the OLD cell's portal
|
||||
// silhouettes — which are now behind/beside the camera — and
|
||||
// the subsequent IndoorPass + stencil-gated outdoor produce
|
||||
// a brief frame of "walls disappear + buildings under ground"
|
||||
// garbage. Gate the stencil branch on the stricter
|
||||
// `PointInCell` containment check so the pipeline only runs
|
||||
// when the camera is ACTUALLY inside its cell volume; sky /
|
||||
// lighting / depth-clear continue to use `cameraInsideCell`
|
||||
// for their smoother grace-aware behavior.
|
||||
bool cameraReallyInside = visibility?.CameraCell is not null
|
||||
&& CellVisibility.PointInCell(camPos, visibility.CameraCell);
|
||||
|
||||
// The `visibility?.CameraCell is not null` repeat is for the
|
||||
// compiler's null-flow analysis: `cameraReallyInside` already
|
||||
// implies it, but flow doesn't propagate through a separate
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue