Skip to content

The GI calibration harness stops subtracting a diffuse loss Unreal compiles out: cornell 4 to 10 of 12 rows (PRD-646) - #610

Open
jonit-dev wants to merge 4 commits into
developfrom
goal/prd-646-r13
Open

jonit-dev wants to merge 4 commits into
developfrom
goal/prd-646-r13

Conversation

@jonit-dev

Copy link
Copy Markdown
Contributor

What is this PR about?

The GI calibration harness drew the cornell box darker than Unreal because it subtracted a diffuse energy loss that Unreal does not subtract in this project. This PR removes that loss, so cornell passes 10 of 12 direct-light rows instead of 4 (PRD-646).

Why does it matter?

PRD-646 is the gate for every GI verdict: a calibration scene must match Unreal before a GI change can be judged against it. Round 9 (#609) ported Unreal's 1 - E diffuse loss. Rounds 11 to 13 read Unreal's source and the level and found that Unreal compiles that loss out for this project.

  • USE_ENERGY_CONSERVATION is 1 only with (SUBSTRATE_ENABLED && SUBSTRATE_GBUFFER_FORMAT>0) || LEGACY_MATERIAL_ENERGYCONSERVATION (UE 5.8.3: Engine/Shaders/Private/ShadingEnergyConservation.ush:20-31).
  • The project sets r.Substrate.ProjectGBufferFormat=0 (blendable GBuffer, RenderUtils.cpp:2226-2233), and r.Material.EnergyConservation defaults to 0 (Renderer/Private/ShadingEnergyConservation.cpp:71-74).
  • Measured on cornell: Unreal's specular on/off is 1 + the analytic GGX lobe (Floor 1.0274, Back 1.0138). The harness drew 1 - E + the lobe (0.9993, 1.0038). The gap equals E on each row.

Done

  • ✅ Round 11: read every cornell material from the level with Unreal Python. Specular is 0.5, so F0 0.04 and F90 1, the harness's values. Nothing to port.
  • ✅ Round 12: four harness arms plus an analytic D*Vis*F*NoL. The 5 cm sphere lamp is exactly the point light at roughness 1 (SinAlpha 0). The harness's 1 - E is the gap.
  • ✅ Round 13: scripts/unreal-look/scaffold/render/diffuseEnergy.ts reads the project's switch (ENERGY_CONSERVATION = false), so the scale is 1 / (1 - F) and the diffuse keeps all of its light. F0/F90 come from the material. ?specular=0 now survives the term, and fails closed by name (TN_DIFFUSE_ENERGY_SPECULAR) on the owned engine, which bridges no MeshPhysicalNodeMaterial.
    • Red-green: scripts/__tests__/unreal-look-diffuse-energy.spec.ts, 2 failed before, 10 pass after.
    • pnpm unreal-look:compare --scene gi:cornell,gi:sunlit-room --layer L1, 2 runs per arm, byte-identical within each arm:
Arm cornell sunlit-room worst dP50 worst deltaE
BEFORE 4 of 12 40 of 40 0.059 1.10
AFTER 10 of 12 39 of 40 0.029 0.34
  • Visual judge (fresh Opus, BEFORE/AFTER/UNREAL sheets of all 8 poses): NEUTRAL on every sheet. No artefact, no hue or shadow change. The 1 to 6% linear gain is below what the sheets show, and the compare rows carry it.

No file under packages/ or templates/ changes.

Still open

  • ⚠️ AC-2 stays unticked. Three rows miss: cornell Box_Tall in two poses (2.9% and 3.2% low) and sunlit-room sun15-window Floor (3.1% bright, dP50 0.011 against 0.010; it was 1.4% bright and passing before). No constant was added to win them back.
  • ⚠️ Engine follow-up: a MeshPhysicalNodeMaterial catalogue entry would let ?specular=0 run on the owned engine.

Checks

  • pnpm exec vitest run scripts/__tests__/unreal-look: 47 files, 678 tests pass.
  • pnpm check:docs passes. unreal:status is up to date. Biome is clean on the changed files.
  • pnpm prd:progress: 3/4 phases, 11/12 boxes, 2/3 ACs, prd:75%.

… cornell gains six rows

Unreal compiles energy preservation out for this project, so the harness's twin
stops subtracting a loss Unreal does not draw. `ENERGY_CONSERVATION` is the
project's switch, not the shader's default: `r.Substrate.ProjectGBufferFormat=0`
keeps the blendable Substrate GBuffer
(UE 5.8.3: Shaders/Private/ShadingEnergyConservation.ush:20-31,
RenderCore/Private/RenderUtils.cpp:2226-2233,
Engine/Private/ShaderCompiler/ShaderCompiler.cpp:4093), so
`ComputeEnergyPreservation` returns 1. The albedo scale is now
`preservation / (1 - F)` with F0 and F90 read from the material the engine
draws, so three's pointwise loss cancels and no loss is added; `?diffuseEnergy=three`
still draws the BEFORE arm.

The twin keeps its source's class, so `?specular=0` and `?specular=f0` stay
ablations under the term, and the owned engine, which bridges no
`MeshPhysicalNodeMaterial`, refuses the combination by name
(`TN_DIFFUSE_ENERGY_SPECULAR`) instead of drawing the term's frame for both
knobs.

`cornell` L1 4 of 12 rows to 10 of 12; `sunlit-room` 40 of 40 to 39 of 40
(`sun15-window` Floor, dP50 0.011 against 0.010). No constant was added.
AC-2 and the `cornell` box stay open with the numbers on them (PRD-646).
@jonit-dev jonit-dev added the prd:75% PRD phases in, acceptance open label Nov 4, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

prd:75% PRD phases in, acceptance open

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant