Implement distance fade properties in OmniLight3D and SpotLight3D
This can be used to fade lights and their shadows in the distance, similar to Decal nodes. This can bring significant performance improvements, especially for lights with shadows enabled and when using higher-than-default shadow quality settings. While lights can be smoothly faded out over distance, shadows are currently "all or nothing" since per-light shadow color is no longer customizable in the Vulkan renderer. This may result in noticeable pop-in when leaving the shadow cutoff distance, but depending on the scene, it may not always be that noticeable.
This commit is contained in:
parent
4dc8214831
commit
b1a295b739
14 changed files with 214 additions and 6 deletions
|
|
@ -412,6 +412,7 @@ public:
|
|||
void light_set_projector(RID p_light, RID p_texture) override {}
|
||||
void light_set_negative(RID p_light, bool p_enable) override {}
|
||||
void light_set_cull_mask(RID p_light, uint32_t p_mask) override {}
|
||||
void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) override {}
|
||||
void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override {}
|
||||
void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override {}
|
||||
void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override {}
|
||||
|
|
|
|||
|
|
@ -3440,8 +3440,22 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
|
|||
continue;
|
||||
}
|
||||
|
||||
const real_t distance = camera_plane.distance_to(li->transform.origin);
|
||||
|
||||
if (storage->light_is_distance_fade_enabled(li->light)) {
|
||||
const float fade_begin = storage->light_get_distance_fade_begin(li->light);
|
||||
const float fade_length = storage->light_get_distance_fade_length(li->light);
|
||||
|
||||
if (distance > fade_begin) {
|
||||
if (distance > fade_begin + fade_length) {
|
||||
// Out of range, don't draw this light to improve performance.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cluster.omni_light_sort[cluster.omni_light_count].instance = li;
|
||||
cluster.omni_light_sort[cluster.omni_light_count].depth = camera_plane.distance_to(li->transform.origin);
|
||||
cluster.omni_light_sort[cluster.omni_light_count].depth = distance;
|
||||
cluster.omni_light_count++;
|
||||
} break;
|
||||
case RS::LIGHT_SPOT: {
|
||||
|
|
@ -3449,8 +3463,22 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
|
|||
continue;
|
||||
}
|
||||
|
||||
const real_t distance = camera_plane.distance_to(li->transform.origin);
|
||||
|
||||
if (storage->light_is_distance_fade_enabled(li->light)) {
|
||||
const float fade_begin = storage->light_get_distance_fade_begin(li->light);
|
||||
const float fade_length = storage->light_get_distance_fade_length(li->light);
|
||||
|
||||
if (distance > fade_begin) {
|
||||
if (distance > fade_begin + fade_length) {
|
||||
// Out of range, don't draw this light to improve performance.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cluster.spot_light_sort[cluster.spot_light_count].instance = li;
|
||||
cluster.spot_light_sort[cluster.spot_light_count].depth = camera_plane.distance_to(li->transform.origin);
|
||||
cluster.spot_light_sort[cluster.spot_light_count].depth = distance;
|
||||
cluster.spot_light_count++;
|
||||
} break;
|
||||
}
|
||||
|
|
@ -3494,7 +3522,24 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
|
|||
|
||||
light_data.attenuation = storage->light_get_param(base, RS::LIGHT_PARAM_ATTENUATION);
|
||||
|
||||
float energy = sign * storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI;
|
||||
// Reuse fade begin, fade length and distance for shadow LOD determination later.
|
||||
float fade_begin = 0.0;
|
||||
float fade_length = 0.0;
|
||||
real_t distance = 0.0;
|
||||
|
||||
float fade = 1.0;
|
||||
if (storage->light_is_distance_fade_enabled(li->light)) {
|
||||
fade_begin = storage->light_get_distance_fade_begin(li->light);
|
||||
fade_length = storage->light_get_distance_fade_length(li->light);
|
||||
distance = camera_plane.distance_to(li->transform.origin);
|
||||
|
||||
if (distance > fade_begin) {
|
||||
// Use `smoothstep()` to make opacity changes more gradual and less noticeable to the player.
|
||||
fade = Math::smoothstep(0.0f, 1.0f, 1.0f - float(distance - fade_begin) / fade_length);
|
||||
}
|
||||
}
|
||||
|
||||
float energy = sign * storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI * fade;
|
||||
|
||||
light_data.color[0] = linear_col.r * energy;
|
||||
light_data.color[1] = linear_col.g * energy;
|
||||
|
|
@ -3555,7 +3600,17 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
|
|||
light_data.projector_rect[3] = 0;
|
||||
}
|
||||
|
||||
if (shadow_atlas && shadow_atlas->shadow_owners.has(li->self)) {
|
||||
const bool needs_shadow = shadow_atlas && shadow_atlas->shadow_owners.has(li->self);
|
||||
|
||||
bool in_shadow_range = true;
|
||||
if (needs_shadow && storage->light_is_distance_fade_enabled(li->light)) {
|
||||
if (distance > storage->light_get_distance_fade_shadow(li->light)) {
|
||||
// Out of range, don't draw shadows to improve performance.
|
||||
in_shadow_range = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_shadow && in_shadow_range) {
|
||||
// fill in the shadow information
|
||||
|
||||
light_data.shadow_enabled = true;
|
||||
|
|
|
|||
|
|
@ -6600,6 +6600,16 @@ void RendererStorageRD::light_set_cull_mask(RID p_light, uint32_t p_mask) {
|
|||
light->dependency.changed_notify(DEPENDENCY_CHANGED_LIGHT);
|
||||
}
|
||||
|
||||
void RendererStorageRD::light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) {
|
||||
Light *light = light_owner.get_or_null(p_light);
|
||||
ERR_FAIL_COND(!light);
|
||||
|
||||
light->distance_fade = p_enabled;
|
||||
light->distance_fade_begin = p_begin;
|
||||
light->distance_fade_shadow = p_shadow;
|
||||
light->distance_fade_length = p_length;
|
||||
}
|
||||
|
||||
void RendererStorageRD::light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) {
|
||||
Light *light = light_owner.get_or_null(p_light);
|
||||
ERR_FAIL_COND(!light);
|
||||
|
|
|
|||
|
|
@ -1033,6 +1033,10 @@ private:
|
|||
RS::LightBakeMode bake_mode = RS::LIGHT_BAKE_DYNAMIC;
|
||||
uint32_t max_sdfgi_cascade = 2;
|
||||
uint32_t cull_mask = 0xFFFFFFFF;
|
||||
bool distance_fade = false;
|
||||
real_t distance_fade_begin = 40.0;
|
||||
real_t distance_fade_shadow = 50.0;
|
||||
real_t distance_fade_length = 10.0;
|
||||
RS::LightOmniShadowMode omni_shadow_mode = RS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID;
|
||||
RS::LightDirectionalShadowMode directional_shadow_mode = RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL;
|
||||
bool directional_blend_splits = false;
|
||||
|
|
@ -1841,6 +1845,7 @@ public:
|
|||
void light_set_projector(RID p_light, RID p_texture);
|
||||
void light_set_negative(RID p_light, bool p_enable);
|
||||
void light_set_cull_mask(RID p_light, uint32_t p_mask);
|
||||
void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length);
|
||||
void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled);
|
||||
void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode);
|
||||
void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade);
|
||||
|
|
@ -1899,6 +1904,26 @@ public:
|
|||
return light->cull_mask;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool light_is_distance_fade_enabled(RID p_light) {
|
||||
const Light *light = light_owner.get_or_null(p_light);
|
||||
return light->distance_fade;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ float light_get_distance_fade_begin(RID p_light) {
|
||||
const Light *light = light_owner.get_or_null(p_light);
|
||||
return light->distance_fade_begin;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ float light_get_distance_fade_shadow(RID p_light) {
|
||||
const Light *light = light_owner.get_or_null(p_light);
|
||||
return light->distance_fade_shadow;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ float light_get_distance_fade_length(RID p_light) {
|
||||
const Light *light = light_owner.get_or_null(p_light);
|
||||
return light->distance_fade_length;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool light_has_shadow(RID p_light) const {
|
||||
const Light *light = light_owner.get_or_null(p_light);
|
||||
ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL);
|
||||
|
|
|
|||
|
|
@ -321,6 +321,7 @@ public:
|
|||
virtual void light_set_projector(RID p_light, RID p_texture) = 0;
|
||||
virtual void light_set_negative(RID p_light, bool p_enable) = 0;
|
||||
virtual void light_set_cull_mask(RID p_light, uint32_t p_mask) = 0;
|
||||
virtual void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) = 0;
|
||||
virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) = 0;
|
||||
virtual void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) = 0;
|
||||
virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) = 0;
|
||||
|
|
|
|||
|
|
@ -346,6 +346,7 @@ public:
|
|||
FUNC2(light_set_projector, RID, RID)
|
||||
FUNC2(light_set_negative, RID, bool)
|
||||
FUNC2(light_set_cull_mask, RID, uint32_t)
|
||||
FUNC5(light_set_distance_fade, RID, bool, float, float, float)
|
||||
FUNC2(light_set_reverse_cull_face_mode, RID, bool)
|
||||
FUNC2(light_set_bake_mode, RID, LightBakeMode)
|
||||
FUNC2(light_set_max_sdfgi_cascade, RID, uint32_t)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue