mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-07-13 05:49:21 -04:00
Smooth fadeout for dynamic shadows (#2203)
* Implement smooth fadeout of dynamic shadows * Add edge fade out configuration for dynamic shadows
This commit is contained in:
@@ -21,6 +21,7 @@ struct Uniforms {
|
||||
bias: f32, // shadow-map depth bias (reversed-depth units)
|
||||
size: vec2f, // shadow map size in texels
|
||||
inv_size: vec2f,
|
||||
edge_fade_width: f32,
|
||||
strength: f32, // final darkening amount, horizon fade baked in
|
||||
pcf_taps: f32, // 0 = single tap, 1 = 3x3, 2 = 5x5
|
||||
contact_enabled: f32,
|
||||
@@ -28,7 +29,6 @@ struct Uniforms {
|
||||
contact_length: f32, // view-space march distance
|
||||
debug_mode: u32, // 0 = composite; nonzero modes are diagnostic views
|
||||
_pad0: f32,
|
||||
_pad1: f32,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var scene_depth: texture_2d<f32>;
|
||||
@@ -92,6 +92,16 @@ fn sample_shadow_pcf(light_uv: vec2f, receiver: f32) -> f32 {
|
||||
return sum / count;
|
||||
}
|
||||
|
||||
// Softly fades shadows out over a small band near the shadow-map edge so receivers do not
|
||||
// disappear abruptly when they leave the light's coverage area.
|
||||
fn shadow_edge_fade(light_uv: vec2f) -> f32 {
|
||||
let edge_texels = uniforms.edge_fade_width;
|
||||
let edge_uv = edge_texels * max(uniforms.inv_size.x, uniforms.inv_size.y);
|
||||
let distance_to_edge = min(min(light_uv.x, 1.0 - light_uv.x), min(light_uv.y, 1.0 - light_uv.y));
|
||||
// Avoid division by zero when the fade width is zero (no fade).
|
||||
return select(1.0, saturate(distance_to_edge / edge_uv), edge_uv > 0.0);
|
||||
}
|
||||
|
||||
fn scene_depth_at(uv: vec2f) -> f32 {
|
||||
let size = vec2<i32>(textureDimensions(scene_depth));
|
||||
let texel = clamp(vec2<i32>(uv * vec2f(size)), vec2<i32>(0i), size - 1i);
|
||||
@@ -240,6 +250,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
||||
var occlusion = 0.0;
|
||||
if in_shadow_bounds {
|
||||
occlusion = sample_shadow_pcf(light_uv, receiver);
|
||||
occlusion *= shadow_edge_fade(light_uv);
|
||||
}
|
||||
|
||||
if uniforms.debug_mode == 3u {
|
||||
|
||||
@@ -57,6 +57,7 @@ ConfigVarHandle g_cvarStrength = 0;
|
||||
ConfigVarHandle g_cvarPcf = 0;
|
||||
ConfigVarHandle g_cvarBias = 0;
|
||||
ConfigVarHandle g_cvarBoxRadius = 0;
|
||||
ConfigVarHandle g_cvarEdgeFadeWidth = 0;
|
||||
ConfigVarHandle g_cvarContactShadows = 0;
|
||||
ConfigVarHandle g_cvarDebugView = 0;
|
||||
|
||||
@@ -107,6 +108,7 @@ struct ShadowUniforms {
|
||||
float bias;
|
||||
float size[2];
|
||||
float inv_size[2];
|
||||
float edge_fade_width;
|
||||
float strength;
|
||||
float pcf_taps;
|
||||
float contact_enabled;
|
||||
@@ -114,7 +116,6 @@ struct ShadowUniforms {
|
||||
float contact_length;
|
||||
uint32_t debug_mode;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
};
|
||||
static_assert(sizeof(ShadowUniforms) % 16 == 0);
|
||||
|
||||
@@ -792,6 +793,8 @@ void on_frame_before_hud(ModContext*, const GfxStageContext*, void*) {
|
||||
uniforms.size[1] = static_cast<float>(mapPass.mapSize);
|
||||
uniforms.inv_size[0] = 1.0f / uniforms.size[0];
|
||||
uniforms.inv_size[1] = 1.0f / uniforms.size[1];
|
||||
uniforms.edge_fade_width =
|
||||
static_cast<float>(std::clamp<int64_t>(get_int_option(g_cvarEdgeFadeWidth, 32), 0, 128));
|
||||
uniforms.strength =
|
||||
mapPass.fade *
|
||||
static_cast<float>(std::clamp<int64_t>(get_int_option(g_cvarStrength, 45), 0, 100)) /
|
||||
@@ -867,6 +870,8 @@ ModResult build_controls_tab(
|
||||
"dynamic shadows. This can be expensive.");
|
||||
add_number(left, "Coverage", g_cvarBoxRadius, 1000, 20000, 500, nullptr,
|
||||
"Radius of the shadowed area around the camera, in world units. Smaller is sharper.");
|
||||
add_number(left, "Fade Out", g_cvarEdgeFadeWidth, 0, 128, 32, " texels",
|
||||
"Fade out shadows gradually near the edge of the coverage area.");
|
||||
|
||||
svc_ui->pane_add_section(mod_ctx, left, "Appearance");
|
||||
add_number(left, "Strength", g_cvarStrength, 0, 100, 5, "%", "How dark shadowed areas become.");
|
||||
@@ -992,6 +997,10 @@ MOD_EXPORT ModResult mod_initialize(ModError* error) {
|
||||
if (result != MOD_OK) {
|
||||
return result;
|
||||
}
|
||||
result = register_int_option("edgeFadeWidth", 32, g_cvarEdgeFadeWidth, error);
|
||||
if (result != MOD_OK) {
|
||||
return result;
|
||||
}
|
||||
result = register_bool_option("contactShadows", true, g_cvarContactShadows, error);
|
||||
if (result != MOD_OK) {
|
||||
return result;
|
||||
@@ -1087,7 +1096,8 @@ MOD_EXPORT ModResult mod_shutdown(ModError*) {
|
||||
}
|
||||
g_cvarEnabled = g_cvarMapSize = g_cvarNoFrustumClipping = 0;
|
||||
g_cvarStrength = 0;
|
||||
g_cvarPcf = g_cvarBias = g_cvarBoxRadius = g_cvarContactShadows = g_cvarDebugView = 0;
|
||||
g_cvarPcf = g_cvarBias = g_cvarBoxRadius = g_cvarEdgeFadeWidth = g_cvarContactShadows =
|
||||
g_cvarDebugView = 0;
|
||||
g_drawType = g_sceneBeginHook = g_sceneAfterTerrainHook = g_frameBeforeHudHook = 0;
|
||||
g_controlsWindow = 0;
|
||||
g_mapPass = {};
|
||||
|
||||
Reference in New Issue
Block a user