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
|
|
@ -55,20 +55,25 @@ public sealed record GfxObjSubMesh(
|
|||
public bool NeedsUvRepeat { get; init; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// <c>Surface.Translucency</c> float (0..1 — distinct from the
|
||||
/// <c>Surface.Translucency</c> float (0..1) treated as an OPACITY
|
||||
/// multiplier on fragment alpha. 1.0 = fully opaque (default for
|
||||
/// non-Translucent surfaces). Distinct from the
|
||||
/// <see cref="TranslucencyKind"/> classifier above, which buckets the
|
||||
/// flag bits). Retail's <c>D3DPolyRender::SetSurface</c> at
|
||||
/// <c>0x59c767</c> reads this when the <c>Translucent</c> (0x10) bit
|
||||
/// is set on the surface and feeds it into the per-vertex alpha
|
||||
/// (<c>curr_alpha</c>); the rasterizer then multiplies fragment alpha
|
||||
/// by <c>(1 - translucency)</c> so the resulting opacity is
|
||||
/// <c>1 - x</c>. ACViewer (<c>TextureCache.cs:142</c>) and WorldBuilder
|
||||
/// (<c>ObjectMeshManager.cs:1115</c>) both use the same convention.
|
||||
/// For the rain Surface 0x080000C5, <c>Translucency = 0.5</c> ⇒
|
||||
/// opacity = 0.5; with the <c>(SrcAlpha, One)</c> additive blend the
|
||||
/// rain streaks contribute at half intensity instead of full.
|
||||
/// Defaults to 0.0 (fully opaque) so non-translucent surfaces render
|
||||
/// through the normal lighting path without change.
|
||||
/// flag bits. Retail's <c>D3DPolyRender::SetSurface</c> at
|
||||
/// <c>0x59c7a6</c> (decomp lines 425255-425260) reads
|
||||
/// <c>Surface.Translucency</c> when the <c>Translucent</c> (0x10) bit
|
||||
/// is set, computes <c>curr_alpha = _ftol2(translucency × 255)</c>,
|
||||
/// and writes that as vertex alpha — i.e. the dat's Translucency float
|
||||
/// is used DIRECTLY as opacity, NOT inverted. ACViewer
|
||||
/// (<c>TextureCache.cs:142</c>) and WorldBuilder
|
||||
/// (<c>ObjectMeshManager.cs:1115</c>) both use <c>1 - translucency</c>
|
||||
/// and are wrong by the same misread.
|
||||
/// For the rain Surface 0x080000C5 (translucency=0.5): opacity = 0.5;
|
||||
/// with the <c>(SrcAlpha, One)</c> additive blend the rain streaks
|
||||
/// contribute at half intensity. For cloud surface 0x08000023
|
||||
/// (translucency=0.25): opacity = 0.25 (matches retail's dim clouds).
|
||||
/// Defaults to 1.0 (fully opaque) so non-Translucent surfaces render
|
||||
/// at full opacity without change.
|
||||
/// </summary>
|
||||
public float SurfTranslucency { get; init; } = 0f;
|
||||
public float SurfTranslucency { get; init; } = 1f;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue