render: stand the static sun shadows down in venues with no sane world lighting capture

This commit is contained in:
mchughalex 2026-07-22 17:49:59 +01:00
parent c9e501e298
commit 186c7de6f4
4 changed files with 29 additions and 2 deletions

View file

@ -4853,6 +4853,7 @@ void OnDrawDone(uint8_t* base, uint32_t func, uint32_t r4, uint32_t r5, uint32_t
std::memcpy(g_shadow_rows, rows, sizeof(rows)); std::memcpy(g_shadow_rows, rows, sizeof(rows));
g_shadow_have = true; g_shadow_have = true;
g_shadow_frame_done = true; g_shadow_frame_done = true;
g_shadow_rows_frame = g_guest_frame;
} }
} }
// tree / proxyworld frame rows (see FrameScene::family_rows): their PS // tree / proxyworld frame rows (see FrameScene::family_rows): their PS
@ -9815,6 +9816,10 @@ void BuildFrameScene(uint8_t* base, const SubmitRecord* records, size_t count) {
if (g_shadow_have) { if (g_shadow_have) {
std::memcpy(scene.shadow_rows, g_shadow_rows, sizeof(g_shadow_rows)); std::memcpy(scene.shadow_rows, g_shadow_rows, sizeof(g_shadow_rows));
scene.shadow_valid = true; scene.shadow_valid = true;
// Same freshness pattern as the char CSM biases below: outdoor scenes
// re-capture every frame, so anything past a couple of seconds means the
// current venue's environment shader never produces a sane bank.
scene.shadow_fresh = g_guest_frame - g_shadow_rows_frame <= 120;
} }
// Character CSM receive biases (see the header comment): frame-coherent, // Character CSM receive biases (see the header comment): frame-coherent,
// served while fresh enough that a few capture-less frames don't flap the // served while fresh enough that a few capture-less frames don't flap the

View file

@ -321,6 +321,13 @@ struct FrameScene {
// c5.x = depth bias, c8 = shadow color + floor; c6 = sun dir, c7 = camera, // c5.x = depth bias, c8 = shadow color + floor; c6 = sun dir, c7 = camera,
// captured for validation only). // captured for validation only).
bool shadow_valid = false; bool shadow_valid = false;
// True while the rows above were captured within the last ~2 s of guest
// frames. shadow_valid alone latches forever, so interior venues whose
// environment banks never pass the capture sanity gate (park-editor
// warehouses: near-zero sun, exposure ~0.001) serve stale outdoor rows;
// sun-derived features (the native static sun-shadow map) must stand down
// there instead of shading a sunless interior with an outdoor sun.
bool shadow_fresh = false;
// Extended to c0..c11: c10.x = scene exposure (2.5), c11.y = the material // Extended to c0..c11: c10.x = scene exposure (2.5), c11.y = the material
// multiplier (1.0), consumed by the exact world-shading tone chain. // multiplier (1.0), consumed by the exact world-shading tone chain.
float shadow_rows[48] = {}; float shadow_rows[48] = {};

View file

@ -7970,8 +7970,17 @@ bool RenderScene(const NativeGuestOutputRenderContext& context, void* /*user_dat
REXCVAR_GET(skate3_native_render_scene_shadow_pcss_min_texel)); REXCVAR_GET(skate3_native_render_scene_shadow_pcss_min_texel));
} }
// Native static sun-shadow rows (nsm_x/y/z + params, cb[144..163]); // Native static sun-shadow rows (nsm_x/y/z + params, cb[144..163]);
// nsm_p.x = 0 disables the term in every branch. // nsm_p.x = 0 disables the term in every branch (the scene receive, the
if (g_r.static_sun_valid) { // character branch and the volumetric shafts all read this slice).
// Gated on FRESH world rows, not just shadow_valid: interior venues
// (park-editor warehouses) never produce a sane environment bank, so the
// sun direction here would be a stale outdoor capture. The map then
// shades the whole interior as roof-shadowed, and with no game shadow
// pass this frame sh_color is zero, so the exact env families clamp
// min(lm^2, s + sh_color) to pure black - the missing walls/floor.
// The game itself never sun-shadows static world geometry indoors;
// standing the map down restores its exact lm^2 shading.
if (g_r.static_sun_valid && scene.shadow_fresh) {
std::memcpy(cb + 144, g_r.nsm_rows, sizeof(g_r.nsm_rows)); std::memcpy(cb + 144, g_r.nsm_rows, sizeof(g_r.nsm_rows));
cb[156] = std::clamp( cb[156] = std::clamp(
float(REXCVAR_GET(skate3_native_render_scene_shadow_static_strength)), float(REXCVAR_GET(skate3_native_render_scene_shadow_static_strength)),

View file

@ -291,6 +291,12 @@ inline float g_outline_color[4] = {0.21569f, 0.64706f, 1.0f, 1.0f};
inline float g_shadow_rows[48] = {}; inline float g_shadow_rows[48] = {};
inline bool g_shadow_have = false; inline bool g_shadow_have = false;
inline bool g_shadow_frame_done = false; inline bool g_shadow_frame_done = false;
// Guest frame of the last SANE capture. Interior venues (the park-editor
// warehouses) run a different environment shader whose bank never passes the
// sanity gate (near-zero sun, exposure ~0.001), so the rows latch stale
// outdoor state there; consumers that must not act on stale sun state key
// off this freshness stamp (see FrameScene::shadow_fresh).
inline uint64_t g_shadow_rows_frame = 0;
// Frame-global rows of the tree / proxyworld shader families (see // Frame-global rows of the tree / proxyworld shader families (see
// FrameScene::family_rows), captured from their PS banks when a draw with // FrameScene::family_rows), captured from their PS banks when a draw with
// the matching debug path runs. Guest render thread only. // the matching debug path runs. Guest render thread only.