fix(render): Phase U.4 — EnvCellRenderer.Render uploads its own uViewProjection

Root cause of the indoor cell-shell SEAM flicker ("transparent walls, oscillating
when moving"): EnvCellRenderer.Render never set uViewProjection — it inherited
WbDrawDispatcher's. But the opaque shell pass draws BEFORE the dispatcher's Draw
(GameWindow ~7411 vs ~7418, the only other setter), so opaque shells rendered with
the PREVIOUS frame's matrix — a stale gl_Position against this frame's clip planes,
yielding pose-dependent clipping that's worst while moving. Make Render self-contained:
stash the view-projection in PrepareRenderBatches and upload it in Render (same matrix
the portal clip planes use). Same self-contained-GL-state precedent as the 2026-05-28
cull-state fix in this file.

Visual re-test confirms this removes the wall-seam flicker. A separate residual
("some houses show only background on interior walls; some flicker remains") is a
distinct root cause, under investigation — NOT this matrix bug.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-30 19:00:29 +02:00
parent 354ca746ad
commit d6d4671989

View file

@ -53,6 +53,13 @@ public sealed unsafe class EnvCellRenderer : IDisposable
// API mapping: Bind() -> Use(), SetUniform(s, int) -> SetInt(s, int),
// SetUniform(s, Vector4) -> SetVec4(s, Vector4).
private AcDream.App.Rendering.Shader? _shader;
// Phase U.4 root-cause fix: the view-projection captured in PrepareRenderBatches,
// re-uploaded by Render() so the cell-shell pass is self-contained and does NOT
// inherit WbDrawDispatcher's uViewProjection (which the opaque pass would read one
// frame stale, since it draws BEFORE the dispatcher's upload). Same matrix the
// portal clip planes are computed with (envCellViewProj).
private Matrix4x4 _lastViewProjection = Matrix4x4.Identity;
private bool _initialized;
// List pool — copied from WB ObjectRenderManagerBase.
@ -522,6 +529,9 @@ public sealed unsafe class EnvCellRenderer : IDisposable
int? centerLbY = null,
int? renderRadius = null)
{
// Phase U.4 fix: stash the view-projection so Render() can upload it itself.
_lastViewProjection = viewProjection;
// WB EnvCellRenderManager.cs:249-250:
if (!_initialized || cameraPosition.Z > 4000) return;
@ -815,6 +825,16 @@ public sealed unsafe class EnvCellRenderer : IDisposable
_shader.SetInt("uRenderPass", (int)renderPass);
_shader.SetInt("uFilterByCell", 0);
// Phase U.4 ROOT-CAUSE FIX (cell-shell flicker / "transparent walls when
// moving"): upload uViewProjection HERE rather than inheriting it from
// WbDrawDispatcher. The opaque shell pass runs BEFORE the dispatcher's
// Draw (GameWindow ~7411 vs ~7418, the only other setter), so without
// this the opaque shells used the PREVIOUS frame's matrix — a stale
// gl_Position against this frame's clip planes → pose-dependent clipping,
// worst while moving. Same self-contained-GL-state precedent as the
// 2026-05-28 cull-state cache fix above.
_shader.SetMatrix4("uViewProjection", _lastViewProjection);
var allInstances = new List<InstanceData>();
var drawCalls = new List<(ObjectRenderData renderData, int count, int offset)>();