fix(sky): translucency-as-opacity + sky fog floor + additive fog-skip
Three retail-faithful sky/weather composite fixes (one cohesive commit because they touch the same per-Surface flag plumbing path). 1. Surface.Translucency is OPACITY, not (1 - opacity). Retail D3DPolyRender::SetSurface at 0x59c7a6 (decomp 425255-425260) computes `curr_alpha = _ftol2(translucency × 255)` and writes that directly as vertex.color.alpha. ACViewer (TextureCache.cs:142) and WorldBuilder (ObjectMeshManager.cs:1115) both use `1 - translucency` and are wrong by the same misread. Cloud surface 0x08000023 has Translucency=0.25; under the old (1-x) formula opacity was 0.75, making clouds 3× too bright vs retail. Flipped to use translucency directly. Gated on the Translucent flag (0x10) so non-Translucent surfaces (which carry Translucency=0 in the dat) keep opacity 1.0 instead of going invisible. 2. Sky fog re-enabled with a "fog floor" mitigation. Disabled 2026-04-24 because Dereth sky meshes are authored at radii 1050-1820m while storm-keyframe FogEnd is ~400m, which would saturate the entire dome to flat fogColor and destroy stars/moon/dome texture. Retail visibly DOES fog its sky, mechanism still un-pinned. Workaround: clamp `vFogFactor` to a minimum of SKY_FOG_FLOOR=0.2 so the dome shows AT LEAST 20% raw texture even at extreme distances. Tuned via dual- client visual comparison; preserves stars/moon while letting the horizon haze visibly in low-FogEnd keyframes. 3. Additive sky surfaces skip fog entirely. Retail D3DPolyRender::SetSurface at 0x59c882 calls SetFFFogAlphaDisabled(1) when the Additive flag (0x10000) is set — sun, moon, stars, additive cloud sheets render unfogged. Without this gate the sun dimmed to fog color at horizon dusk/dawn instead of staying bright. Plumbed via new `uApplyFog` shader uniform driven by the existing SubMeshGpu.IsAdditive boolean (already set from TranslucencyKind.Additive at upload time). User visually verified all three vs retail screenshots in Holtburg. Tests: 1223 pass.
This commit is contained in:
parent
63b50c5291
commit
97fc1b51d8
4 changed files with 121 additions and 55 deletions
|
|
@ -200,7 +200,21 @@ public static class GfxObjMesh
|
|||
// docs/research/2026-04-23-sky-retail-verbatim.md §6).
|
||||
var translucency = TranslucencyKind.Opaque;
|
||||
var luminosity = 0f;
|
||||
var surfTranslucency = 0f;
|
||||
// SurfTranslucency = the OPACITY multiplier the shader applies
|
||||
// to fragment alpha. 1.0 = fully opaque (default, non-Translucent
|
||||
// surfaces). For Translucent-flag surfaces, retail's
|
||||
// D3DPolyRender::SetSurface at 0x59c7a6 (decomp lines 425255-
|
||||
// 425260) computes curr_alpha = _ftol2(translucency × 255) and
|
||||
// feeds that as vertex.color.alpha — so the dat's Translucency
|
||||
// float is the OPACITY directly (NOT inverted). For rain
|
||||
// (translucency=0.5) opacity is 0.5; for cloud surface
|
||||
// 0x08000023 (translucency=0.25) opacity is 0.25 — that's why
|
||||
// retail's clouds are dim and acdream's were 3× too bright
|
||||
// before this fix (we used 1-translucency, inverting the
|
||||
// semantic). ACViewer's TextureCache.cs:142 and WorldBuilder's
|
||||
// ObjectMeshManager.cs:1115 also use 1-translucency and are
|
||||
// both wrong by the same misread.
|
||||
var surfTranslucency = 1.0f;
|
||||
if (dats is not null)
|
||||
{
|
||||
var surface = dats.Get<Surface>(surfaceId);
|
||||
|
|
@ -208,15 +222,13 @@ public static class GfxObjMesh
|
|||
{
|
||||
translucency = TranslucencyKindExtensions.FromSurfaceType(surface.Type);
|
||||
luminosity = surface.Luminosity;
|
||||
// Retail D3DPolyRender::SetSurface at 0x59c767: when the
|
||||
// Translucent (0x10) flag is set, the surface's
|
||||
// Translucency float drives per-vertex alpha. Both
|
||||
// ACViewer and WorldBuilder apply opacity = (1 - x).
|
||||
// For the rain Surface 0x080000C5 this is 0.5. Carrying
|
||||
// the float verbatim and converting to opacity in the
|
||||
// shader keeps non-Translucent surfaces (Translucency=0)
|
||||
// identical to the previous behavior.
|
||||
surfTranslucency = surface.Translucency;
|
||||
// Apply the dat's Translucency value as opacity ONLY
|
||||
// when the Translucent flag (0x10) is set on the
|
||||
// Surface. Without this gate, surfaces with
|
||||
// Translucency=0 (non-Translucent default) would
|
||||
// render fully transparent.
|
||||
if (((uint)surface.Type & (uint)DatReaderWriter.Enums.SurfaceType.Translucent) != 0)
|
||||
surfTranslucency = surface.Translucency;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue