feat(render): Campaign V slice V4d-1 - converge terrain's view and projection
terrain_modern.vert took uView and uProjection as two mat4 uniforms and formed `uProjection * uView` per vertex. GpuPushConstants carries one ViewProjection, so terrain could not reach the pinned push-constant block until these became one uniform. This does that change and nothing else. The transform is unchanged. System.Numerics is row-vector and stores row-major; uploaded untransposed, GLSL reads those bytes as column-major, which is the transpose. So the CPU's camera.View * camera.Projection arrives in the shader as (View*Proj)^T = Proj^T * View^T - exactly the uProjection * uView it replaces. The product it now uses is the same one Draw already computed for the per-cell visibility pass, so no new work is done either. What genuinely changes is where the multiply happens: per-vertex on the GPU before, once on the CPU now. Two float32 matrix products with different association and rounding are not bit-identical, and terrain fills most of the gate's frame, so this was split into its own commit to make that effect attributable rather than buried in the V4d-2 plumbing diff. It is not measurable. The gate run againstc7f5f251reported 32 differing pixels of 563,200 (fraction 5.68e-05, 17.6x under the 0.001 threshold). Because 32 sits just above the plan's recorded 8-29 noise band, the difference was characterised rather than accepted: three captures were taken at this commit and compared every way. cross-commit (c7f5f251vs here): 32, 26, 26 same-commit (here vs here): 28, 27, 8 The distributions are the same distribution. Two cross-commit pairs (26, 26) differ by LESS than two same-commit pairs (27, 28), and a systematic shift cannot produce that - it would floor every cross-commit comparison above every same-commit one. maximumChannelDelta is 48-49 in all six comparisons, including the pure same-commit controls, so the few large-delta pixels are a property of the capture, not of this change. The 32 was the high draw of a noise distribution whose floor today spans roughly 8 to 32; a same-commit control measured 17 atcb0182a0earlier in the session, so the band drifted on its own, with an unchanged binary, by more than this change moved anything. Gates. Release build green with TreatWarningsAsErrors. App tests 3,846 passed / 3 skipped over two consecutive clean runs. A third run failed only UiDatFontTests.InstanceMeasureWidth_ReusesGlyphTableWithoutAllocating, which is the known issue #250 flake on an unchanged tree. Two files changed, CRLF and UTF-8 preserved. No divergence-register row: the transform is identical and no retail-facing behaviour changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
c7f5f251f8
commit
0cb1059765
2 changed files with 14 additions and 5 deletions
|
|
@ -13,8 +13,14 @@ layout(location = 3) in uvec4 aPacked1;
|
|||
layout(location = 4) in uvec4 aPacked2;
|
||||
layout(location = 5) in uvec4 aPacked3;
|
||||
|
||||
uniform mat4 uView;
|
||||
uniform mat4 uProjection;
|
||||
// Campaign V slice V4d-1 (2026-07-27): uView/uProjection converged into the
|
||||
// single uViewProjection that GpuPushConstants carries, so terrain can move
|
||||
// onto the pinned push-constant block at V4d-2. The product is now formed on
|
||||
// the CPU (camera.View * camera.Projection) instead of per vertex here; the
|
||||
// two are the same transform, and System.Numerics' row-vector layout uploaded
|
||||
// untransposed reads in GLSL as the transpose, so (View*Proj)^T == Proj^T *
|
||||
// View^T is exactly the uProjection * uView this replaced.
|
||||
uniform mat4 uViewProjection;
|
||||
|
||||
struct Light {
|
||||
vec4 posAndKind;
|
||||
|
|
@ -165,7 +171,7 @@ void main() {
|
|||
// the un-nudged heightmap via TerrainSurface.SampleZ.
|
||||
// Closes issue #100; supersedes the hiddenTerrainCells cell-collapse hack.
|
||||
vec3 terrainPos = vec3(aPos.xy, aPos.z - 0.01);
|
||||
gl_Position = uProjection * uView * vec4(terrainPos, 1.0);
|
||||
gl_Position = uViewProjection * vec4(terrainPos, 1.0);
|
||||
|
||||
// Phase U.3: terrain clip gate against the single OutsideView region. With
|
||||
// uTerrainClipCount == 0 (U.3 default) the first loop is skipped and the
|
||||
|
|
|
|||
|
|
@ -501,8 +501,11 @@ public sealed unsafe class TerrainModernRenderer : IDisposable
|
|||
// unified camera matrix everywhere, so no separate viewpoint divergence can occur.
|
||||
_shader.Use();
|
||||
UploadTextureTilingOnce();
|
||||
_shader.SetMatrix4("uView", camera.View);
|
||||
_shader.SetMatrix4("uProjection", camera.Projection);
|
||||
// Campaign V slice V4d-1: one uViewProjection, matching the field
|
||||
// GpuPushConstants already carries, instead of the separate uView and
|
||||
// uProjection the shader used to combine per vertex. viewProjection is
|
||||
// the same product the visibility pass above already computed.
|
||||
_shader.SetMatrix4("uViewProjection", viewProjection);
|
||||
|
||||
var (terrainHandle, alphaHandle) = _atlas.GetBindlessHandles();
|
||||
// Campaign V slice V2b: pass each handle's binding=9 table slot
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue