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;