render: add a Vulkan-only static sun receiver bias margin for toolchain rounding differences

This commit is contained in:
mchughalex 2026-07-21 07:57:12 +01:00
parent fbcffc1445
commit b6d9442b45
2 changed files with 19 additions and 1 deletions

View file

@ -753,6 +753,16 @@ REXCVAR_DEFINE_DOUBLE(skate3_native_render_scene_shadow_static_bias, 0.08,
"static shadows visibly detach from their casters.") "static shadows visibly detach from their casters.")
.range(0.0, 0.5) .range(0.0, 0.5)
.lifecycle(rex::cvar::Lifecycle::kHotReload); .lifecycle(rex::cvar::Lifecycle::kHotReload);
REXCVAR_DEFINE_DOUBLE(skate3_native_render_scene_shadow_static_bias_vk, 0.05,
"Skate 3",
"Additional static sun-shadow receiver bias in METERS "
"applied on the Vulkan backend only. The caster and "
"receiver depth terms compile through different "
"toolchains per backend; their rounding differences "
"left borderline texels flipping at map rebuilds on "
"Vulkan (triangular ground flicker while moving).")
.range(0.0, 0.5)
.lifecycle(rex::cvar::Lifecycle::kHotReload);
REXCVAR_DEFINE_BOOL(skate3_native_render_scene_char_shadow_exact, true, REXCVAR_DEFINE_BOOL(skate3_native_render_scene_char_shadow_exact, true,
"Skate 3", "Skate 3",
"Characters sample the CSM atlas with the game's own math: " "Characters sample the CSM atlas with the game's own math: "

View file

@ -104,6 +104,7 @@ REXCVAR_DECLARE(double, skate3_native_render_scene_shadow_pcss_max_m);
REXCVAR_DECLARE(double, skate3_native_render_scene_shadow_pcss_min_texel); REXCVAR_DECLARE(double, skate3_native_render_scene_shadow_pcss_min_texel);
REXCVAR_DECLARE(double, skate3_native_render_scene_shadow_pcss_sun_deg); REXCVAR_DECLARE(double, skate3_native_render_scene_shadow_pcss_sun_deg);
REXCVAR_DECLARE(double, skate3_native_render_scene_shadow_static_bias); REXCVAR_DECLARE(double, skate3_native_render_scene_shadow_static_bias);
REXCVAR_DECLARE(double, skate3_native_render_scene_shadow_static_bias_vk);
REXCVAR_DECLARE(double, skate3_native_render_scene_shadow_static_radius); REXCVAR_DECLARE(double, skate3_native_render_scene_shadow_static_radius);
REXCVAR_DECLARE(int32_t, skate3_native_render_scene_shadow_static_size); REXCVAR_DECLARE(int32_t, skate3_native_render_scene_shadow_static_size);
REXCVAR_DECLARE(double, skate3_native_render_scene_shadow_static_strength); REXCVAR_DECLARE(double, skate3_native_render_scene_shadow_static_strength);
@ -7548,8 +7549,15 @@ bool RenderScene(const NativeGuestOutputRenderContext& context, void* /*user_dat
float(REXCVAR_GET( float(REXCVAR_GET(
skate3_native_render_scene_shadow_pcss_min_texel)) / skate3_native_render_scene_shadow_pcss_min_texel)) /
float(std::max(1u, g_r.static_sun_size)); float(std::max(1u, g_r.static_sun_size));
const float bias_m = float( float bias_m = float(
REXCVAR_GET(skate3_native_render_scene_shadow_static_bias)); REXCVAR_GET(skate3_native_render_scene_shadow_static_bias));
if (context.device->backend() == nrhi::Backend::kVulkan) {
// The two backends compile the caster/receiver depth math through
// different shader toolchains; their rounding differences leave
// borderline texels flipping at map rebuilds without this margin.
bias_m += float(
REXCVAR_GET(skate3_native_render_scene_shadow_static_bias_vk));
}
cb[160] = bias_m / g_r.nsm_depth_range; // base receiver bias cb[160] = bias_m / g_r.nsm_depth_range; // base receiver bias
cb[161] = bias_m / g_r.nsm_depth_range; // slope-scaled term cb[161] = bias_m / g_r.nsm_depth_range; // slope-scaled term
cb[162] = 0.0f; // reserved (cascade ratios are shader constants) cb[162] = 0.0f; // reserved (cascade ratios are shader constants)