diff --git a/doc/classes/ReflectionProbe.xml b/doc/classes/ReflectionProbe.xml index fee48dd2462..165e793243c 100644 --- a/doc/classes/ReflectionProbe.xml +++ b/doc/classes/ReflectionProbe.xml @@ -28,6 +28,15 @@ Sets the cull mask which determines what objects are drawn by this probe. Every [VisualInstance3D] with a layer included in this cull mask will be rendered by the probe. To improve performance, it is best to only include large objects which are likely to take up a lot of space in the reflection. + + The distance from the camera at which the reflection probe begins to fade away (in 3D units). + + + If [code]true[/code], the reflection probe will smoothly fade away when far from the active [Camera3D] starting at [member distance_fade_begin]. This acts as a form of level of detail (LOD). The reflection probe will fade out over [member distance_fade_begin] + [member distance_fade_length], after which it will be culled and not sent to the shader at all. Use this to reduce the number of active reflection probes in a scene and thus improve performance. + + + Distance over which the reflection probe fades. The reflection probe's intensity is progressively reduced over this distance and is completely invisible at the end. + If [code]true[/code], computes shadows in the reflection probe. This makes the reflection probe slower to render; you may want to disable this if using the [constant UPDATE_ALWAYS] [member update_mode]. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 6aa92373855..d3e556da789 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -2617,6 +2617,16 @@ Sets the render cull mask for this reflection probe. Only instances with a matching cull mask will be rendered by this probe. Equivalent to [member ReflectionProbe.cull_mask]. + + + + + + + + Sets the distance fade for this reflection probe. This acts as a form of level of detail (LOD) and can be used to improve performance. Equivalent to [member ReflectionProbe.distance_fade_enabled], [member ReflectionProbe.distance_fade_begin], and [member ReflectionProbe.distance_fade_length]. + + diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp index b6bd4a2760a..0cab3fd8e45 100644 --- a/drivers/gles3/storage/light_storage.cpp +++ b/drivers/gles3/storage/light_storage.cpp @@ -418,6 +418,9 @@ void LightStorage::reflection_probe_set_enable_box_projection(RID p_probe, bool void LightStorage::reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) { } +void LightStorage::reflection_probe_set_distance_fade(RID p_probe, bool p_enable, float p_begin, float p_length) { +} + void LightStorage::reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) { } diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h index 12cb2c43936..2f1f0d6a5fd 100644 --- a/drivers/gles3/storage/light_storage.h +++ b/drivers/gles3/storage/light_storage.h @@ -118,6 +118,9 @@ struct ReflectionProbe { bool interior = false; bool box_projection = false; bool enable_shadows = false; + bool distance_fade = false; + float distance_fade_begin = 40.0; + float distance_fade_length = 10.0; uint32_t cull_mask = (1 << 20) - 1; float mesh_lod_threshold = 0.01; float baked_exposure = 1.0; @@ -337,6 +340,7 @@ public: virtual void reflection_probe_set_as_interior(RID p_probe, bool p_enable) override; virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) override; virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) override; + virtual void reflection_probe_set_distance_fade(RID p_probe, bool p_enable, float p_begin, float p_length) override; virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) override; virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) override; virtual void reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) override; diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp index bc3cc31963c..a13117363ea 100644 --- a/scene/3d/reflection_probe.cpp +++ b/scene/3d/reflection_probe.cpp @@ -153,6 +153,34 @@ bool ReflectionProbe::are_shadows_enabled() const { return enable_shadows; } +void ReflectionProbe::set_enable_distance_fade(bool p_enable) { + distance_fade_enabled = p_enable; + RS::get_singleton()->reflection_probe_set_distance_fade(probe, distance_fade_enabled, distance_fade_begin, distance_fade_length); + notify_property_list_changed(); +} + +bool ReflectionProbe::is_distance_fade_enabled() const { + return distance_fade_enabled; +} + +void ReflectionProbe::set_distance_fade_begin(real_t p_distance) { + distance_fade_begin = p_distance; + RS::get_singleton()->reflection_probe_set_distance_fade(probe, distance_fade_enabled, distance_fade_begin, distance_fade_length); +} + +real_t ReflectionProbe::get_distance_fade_begin() const { + return distance_fade_begin; +} + +void ReflectionProbe::set_distance_fade_length(real_t p_length) { + distance_fade_length = p_length; + RS::get_singleton()->reflection_probe_set_distance_fade(probe, distance_fade_enabled, distance_fade_begin, distance_fade_length); +} + +real_t ReflectionProbe::get_distance_fade_length() const { + return distance_fade_length; +} + void ReflectionProbe::set_cull_mask(uint32_t p_layers) { cull_mask = p_layers; RS::get_singleton()->reflection_probe_set_cull_mask(probe, p_layers); @@ -184,6 +212,12 @@ void ReflectionProbe::_validate_property(PropertyInfo &p_property) const { p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } + + if (!distance_fade_enabled && (p_property.name == "distance_fade_begin" || p_property.name == "distance_fade_length")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + + VisualInstance3D::_validate_property(p_property); } void ReflectionProbe::_bind_methods() { @@ -220,6 +254,15 @@ void ReflectionProbe::_bind_methods() { ClassDB::bind_method(D_METHOD("set_enable_shadows", "enable"), &ReflectionProbe::set_enable_shadows); ClassDB::bind_method(D_METHOD("are_shadows_enabled"), &ReflectionProbe::are_shadows_enabled); + ClassDB::bind_method(D_METHOD("set_enable_distance_fade", "enable"), &ReflectionProbe::set_enable_distance_fade); + ClassDB::bind_method(D_METHOD("is_distance_fade_enabled"), &ReflectionProbe::is_distance_fade_enabled); + + ClassDB::bind_method(D_METHOD("set_distance_fade_begin", "distance"), &ReflectionProbe::set_distance_fade_begin); + ClassDB::bind_method(D_METHOD("get_distance_fade_begin"), &ReflectionProbe::get_distance_fade_begin); + + ClassDB::bind_method(D_METHOD("set_distance_fade_length", "distance"), &ReflectionProbe::set_distance_fade_length); + ClassDB::bind_method(D_METHOD("get_distance_fade_length"), &ReflectionProbe::get_distance_fade_length); + ClassDB::bind_method(D_METHOD("set_cull_mask", "layers"), &ReflectionProbe::set_cull_mask); ClassDB::bind_method(D_METHOD("get_cull_mask"), &ReflectionProbe::get_cull_mask); @@ -242,6 +285,11 @@ void ReflectionProbe::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ambient_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ambient_color", "get_ambient_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ambient_color_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_ambient_color_energy", "get_ambient_color_energy"); + ADD_GROUP("Distance Fade", "distance_fade_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_fade_enabled"), "set_enable_distance_fade", "is_distance_fade_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_begin", "get_distance_fade_begin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_length", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_length", "get_distance_fade_length"); + BIND_ENUM_CONSTANT(UPDATE_ONCE); BIND_ENUM_CONSTANT(UPDATE_ALWAYS); diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h index 5a5a3fe0bb7..6b2b261edc1 100644 --- a/scene/3d/reflection_probe.h +++ b/scene/3d/reflection_probe.h @@ -62,6 +62,9 @@ private: float ambient_color_energy = 1.0; float mesh_lod_threshold = 1.0; + bool distance_fade_enabled = false; + real_t distance_fade_begin = 40.0; + real_t distance_fade_length = 10.0; uint32_t cull_mask = (1 << 20) - 1; UpdateMode update_mode = UPDATE_ONCE; @@ -106,6 +109,15 @@ public: void set_enable_shadows(bool p_enable); bool are_shadows_enabled() const; + void set_enable_distance_fade(bool p_enable); + bool is_distance_fade_enabled() const; + + void set_distance_fade_begin(real_t p_distance); + real_t get_distance_fade_begin() const; + + void set_distance_fade_length(real_t p_length); + real_t get_distance_fade_length() const; + void set_cull_mask(uint32_t p_layers); uint32_t get_cull_mask() const; diff --git a/servers/rendering/dummy/storage/light_storage.h b/servers/rendering/dummy/storage/light_storage.h index 13c342d8234..488530740ed 100644 --- a/servers/rendering/dummy/storage/light_storage.h +++ b/servers/rendering/dummy/storage/light_storage.h @@ -106,6 +106,7 @@ public: virtual void reflection_probe_set_as_interior(RID p_probe, bool p_enable) override {} virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) override {} virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) override {} + virtual void reflection_probe_set_distance_fade(RID p_probe, bool p_enable, float p_begin, float p_length) override {} virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) override {} virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) override {} virtual void reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) override {} diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp index c83473ef072..d1f671ff625 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp @@ -1108,6 +1108,15 @@ void LightStorage::reflection_probe_set_enable_shadows(RID p_probe, bool p_enabl reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE); } +void LightStorage::reflection_probe_set_distance_fade(RID p_probe, bool p_enable, float p_begin, float p_length) { + ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->distance_fade = p_enable; + reflection_probe->distance_fade_begin = p_begin; + reflection_probe->distance_fade_length = p_length; +} + void LightStorage::reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) { ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND(!reflection_probe); @@ -1625,10 +1634,15 @@ void LightStorage::update_reflection_probe_buffer(RenderDataRD *p_render_data, c continue; } - Transform3D transform = rpi->transform; + const real_t distance = -p_camera_inverse_transform.xform(rpi->transform.origin).z; + const ReflectionProbe *probe = reflection_probe_owner.get_or_null(rpi->probe); + if (probe->distance_fade && distance > probe->distance_fade_begin + probe->distance_fade_length) { + // Don't use this reflection probe, as it's invisible. + continue; + } reflection_sort[reflection_count].probe_instance = rpi; - reflection_sort[reflection_count].depth = -p_camera_inverse_transform.xform(transform.origin).z; + reflection_sort[reflection_count].depth = distance; reflection_count++; } @@ -1665,7 +1679,17 @@ void LightStorage::update_reflection_probe_buffer(RenderDataRD *p_render_data, c reflection_ubo.box_offset[2] = origin_offset.z; reflection_ubo.mask = probe->cull_mask; - reflection_ubo.intensity = probe->intensity; + float fade = 1.0; + if (probe->distance_fade) { + const real_t distance = -p_camera_inverse_transform.xform(rpi->transform.origin).z; + + if (distance > probe->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 - probe->distance_fade_begin) / probe->distance_fade_length); + } + } + reflection_ubo.intensity = probe->intensity * fade; + reflection_ubo.ambient_mode = probe->ambient_mode; reflection_ubo.exterior = !probe->interior; diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h index 79006ad9824..e04d62f9950 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h @@ -230,6 +230,9 @@ private: bool interior = false; bool box_projection = false; bool enable_shadows = false; + bool distance_fade = false; + float distance_fade_begin = 40.0; + float distance_fade_length = 10.0; uint32_t cull_mask = (1 << 20) - 1; float mesh_lod_threshold = 0.01; float baked_exposure = 1.0; @@ -793,6 +796,7 @@ public: virtual void reflection_probe_set_as_interior(RID p_probe, bool p_enable) override; virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) override; virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) override; + virtual void reflection_probe_set_distance_fade(RID p_probe, bool p_enable, float p_begin, float p_length) override; virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) override; virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) override; virtual void reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) override; diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index fe212151e3c..a2783015bf4 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -391,6 +391,7 @@ public: FUNC2(reflection_probe_set_as_interior, RID, bool) FUNC2(reflection_probe_set_enable_box_projection, RID, bool) FUNC2(reflection_probe_set_enable_shadows, RID, bool) + FUNC4(reflection_probe_set_distance_fade, RID, bool, float, float) FUNC2(reflection_probe_set_cull_mask, RID, uint32_t) FUNC2(reflection_probe_set_resolution, RID, int) FUNC2(reflection_probe_set_mesh_lod_threshold, RID, float) diff --git a/servers/rendering/storage/light_storage.h b/servers/rendering/storage/light_storage.h index df5b893cd5f..927719ae23d 100644 --- a/servers/rendering/storage/light_storage.h +++ b/servers/rendering/storage/light_storage.h @@ -114,6 +114,7 @@ public: virtual void reflection_probe_set_as_interior(RID p_probe, bool p_enable) = 0; virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) = 0; virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) = 0; + virtual void reflection_probe_set_distance_fade(RID p_probe, bool p_enable, float p_begin, float p_length) = 0; virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) = 0; virtual void reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) = 0; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index cf13118451c..5fdf2176498 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1966,6 +1966,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("reflection_probe_set_as_interior", "probe", "enable"), &RenderingServer::reflection_probe_set_as_interior); ClassDB::bind_method(D_METHOD("reflection_probe_set_enable_box_projection", "probe", "enable"), &RenderingServer::reflection_probe_set_enable_box_projection); ClassDB::bind_method(D_METHOD("reflection_probe_set_enable_shadows", "probe", "enable"), &RenderingServer::reflection_probe_set_enable_shadows); + ClassDB::bind_method(D_METHOD("reflection_probe_set_distance_fade", "probe", "enable", "begin", "length"), &RenderingServer::reflection_probe_set_distance_fade); ClassDB::bind_method(D_METHOD("reflection_probe_set_cull_mask", "probe", "layers"), &RenderingServer::reflection_probe_set_cull_mask); ClassDB::bind_method(D_METHOD("reflection_probe_set_resolution", "probe", "resolution"), &RenderingServer::reflection_probe_set_resolution); ClassDB::bind_method(D_METHOD("reflection_probe_set_mesh_lod_threshold", "probe", "pixels"), &RenderingServer::reflection_probe_set_mesh_lod_threshold); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 6885d80301a..2bd259eee4f 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -544,6 +544,7 @@ public: virtual void reflection_probe_set_as_interior(RID p_probe, bool p_enable) = 0; virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) = 0; virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) = 0; + virtual void reflection_probe_set_distance_fade(RID p_probe, bool p_enabled, float p_begin, float p_length) = 0; virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) = 0; virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) = 0; virtual void reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_pixels) = 0;