fix(sky): A7 — correct sun-vector magnitude (ambient + sun were ~32% too bright)
Outdoor lighting was ~32% too bright (washed-out, weak shading). Live cdb on retail (SmartBox::SetWorldAmbientLight + SkyDesc::GetLighting + LScape::sunlight, binary matches refs/acclient.pdb) pinned it: at the SAME game time + DayGroup, acdream's ambient COLOR matched retail exactly (the purple is correct, authored per-time-of-day in the sky dat) but the LEVEL was 0.607 vs retail's 0.459. level = AmbBright + 0.2·|sunVec|, both AmbBright=0.40, so acdream's |sunVec|≈1.06 vs retail's ≈0.30. Retail's LScape::sunlight read live = (0.2238, ~0, 0.00352), magnitude 0.224 = DirBright, y≈0. RetailSunVector had `y = cos(P)` (≈1) — the raw PRE-transform value SkyDesc:: GetLighting writes to arg5 (0x00500ac9), before LScape::set_sky_position's world transform. acdream ported the un-transformed vector, so the y=cos(P)≈1 term inflated |sunVec| to ~1.06. That magnitude feeds BOTH the ambient boost (SkyKeyframe.AmbientColor) AND the sun colour (SkyKeyframe.SunColor = DirColor×|sunVec|), over-brightening the whole scene (terrain, objects, sky) ~30% and also pointing the sun the wrong way. Fix: RetailSunVector = DirBright × (cos(P)·sin(H), cos(P)·cos(H), sin(P)) — the world-space spherical form LScape::sunlight actually holds; |sunVec| == DirBright for all H/P. After: acdream ambient (0.353,0.176,0.449) vs retail (0.360,0.180, 0.459) — within ~2%, user-confirmed "better outside". Sun direction also corrected (was pointing ~North from the bad y term). Tests updated to the cdb-verified values (the prior tests pinned the inflated magnitude). 18/18 sky tests green. reference-retail-ambient-values memory updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4345e77d62
commit
57c11358b6
3 changed files with 73 additions and 56 deletions
|
|
@ -100,22 +100,23 @@ public sealed class SkyDescLoaderTests
|
|||
{
|
||||
// The loader stores DirColor and DirBright RAW. The SunColor property
|
||||
// composes them via |sunVec| per retail's UpdateLightsInternal at
|
||||
// 0x59b57c (decomp 424118) — the diffuse magnitude is sqrt(x²+y²+z²)
|
||||
// where the sun vector is built from heading/pitch/brightness with
|
||||
// Y unscaled by brightness (decomp 261352).
|
||||
// 0x59b57c (decomp 424118) — diffuse = DirColor × |LScape::sunlight|.
|
||||
// cdb-verified (reference-retail-ambient-values): |LScape::sunlight| ==
|
||||
// DirBright for every keyframe (world-space spherical vector, magnitude
|
||||
// DirBright·sqrt(cos²P+sin²P) = DirBright).
|
||||
//
|
||||
// For this region: H=180°, P=70°, B=1.5
|
||||
// sunVec = (sin(180)*1.5*cos(70), cos(70), 1.5*sin(70))
|
||||
// = (0, 0.342, 1.410)
|
||||
// |sunVec| = sqrt(0 + 0.117 + 1.988) = 1.4509
|
||||
// sunVec = 1.5 × (cos(70)·sin(180), cos(70)·cos(180), sin(70))
|
||||
// = (0, -0.513, 1.410)
|
||||
// |sunVec| = sqrt(0 + 0.263 + 1.988) = 1.500 (= DirBright)
|
||||
// DirColor.X = 200/255 = 0.7843
|
||||
// SunColor.X = 0.7843 × 1.4509 = 1.138
|
||||
// SunColor.X = 0.7843 × 1.500 = 1.1765
|
||||
var region = MakeRegion(dirBright: 1.5f, rBgrOrder: 200);
|
||||
var loaded = SkyDescLoader.LoadFromRegion(region);
|
||||
Assert.NotNull(loaded);
|
||||
|
||||
var kf = loaded!.DayGroups[0].SkyTimes[0].Keyframe;
|
||||
Assert.InRange(kf.SunColor.X, 1.13f, 1.15f);
|
||||
Assert.InRange(kf.SunColor.X, 1.17f, 1.18f);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -66,24 +66,33 @@ public sealed class SkyStateTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void RetailSunVector_AtHorizonNorth_MagnitudeIsOne()
|
||||
public void RetailSunVector_MagnitudeAlwaysEqualsDirBright()
|
||||
{
|
||||
// Sun on horizon to the north (H=0°, P=0°): cos(P)=1, sin(P)=0.
|
||||
// sunVec = (sin(0)×B×1, 1, B×0) = (0, 1, 0)
|
||||
// |sunVec| = 1 regardless of B (because Y is unscaled by B)
|
||||
var kf = new SkyKeyframe(
|
||||
Begin: 0f,
|
||||
SunHeadingDeg: 0f,
|
||||
SunPitchDeg: 0f,
|
||||
DirColor: Vector3.One,
|
||||
DirBright: 2.0f, // anything
|
||||
AmbColor: Vector3.One,
|
||||
AmbBright: 1f,
|
||||
FogColor: Vector3.One,
|
||||
FogDensity: 0f);
|
||||
// cdb-verified (2026-06-18, reference-retail-ambient-values): retail's
|
||||
// world-space LScape::sunlight = DirBright × (cosP·sinH, cosP·cosH, sinP),
|
||||
// whose magnitude is DirBright·sqrt(cos²P·(sin²H+cos²H)+sin²P) = DirBright
|
||||
// for ALL headings/pitches. (The prior y=cos(P) port gave |sunVec|≈1 at the
|
||||
// horizon — that was the ~30% over-bright bug.)
|
||||
// Horizon north (H=0°, P=0°): (0, B, 0), |.| = B.
|
||||
var horizon = new SkyKeyframe(
|
||||
Begin: 0f, SunHeadingDeg: 0f, SunPitchDeg: 0f,
|
||||
DirColor: Vector3.One, DirBright: 2.0f,
|
||||
AmbColor: Vector3.One, AmbBright: 1f,
|
||||
FogColor: Vector3.One, FogDensity: 0f);
|
||||
Assert.InRange(SkyStateProvider.RetailSunVector(horizon).Length(), 1.99f, 2.01f);
|
||||
|
||||
var v = SkyStateProvider.RetailSunVector(kf);
|
||||
Assert.InRange(v.Length(), 0.99f, 1.01f);
|
||||
// Reproduce the live cdb capture: dawn keyframe H=90°, P=0.9°, DirBright=0.224
|
||||
// → LScape::sunlight = (0.2238, ~0, 0.00352), magnitude 0.224 = DirBright.
|
||||
var dawn = new SkyKeyframe(
|
||||
Begin: 0f, SunHeadingDeg: 90f, SunPitchDeg: 0.9f,
|
||||
DirColor: Vector3.One, DirBright: 0.224f,
|
||||
AmbColor: Vector3.One, AmbBright: 0.40f,
|
||||
FogColor: Vector3.One, FogDensity: 0f);
|
||||
var v = SkyStateProvider.RetailSunVector(dawn);
|
||||
Assert.InRange(v.X, 0.223f, 0.225f); // DirBright·cosP·sin(90°) ≈ 0.224
|
||||
Assert.InRange(v.Y, -0.001f, 0.001f); // DirBright·cosP·cos(90°) ≈ 0 (was the bug: ≈1)
|
||||
Assert.InRange(v.Z, 0.003f, 0.004f); // DirBright·sin(0.9°) ≈ 0.0035
|
||||
Assert.InRange(v.Length(), 0.223f, 0.225f); // = DirBright
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue