Files
behind_glass/docs/qa-surface-reflection/implementation-report.md
T
2026-10-06 12:16:46 +09:00

8.4 KiB

Surface reflection implementation report

Status and scope

Implemented Task 1 from .superpowers/sdd/surface-reflection/task-1-brief.md. The change is limited to the final non-REGION_ATLAS reflection composition in src/shaders/mosaic.frag plus one source/math regression script. Existing uniforms were sufficient, so src/main.ts was not changed.

The exact pre-edit shader is preserved at docs/qa-surface-reflection/baseline-mosaic.frag.

Baseline SHA-256:

CEE5F75C7A62A9146CBE451C661C2E0066EE57EB8963A7B0F56C281873F37E35

Implementation

Changed files:

  • src/shaders/mosaic.frag
  • scripts/test-surface-reflection.mjs
  • docs/qa-surface-reflection/baseline-mosaic.frag (pre-edit snapshot)
  • docs/qa-surface-reflection/implementation-report.md

The existing rectangular reflection remains intact and is now named specGloss. A separate broad reflection is added after color-plane simplification and before #include <colorspace_fragment>.

Final coefficients and rationale:

  • Surface reflection edge weight: mix(0.35, 1.0, edgeWeight). This keeps the existing refraction edgeWeight and strength path unchanged while reducing reflection at pane edges without removing it entirely.
  • Existing rectangular reflection strength: .12, unchanged.
  • Broad reflection strength: .035, intentionally low relative to the rectangular lobe.
  • Broad spatial footprint: smoothstep(vec2(.15), vec2(1.75), windowCoord), wider and softer than the existing .45 to 1.15 rectangular mask.
  • View/surface response: pow(max(dot(nCurved, v), 0.), 2.), using the same signed curved normal that drives refraction and the existing reflection.
  • Both reflection amplitudes use the same clamp(uGlossStrength, 0., 1.) gate, so gloss 0 makes both contributions exactly zero.

No environment texture, render pass, render target, dependency, palette input, history input, UI, camera, placement, framing, or other glass-mode change was added.

TDD evidence

Focused regression: scripts/test-surface-reflection.mjs. The assertions are source/math-only and do not claim to execute GLSL or prove browser visuals.

RED

Command:

node scripts/test-surface-reflection.mjs

Exact result against the pre-edit shader:

exit_code=1
node:internal/modules/run_main:107
    triggerUncaughtException(
    ^

AssertionError [ERR_ASSERTION]: both surface reflection terms must share a clamped gloss gate
    at file:///D:/project/behind_glass/scripts/test-surface-reflection.mjs:11:8 {
  generatedMessage: false,
  actual: '#ifndef REGION_ATLAS\n' +
    '  color.a=1.;\n' +
    '  vec3 v=normalize(uContentCameraPosition-gh),reflected=reflect(-v,nCurved); float lightT=(uLightPosition.z-gh.z)/safeZ(reflected.z); vec2 lightHit=gh.xy+reflected.xy*lightT; vec2 halfSize=vec2(.32,.42)*min(uPaneSize.x,uPaneSize.y); vec2 windowCoord=abs(lightHit-uLightPosition.xy)/halfSize; vec2 windowMask=1.-smoothstep(vec2(.45),vec2(1.15),windowCoord); float spec=windowMask.x*windowMask.y*step(.0001,reflected.z)*step(0.,lightT); float gloss=spec*clamp(uGlossStrength,0.,1.)*.12*edgeWeight*clamp(uStrength,0.,1.); color.rgb=mix(color.rgb,vec3(1.),gloss); gl_FragColor=color;\n' +
    '  #include <colorspace_fragment>\n',
  expected: /float glossStrength=clamp\(uGlossStrength,0\.,1\.\);/,
  operator: 'match'
}

This failed for the intended reason: the pre-edit shader had one reflection term, no shared gloss gate, no broad reflection, and no separate surface edge weight.

GREEN

Command:

node scripts/test-surface-reflection.mjs

Exact result after the minimal shader edit:

exit_code=0
surface reflection source/math regression passed

The final regression checks zero-gloss gating, .35 to 1.0 surface-weight bounds, preservation of the refraction edge weight, normal/view and light-hit spatial dependence, non-atlas placement, and ordering before color-space conversion.

Automated verification

All six existing scripts from docs/qa-window-immersion/verification.md were run with node --experimental-strip-types after implementation.

=== node --experimental-strip-types scripts/test-camera-history.mjs ===
Camera/history checks passed: default/max clamp, model reset keys, ping-pong and disposal, premultiplied color, cadence 0.999955/0.999955/0.999955, 70ms settling.
=== node --experimental-strip-types scripts/test-region-axis-split.mjs ===
Region axis-split checks passed: SSE axis/cut/pair scoring, vertical/horizontal cuts 2/6, observed side-label pairs, checkerboard model, single-label fallback, metadata round-trip, confidence blending, dark eye/nose protection, and edge-independent color strength.
=== node --experimental-strip-types scripts/test-mosaic-depth-distance.mjs ===
Mosaic depth-region projection checks passed: 21 perspective reconstructions, 12 original-depth UV mappings across camera/zoom-pan/depthScale, near-depth gate, distance-independent color application, and invalid-surface guards.
=== node --experimental-strip-types scripts/test-mosaic-continuity.mjs ===
Mosaic projected-UV continuity passed: 493760 Jacobians, 87200 seam convergence checks, center/edge anchors, and view-dependent within-cell displacement.
=== node --experimental-strip-types scripts/test-region-atlas-dark-sampling.mjs ===
Region atlas dark sampling source/math-only regression passed: phase-offset narrow feature, far/invalid/uniform invariants, and four-probe bounded scoring contract.
=== node --experimental-strip-types scripts/test-window-reveal-coverage.mjs ===
Window reveal coverage passed: 146 previous-frame misses sealed across 2 aspects, 3 depth scales, 17 view offsets (including neutral) and 49 aperture samples; geometry reuse/disposal verified.

Build command:

npm.cmd run build

Result: exit code 0; TypeScript and Vite production build succeeded. Vite reported the existing large-chunk advisory for the main and AI-depth assets.

Visual comparison and limitations

No browser or GPU visual capture was performed in this implementation task. The preserved shader baseline is available at docs/qa-surface-reflection/baseline-mosaic.frag; use it for the same-viewport dog1 comparison with the brief's fixed values, including gloss 0, 0.30, and 0.40, curvature -1/0/+1, camera 0.90, portrait, and indoor checks.

The automated regression does not prove grass highlight appearance, adjacent cell coherence, edge leakage, eye/nose readability, mobile behavior, or GPU shader compilation. Those remain visual/browser verification items. No claim of GPU, mobile, or visual success is made here.

Round 1/5 regression hardening

Addressed both Astra findings without changing production scope. The focused script now extracts individual GLSL statements instead of using unrestricted cross-statement matching, compares edgeWeight, strength, and sceneHit against the preserved pre-edit shader, inspects the effective atlas branch, and verifies simplification before final composition and composition before color-space conversion. It evaluates the extracted specGloss, broadReflection, and final mix expressions for gloss-zero output plus representative spatial/view variation. It also applies an in-memory constant 50% final-mix mutation and requires the covering assertions to reject it.

Exact covering command:

node scripts/test-surface-reflection.mjs

Exact output:

exit_code=0
surface reflection source/math regression passed; constant-output mutation rejected

Only the focused regression was rerun because this round changed no production file; the six existing checks and build remain covered by the prior verification record above.

Round 2/5 atlas exclusion hardening

Addressed the remaining atlas-exclusion finding without changing production code. scripts/test-surface-reflection.mjs now walks all REGION_ATLAS preprocessor branches, retains the complete effective source for the macro defined, and compares that source with the preserved baseline. It also inserts an in-memory gloss-dependent white reflection immediately after the earlier atlas atlasCenterColor sample and requires the covering assertions to reject that mutation. This remains a source/math regression and is not GPU visual verification.

Exact covering command:

node scripts/test-surface-reflection.mjs

Exact output:

exit_code=0
surface reflection source/math regression passed; constant-output and atlas-reflection mutations rejected

No full suite or build was rerun because the production shader remained unchanged.