diff --git a/doc/classes/DirectionalLight.xml b/doc/classes/DirectionalLight.xml index 8496d3f19e9b..6782c90efe91 100644 --- a/doc/classes/DirectionalLight.xml +++ b/doc/classes/DirectionalLight.xml @@ -21,6 +21,9 @@ Optimizes shadow rendering for detail versus movement. See [enum ShadowDepthRange]. + + Proportion of [member directional_shadow_max_distance] at which point the shadow starts to fade. At [member directional_shadow_max_distance], the shadow will disappear. The default value is a balance between smooth fading and distant shadow visibility. If the camera moves fast and the [member directional_shadow_max_distance] is low, consider lowering [member directional_shadow_fade_start] below [code]0.8[/code] to make shadow transitions less noticeable. On the other hand, if you tuned [member directional_shadow_max_distance] to cover the entire scene, you can set [member directional_shadow_fade_start] to [code]1.0[/code] to prevent the shadow from fading in the distance (it will suddenly cut off instead). + The maximum distance for shadow splits. Increasing this value will make directional shadows visible from further away, at the cost of lower overall shadow detail and performance (since more objects need to be included in the directional shadow rendering). diff --git a/doc/classes/Light.xml b/doc/classes/Light.xml index 7cfa11ad4090..6051311fa279 100644 --- a/doc/classes/Light.xml +++ b/doc/classes/Light.xml @@ -122,7 +122,10 @@ Constant for accessing [member DirectionalLight.directional_shadow_bias_split_scale]. - + + Constant for accessing [member DirectionalLight.directional_shadow_fade_start]. + + Represents the size of the [enum Param] enum. diff --git a/doc/classes/VisualServer.xml b/doc/classes/VisualServer.xml index bcc4fe0f44a8..e7091326b712 100644 --- a/doc/classes/VisualServer.xml +++ b/doc/classes/VisualServer.xml @@ -3648,7 +3648,10 @@ Increases bias on further splits to fix self-shadowing that only occurs far away from the camera. - + + Proportion of [constant LIGHT_PARAM_SHADOW_MAX_DISTANCE] at which point the shadow starts to fade. At [constant LIGHT_PARAM_SHADOW_MAX_DISTANCE], the shadow will disappear. The default value is a balance between smooth fading and distant shadow visibility. If the camera moves fast and the [constant LIGHT_PARAM_SHADOW_MAX_DISTANCE] is low, consider lowering [constant LIGHT_PARAM_SHADOW_FADE_START] below [code]0.8[/code] to make shadow transitions less noticeable. On the other hand, if you tuned [constant LIGHT_PARAM_SHADOW_MAX_DISTANCE] to cover the entire scene, you can set [constant LIGHT_PARAM_SHADOW_FADE_START] to [code]1.0[/code] to prevent the shadow from fading in the distance (it will suddenly cut off instead). + + Represents the size of the [enum LightParam] enum. diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp index 08a452428531..cfb9e593cab3 100644 --- a/drivers/gles2/rasterizer_scene_gles2.cpp +++ b/drivers/gles2/rasterizer_scene_gles2.cpp @@ -2087,6 +2087,12 @@ void RasterizerSceneGLES2::_setup_light(LightInstance *light, ShadowAtlas *shado // state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_CLAMP, light_clamp); state.scene_shader.set_uniform(SceneShaderGLES2::SHADOW_PIXEL_SIZE, Size2(1.0 / directional_shadow.size, 1.0 / directional_shadow.size)); state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPLIT_OFFSETS, split_offsets); + + const float fade_start = light_ptr->param[VS::LIGHT_PARAM_SHADOW_FADE_START]; + // Using 1.0 would break `smoothstep()` in the shader. + state.scene_shader.set_uniform(SceneShaderGLES2::FADE_FROM, -split_offsets[shadow_count - 1] * MIN(fade_start, 0.999)); + state.scene_shader.set_uniform(SceneShaderGLES2::FADE_TO, -split_offsets[shadow_count - 1]); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX, matrices[0]); state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX2, matrices[1]); state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX3, matrices[2]); diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index 11d8fd815f7f..2b58d228c709 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -4233,6 +4233,7 @@ RID RasterizerStorageGLES2::light_create(VS::LightType p_type) { light->param[VS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET] = 0.6; light->param[VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] = 0.1; light->param[VS::LIGHT_PARAM_SHADOW_BIAS_SPLIT_SCALE] = 0.1; + light->param[VS::LIGHT_PARAM_SHADOW_FADE_START] = 0.8; light->color = Color(1, 1, 1, 1); light->shadow = false; @@ -4270,7 +4271,8 @@ void RasterizerStorageGLES2::light_set_param(RID p_light, VS::LightParam p_param case VS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET: case VS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET: case VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS: - case VS::LIGHT_PARAM_SHADOW_BIAS: { + case VS::LIGHT_PARAM_SHADOW_BIAS: + case VS::LIGHT_PARAM_SHADOW_FADE_START: { light->version++; light->instance_change_notify(true, false); } break; diff --git a/drivers/gles2/shaders/scene.glsl b/drivers/gles2/shaders/scene.glsl index 961940c2610b..d9f9cccd0386 100644 --- a/drivers/gles2/shaders/scene.glsl +++ b/drivers/gles2/shaders/scene.glsl @@ -1104,6 +1104,8 @@ uniform highp sampler2D light_shadow_atlas; //texunit:-3 #ifdef LIGHT_MODE_DIRECTIONAL uniform highp sampler2D light_directional_shadow; // texunit:-3 uniform highp vec4 light_split_offsets; +uniform mediump float fade_from; +uniform mediump float fade_to; #endif varying highp vec4 shadow_coord; @@ -1987,7 +1989,6 @@ FRAGMENT_SHADER_CODE float shadow4 = sample_shadow(light_directional_shadow, shadow_coord4); if (depth_z < light_split_offsets.w) { - float pssm_fade = 0.0; float shadow_att = 1.0; #ifdef LIGHT_USE_PSSM_BLEND float shadow_att2 = 1.0; @@ -2023,7 +2024,6 @@ FRAGMENT_SHADER_CODE } else { shadow_att = shadow4; - pssm_fade = smoothstep(light_split_offsets.z, light_split_offsets.w, depth_z); #if defined(LIGHT_USE_PSSM_BLEND) use_blend = false; @@ -2047,7 +2047,6 @@ FRAGMENT_SHADER_CODE float shadow3 = sample_shadow(light_directional_shadow, shadow_coord3); if (depth_z < light_split_offsets.z) { - float pssm_fade = 0.0; float shadow_att = 1.0; #ifdef LIGHT_USE_PSSM_BLEND float shadow_att2 = 1.0; @@ -2097,7 +2096,6 @@ FRAGMENT_SHADER_CODE if (depth_z < light_split_offsets.y) { float shadow_att = 1.0; - float pssm_fade = 0.0; #ifdef LIGHT_USE_PSSM_BLEND float shadow_att2 = 1.0; @@ -2105,7 +2103,6 @@ FRAGMENT_SHADER_CODE bool use_blend = true; #endif if (depth_z < light_split_offsets.x) { - float pssm_fade = 0.0; shadow_att = shadow1; #ifdef LIGHT_USE_PSSM_BLEND @@ -2114,7 +2111,6 @@ FRAGMENT_SHADER_CODE #endif } else { shadow_att = shadow2; - pssm_fade = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); #ifdef LIGHT_USE_PSSM_BLEND use_blend = false; #endif @@ -2148,7 +2144,6 @@ FRAGMENT_SHADER_CODE #endif //pssm2 highp vec4 pssm_coord; - float pssm_fade = 0.0; #ifdef LIGHT_USE_PSSM_BLEND float pssm_blend; @@ -2187,7 +2182,6 @@ FRAGMENT_SHADER_CODE } else { pssm_coord = shadow_coord4; - pssm_fade = smoothstep(light_split_offsets.z, light_split_offsets.w, depth_z); #if defined(LIGHT_USE_PSSM_BLEND) use_blend = false; @@ -2217,7 +2211,6 @@ FRAGMENT_SHADER_CODE } } else { pssm_coord = shadow_coord3; - pssm_fade = smoothstep(light_split_offsets.y, light_split_offsets.z, depth_z); #if defined(LIGHT_USE_PSSM_BLEND) use_blend = false; @@ -2236,7 +2229,6 @@ FRAGMENT_SHADER_CODE #endif } else { pssm_coord = shadow_coord2; - pssm_fade = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); #ifdef LIGHT_USE_PSSM_BLEND use_blend = false; #endif @@ -2258,7 +2250,8 @@ FRAGMENT_SHADER_CODE } #endif - light_att *= mix(shadow_color.rgb, vec3(1.0), shadow); + float pssm_fade = smoothstep(fade_from, fade_to, vertex.z); + light_att *= mix(mix(shadow_color.rgb, vec3(1.0), shadow), vec3(1.0), pssm_fade); } } #endif //use vertex lighting diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 3093973979dd..b514a580faef 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -2766,6 +2766,7 @@ void RasterizerSceneGLES3::_setup_directional_light(int p_index, const Transform } } + // Store the fade distance factor relative to the last split's end. ubo_data.shadow_split_offsets[j] = li->shadow_transform[j].split; Transform modelview = (p_camera_inverse_transform * li->shadow_transform[j].transform).affine_inverse(); @@ -2785,6 +2786,11 @@ void RasterizerSceneGLES3::_setup_directional_light(int p_index, const Transform ubo_data.light_clamp[2] = atlas_rect.size.x; ubo_data.light_clamp[3] = atlas_rect.size.y; } + + const float fade_start = li->light_ptr->param[VS::LIGHT_PARAM_SHADOW_FADE_START]; + // Using 1.0 would break `smoothstep()` in the shader. + ubo_data.fade_from = -ubo_data.shadow_split_offsets[shadow_count - 1] * MIN(fade_start, 0.999); + ubo_data.fade_to = -ubo_data.shadow_split_offsets[shadow_count - 1]; } glBindBuffer(GL_UNIFORM_BUFFER, state.directional_ubo); diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 4342d4926773..0463e5948682 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -587,6 +587,10 @@ class RasterizerSceneGLES3 : public RasterizerScene { float matrix[4 * 16]; } shadow; float shadow_split_offsets[4]; + + float fade_from; + float fade_to; + float pad[2]; }; struct LightInstance : public RID_Data { diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 04da40024304..6fdd8c466d73 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -5450,6 +5450,7 @@ RID RasterizerStorageGLES3::light_create(VS::LightType p_type) { light->param[VS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET] = 0.6; light->param[VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] = 0.1; light->param[VS::LIGHT_PARAM_SHADOW_BIAS_SPLIT_SCALE] = 0.1; + light->param[VS::LIGHT_PARAM_SHADOW_FADE_START] = 0.8; light->color = Color(1, 1, 1, 1); light->shadow = false; @@ -5486,7 +5487,8 @@ void RasterizerStorageGLES3::light_set_param(RID p_light, VS::LightParam p_param case VS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET: case VS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET: case VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS: - case VS::LIGHT_PARAM_SHADOW_BIAS: { + case VS::LIGHT_PARAM_SHADOW_BIAS: + case VS::LIGHT_PARAM_SHADOW_FADE_START: { light->version++; light->instance_change_notify(true, false); } break; diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index a4d35a79b967..dd22d99cdbc0 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -146,6 +146,10 @@ layout(std140) uniform DirectionalLightData { //ubo:3 highp mat4 shadow_matrix3; highp mat4 shadow_matrix4; mediump vec4 shadow_split_offsets; + + mediump float fade_from; + mediump float fade_to; + mediump vec2 pad; }; #endif //ubershader-skip @@ -842,6 +846,10 @@ layout(std140) uniform DirectionalLightData { highp mat4 shadow_matrix3; highp mat4 shadow_matrix4; mediump vec4 shadow_split_offsets; + + mediump float fade_from; + mediump float fade_to; + mediump vec2 pad; }; uniform highp sampler2DShadow directional_shadow; // texunit:-5 @@ -2141,7 +2149,6 @@ FRAGMENT_SHADER_CODE #endif //LIGHT_USE_PSSM4 //ubershader-runtime if (depth_z < value) { vec3 pssm_coord; - float pssm_fade = 0.0; #ifdef LIGHT_USE_PSSM_BLEND //ubershader-skip float pssm_blend; @@ -2187,7 +2194,6 @@ FRAGMENT_SHADER_CODE } else { highp vec4 splane = (shadow_matrix4 * vec4(vertex, 1.0)); pssm_coord = splane.xyz / splane.w; - pssm_fade = smoothstep(shadow_split_offsets.z, shadow_split_offsets.w, depth_z); #ifdef LIGHT_USE_PSSM_BLEND //ubershader-runtime use_blend = false; @@ -2225,7 +2231,6 @@ FRAGMENT_SHADER_CODE } else { highp vec4 splane = (shadow_matrix3 * vec4(vertex, 1.0)); pssm_coord = splane.xyz / splane.w; - pssm_fade = smoothstep(shadow_split_offsets.y, shadow_split_offsets.z, depth_z); #ifdef LIGHT_USE_PSSM_BLEND //ubershader-runtime use_blend = false; @@ -2250,7 +2255,6 @@ FRAGMENT_SHADER_CODE } else { highp vec4 splane = (shadow_matrix2 * vec4(vertex, 1.0)); pssm_coord = splane.xyz / splane.w; - pssm_fade = smoothstep(shadow_split_offsets.x, shadow_split_offsets.y, depth_z); #ifdef LIGHT_USE_PSSM_BLEND //ubershader-runtime use_blend = false; @@ -2287,6 +2291,7 @@ FRAGMENT_SHADER_CODE shadow = min(shadow, contact_shadow); } #endif //ubershader-runtime + float pssm_fade = smoothstep(fade_from, fade_to, vertex.z); light_attenuation = mix(mix(shadow_color_contact.rgb, vec3(1.0), shadow), vec3(1.0), pssm_fade); } diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp index e0d21bca4e67..8370b4434851 100644 --- a/scene/3d/light.cpp +++ b/scene/3d/light.cpp @@ -267,6 +267,7 @@ void Light::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_SHADOW_NORMAL_BIAS); BIND_ENUM_CONSTANT(PARAM_SHADOW_BIAS); BIND_ENUM_CONSTANT(PARAM_SHADOW_BIAS_SPLIT_SCALE); + BIND_ENUM_CONSTANT(PARAM_SHADOW_FADE_START); BIND_ENUM_CONSTANT(PARAM_MAX); BIND_ENUM_CONSTANT(BAKE_DISABLED); @@ -314,6 +315,7 @@ Light::Light(VisualServer::LightType p_type) { set_param(PARAM_SHADOW_SPLIT_1_OFFSET, 0.1); set_param(PARAM_SHADOW_SPLIT_2_OFFSET, 0.2); set_param(PARAM_SHADOW_SPLIT_3_OFFSET, 0.5); + set_param(PARAM_SHADOW_FADE_START, 0.8); set_param(PARAM_SHADOW_NORMAL_BIAS, 0.0); set_param(PARAM_SHADOW_BIAS, 0.15); set_disable_scale(true); @@ -391,6 +393,7 @@ void DirectionalLight::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_split_2", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_2_OFFSET); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_split_3", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_3_OFFSET); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "directional_shadow_blend_splits"), "set_blend_splits", "is_blend_splits_enabled"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_fade_start", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_FADE_START); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_normal_bias", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_NORMAL_BIAS); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_bias_split_scale", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_BIAS_SPLIT_SCALE); ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_depth_range", PROPERTY_HINT_ENUM, "Stable,Optimized"), "set_shadow_depth_range", "get_shadow_depth_range"); @@ -411,6 +414,7 @@ DirectionalLight::DirectionalLight() : set_param(PARAM_SHADOW_BIAS, 0.1); set_param(PARAM_SHADOW_MAX_DISTANCE, 100); set_param(PARAM_SHADOW_BIAS_SPLIT_SCALE, 0.25); + set_param(PARAM_SHADOW_FADE_START, 0.8); set_shadow_mode(SHADOW_PARALLEL_4_SPLITS); set_shadow_depth_range(SHADOW_DEPTH_RANGE_STABLE); diff --git a/scene/3d/light.h b/scene/3d/light.h index fbce9f678c0f..9ad3f37b4a9c 100644 --- a/scene/3d/light.h +++ b/scene/3d/light.h @@ -57,6 +57,7 @@ class Light : public VisualInstance { PARAM_SHADOW_NORMAL_BIAS = VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS, PARAM_SHADOW_BIAS = VS::LIGHT_PARAM_SHADOW_BIAS, PARAM_SHADOW_BIAS_SPLIT_SCALE = VS::LIGHT_PARAM_SHADOW_BIAS_SPLIT_SCALE, + PARAM_SHADOW_FADE_START = VS::LIGHT_PARAM_SHADOW_FADE_START, PARAM_MAX = VS::LIGHT_PARAM_MAX }; diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp index 447ca596d97e..66c162b4c688 100644 --- a/servers/visual_server.cpp +++ b/servers/visual_server.cpp @@ -2381,6 +2381,7 @@ void VisualServer::_bind_methods() { BIND_ENUM_CONSTANT(LIGHT_PARAM_SHADOW_NORMAL_BIAS); BIND_ENUM_CONSTANT(LIGHT_PARAM_SHADOW_BIAS); BIND_ENUM_CONSTANT(LIGHT_PARAM_SHADOW_BIAS_SPLIT_SCALE); + BIND_ENUM_CONSTANT(LIGHT_PARAM_SHADOW_FADE_START); BIND_ENUM_CONSTANT(LIGHT_PARAM_MAX); BIND_ENUM_CONSTANT(LIGHT_BAKE_DISABLED); diff --git a/servers/visual_server.h b/servers/visual_server.h index bb8fc9010d93..6034f1b80d5e 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -448,6 +448,7 @@ class VisualServer : public Object { LIGHT_PARAM_SHADOW_NORMAL_BIAS, LIGHT_PARAM_SHADOW_BIAS, LIGHT_PARAM_SHADOW_BIAS_SPLIT_SCALE, + LIGHT_PARAM_SHADOW_FADE_START, LIGHT_PARAM_MAX };