acdream/src/AcDream.App/Rendering/Shaders
Erik 3a117bd91a sky(phase-4): retail-verbatim per-vertex lighting on sky meshes
Re-enables the Phase 2 lighting formula that was reverted in Phase 3b
due to a "blue-green-yellow sweep" across clouds. Root cause of that
earlier regression was NOT the formula — it was that we rolled the
wrong DayGroup (Sunny when retail was Cloudy), producing a sharp warm
sun against a sky that should have been rendered with diffuse
overcast light. After Phase 3g pinned the LCG multiplier to 360
(DaysPerYear) so retail + acdream agree on DayGroup, the same
per-vertex formula now faithfully reproduces retail's visuals.

The formula is verified in decompile agent Q2+Q4+Q6 results,
`docs/research/2026-04-23-sky-material-state.md`:

  D3DRS_LIGHTING = ON         (FUN_0059da60:10648)
  D3DRS_AMBIENT  = 0          (never written after init)
  Material.Emissive = (Luminosity, Luminosity, Luminosity, 1)
  Material.Ambient/Diffuse = defaults (≈1,1,1,1) for non-luminous
  light.Ambient = keyframe AmbColor × AmbBright (via SetDirectionalLight)
  light.Diffuse = keyframe DirColor × DirBright

Fixed-function lighting per vertex:
  lit = Emissive + Ambient × lightAmbient + Diffuse × lightDiffuse × max(N·L, 0)
      = Surface.Luminosity + AmbColor×AmbBright + DirColor×DirBright × max(N·L, 0)

Fragment: texture × lit × SkyObjectReplace.Luminosity.

Expected visual:
- Dome (Surface.Luminosity=1): `lit = 1 + amb + diff·N·L` saturates to 1
  → texture passthrough, baked gradient preserved.
- Clouds (Surface.Luminosity=0): `lit = 0 + amb + diff·N·L`
  → purple haze at night (ambient dominates, sun below horizon);
  → warm tan at dusk (ambient + warm sun on west-facing vertices);
  → pale cool gray at noon (ambient + white sun from above).
- Sun/moon (SurfaceType.Additive, Luminosity=1): same as dome +
  additive blend — stays bright regardless.

The shader uniforms (uAmbientColor, uSunColor, uSunDir, uEmissive)
were already wired in the C# renderer from Phase 2; Phase 3b just
stopped using them in the shader. This commit re-activates them.

No clamp at the vertex — retail's D3D lighting allows Emissive+sum
to exceed 1, relies on the framebuffer per-channel saturation. We
keep the 1.2 ceiling in the frag (for lightning flash overbright
headroom) consistent with that convention.

No fog yet (Q1 confirmed retail leaves fog enabled for sky; will add
in a follow-up if horizon looks too bright).

Build + 733 tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:37:40 +02:00
..
debug_line.frag feat(ui): debug overlay + refined input controls 2026-04-17 18:45:38 +02:00
debug_line.vert feat(ui): debug overlay + refined input controls 2026-04-17 18:45:38 +02:00
mesh.frag fix(render): shader reserved-word + defensive SkyRenderer dat reads 2026-04-19 11:00:34 +02:00
mesh.vert feat(render): Phase G.1/G.2 — SceneLighting UBO + sky renderer + shader integration 2026-04-19 10:39:48 +02:00
mesh_instanced.frag fix(render): shader reserved-word + defensive SkyRenderer dat reads 2026-04-19 11:00:34 +02:00
mesh_instanced.vert feat(render): Phase G.1/G.2 — SceneLighting UBO + sky renderer + shader integration 2026-04-19 10:39:48 +02:00
particle.frag feat(render): Phase G.1 — billboard particle renderer for weather + spells 2026-04-19 10:42:05 +02:00
particle.vert feat(render): Phase G.1 — billboard particle renderer for weather + spells 2026-04-19 10:42:05 +02:00
sky.frag sky(phase-4): retail-verbatim per-vertex lighting on sky meshes 2026-04-24 10:37:40 +02:00
sky.vert sky(phase-4): retail-verbatim per-vertex lighting on sky meshes 2026-04-24 10:37:40 +02:00
terrain.frag feat(render): Phase G.1/G.2 — SceneLighting UBO + sky renderer + shader integration 2026-04-19 10:39:48 +02:00
terrain.vert fix(terrain): align per-cell triangle geometry with ACE's ConstructPolygons convention 2026-04-21 13:20:59 +02:00
ui_text.frag feat(ui): debug overlay + refined input controls 2026-04-17 18:45:38 +02:00
ui_text.vert feat(ui): debug overlay + refined input controls 2026-04-17 18:45:38 +02:00