diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 4266bab2a175..50b297fd8e6a 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -2722,10 +2722,11 @@
[b]Note:[/b] Due to memory constraints, occlusion culling is not supported by default in Web export templates. It can be enabled by compiling custom Web export templates with [code]module_raycast_enabled=yes[/code].
- Number of cubemaps to store in the reflection atlas. The number of [ReflectionProbe]s in a scene will be limited by this amount. A higher number requires more VRAM.
+ Number of cubemaps to store in the reflection atlas. The number of [ReflectionProbe]s in a scene will be limited by this amount. A higher number requires more VRAM. Consider lowering this value if increasing [member rendering/reflections/reflection_atlas/reflection_size].
- Size of cubemap faces for [ReflectionProbe]s. A higher number requires more VRAM and may make reflection probe updating slower.
+ Size of cubemap faces for [ReflectionProbe]s. A higher number requires more VRAM and may make reflection probe updating slower. Consider lowering [member rendering/reflections/reflection_atlas/reflection_count] if increasing this value.
+ [b]Note:[/b] The fast filtering algorithm used in [constant ReflectionProbe.FILTER_MODE_REALTIME] is limited to 256×256 cubemaps, so [member ProjectSettings.rendering/reflections/reflection_atlas/reflection_size] must be set to [code]256[/code] if using that filter mode in the project. Otherwise, a warning is printed and the overridden reflection atlas size is ignored.
Lower-end override for [member rendering/reflections/reflection_atlas/reflection_size] on mobile devices, due to performance concerns or driver support.
@@ -2740,7 +2741,7 @@
Lower-end override for [member rendering/reflections/sky_reflections/ggx_samples] on mobile devices, due to performance concerns or driver support.
- Limits the number of layers to use in radiance maps when using importance sampling. A lower number will be slightly faster and take up less VRAM.
+ Limits the number of layers to use in radiance maps when using importance sampling for the sky and reflection probes. A lower number will be slightly faster and take up less VRAM.
If [code]true[/code], uses texture arrays instead of mipmaps for reflection probes and panorama backgrounds (sky). This reduces jitter noise and upscaling artifacts on reflections, but is significantly slower to compute and uses [member rendering/reflections/sky_reflections/roughness_layers] times more memory.
diff --git a/doc/classes/ReflectionProbe.xml b/doc/classes/ReflectionProbe.xml
index 367942682e18..b9392ef0aa0b 100644
--- a/doc/classes/ReflectionProbe.xml
+++ b/doc/classes/ReflectionProbe.xml
@@ -5,7 +5,7 @@
Captures its surroundings as a cubemap, and stores versions of it with increasing levels of blur to simulate different material roughnesses.
- The [ReflectionProbe] is used to create high-quality reflections at a low performance cost (when [member update_mode] is [constant UPDATE_ONCE]). [ReflectionProbe]s can be blended together and with the rest of the scene smoothly. [ReflectionProbe]s can also be combined with [VoxelGI], SDFGI ([member Environment.sdfgi_enabled]) and screen-space reflections ([member Environment.ssr_enabled]) to get more accurate reflections in specific areas. [ReflectionProbe]s render all objects within their [member cull_mask], so updating them can be quite expensive. It is best to update them once with the important static objects and then leave them as-is.
+ The [ReflectionProbe] is used to create high-quality reflections at a low performance cost (when [member update_mode] is [constant UPDATE_ONCE] or [constant UPDATE_STATIC]). [ReflectionProbe]s can be blended together and with the rest of the scene smoothly. [ReflectionProbe]s can also be combined with [VoxelGI], SDFGI ([member Environment.sdfgi_enabled]) and screen-space reflections ([member Environment.ssr_enabled]) to get more accurate reflections in specific areas. [ReflectionProbe]s render all objects within their [member cull_mask], so updating them can be quite expensive. It is best to update them once with the important static objects and then leave them as-is.
[b]Note:[/b] Unlike [VoxelGI] and SDFGI, [ReflectionProbe]s only source their environment from a [WorldEnvironment] node. If you specify an [Environment] resource within a [Camera3D] node, it will be ignored by the [ReflectionProbe]. This can lead to incorrect lighting within the [ReflectionProbe].
[b]Note:[/b] Reflection probes are only supported in the Forward+ and Mobile rendering methods, not Compatibility. When using the Mobile rendering method, only 8 reflection probes can be displayed on each mesh resource. Attempting to display more than 8 reflection probes on a single mesh resource will result in reflection probes flickering in and out as the camera moves.
[b]Note:[/b] When using the Mobile rendering method, reflection probes will only correctly affect meshes whose visibility AABB intersects with the reflection probe's AABB. If using a shader to deform the mesh in a way that makes it go outside its AABB, [member GeometryInstance3D.extra_cull_margin] must be increased on the mesh. Otherwise, the reflection probe may not be visible on the mesh.
@@ -13,6 +13,15 @@
$DOCS_URL/tutorials/3d/global_illumination/reflection_probes.html
+
+
+
+
+ Queues an update of the ReflectionProbe cubemap and ambient lighting (if [member ambient_mode] is [constant AMBIENT_ENVIRONMENT]). The update will not be visible immediately; it will take a number of frames to be visible depending on [member update_slicing] and [member filter_mode]. [method queue_update] should be called [i]after[/i] modifying nearby objects to ensure the reflection remains up-to-date.
+ [b]Note:[/b] [method queue_update] only has an effect when [member update_mode] is [constant UPDATE_ONCE] or [constant UPDATE_STATIC].
+
+
+
The custom ambient color to use within the [ReflectionProbe]'s box defined by its [member size]. Only effective if [member ambient_mode] is [constant AMBIENT_COLOR].
@@ -34,6 +43,9 @@
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].
+
+ The filter mode to use for generating the radiance map, which affects rough reflections and ambient lighting if [member ambient_mode] is [constant AMBIENT_ENVIRONMENT]. Reflections on fully smooth materials (roughness equal to [code]0.0[/code]) are not affected by [member filter_mode].
+
Defines the reflection intensity. Intensity modulates the strength of the reflection.
@@ -59,16 +71,47 @@
[b]Note:[/b] To better fit areas that are not aligned to the grid, you can rotate the [ReflectionProbe] node.
- Sets how frequently the [ReflectionProbe] is updated. Can be [constant UPDATE_ONCE] or [constant UPDATE_ALWAYS].
+ Sets how frequently the [ReflectionProbe] is updated. See also [member update_slicing].
+
+
+ Controls how fast reflection probes should update. Higher values will render more faces per frame, allowing the reflection to update faster, but at a much greater performance cost on both the CPU and GPU. Consider adjusting this depending on the speed objects move at in the scene, since faster-moving objects require higher update rates to look good.
- Update the probe once on the next frame (recommended for most objects). The corresponding radiance map will be generated over the following six frames. This takes more time to update than [constant UPDATE_ALWAYS], but it has a lower performance cost and can result in higher-quality reflections. The ReflectionProbe is updated when its transform changes, but not when nearby geometry changes. You can force a [ReflectionProbe] update by moving the [ReflectionProbe] slightly in any direction.
+ Update the probe once on the next frame (recommended for most objects). The ReflectionProbe is updated when its transform changes, but not when nearby geometry changes. You can force a [ReflectionProbe] update by calling [method queue_update], moving it or changing one of its properties that affect its rendering.
Update the probe every frame. This provides better results for fast-moving dynamic objects (such as cars). However, it has a significant performance cost. Due to the cost, it's recommended to only use one ReflectionProbe with [constant UPDATE_ALWAYS] at most per scene. For all other use cases, use [constant UPDATE_ONCE].
+
+ Update the probe once on the next frame (recommended for most objects). The ReflectionProbe is [b]not[/b] updated when its transform changes and is not updated either when nearby geometry changes. You can force a [ReflectionProbe] update by calling [method queue_update] or changing one of its properties that affect its rendering.
+
+
+ [constant UPDATE_SLICING_1_FACE_PER_FRAME] is used when [member update_mode] is [constant UPDATE_ONCE] or [constant UPDATE_STATIC] and [constant UPDATE_SLICING_6_FACES_PER_FRAME] is used when [member update_mode] is [constant UPDATE_ALWAYS]. This matches behavior found in Godot 4.3 and prior.
+
+
+ Render 1 face of the cubemap every frame (including post-processing steps when using [constant FILTER_MODE_INCREMENTAL]). For example, if using [constant FILTER_MODE_REALTIME] in a project that renders at 60 FPS, reflections will appear to update at 10 FPS. Low performance cost.
+
+
+ Render 2 faces of the cubemap every frame (including post-processing steps when using [constant FILTER_MODE_INCREMENTAL]). For example, if using [constant FILTER_MODE_REALTIME] in a project that renders at 60 FPS, reflections will appear to update at 20 FPS. Medium performance cost.
+
+
+ Render 3 faces of the cubemap every frame (including post-processing steps when using [constant FILTER_MODE_INCREMENTAL]). For example, if using [constant FILTER_MODE_REALTIME] in a project that renders at 60 FPS, reflections will appear to update at 30 FPS. High performance cost.
+
+
+ Render 6 faces of the cubemap every frame (including post-processing steps when using [constant FILTER_MODE_INCREMENTAL]). For example, if using [constant FILTER_MODE_REALTIME] in a project that renders at 60 FPS, reflections will appear to update at 60 FPS. Very high performance cost.
+
+
+ [constant FILTER_MODE_INCREMENTAL] is used when [member update_mode] is [constant UPDATE_ONCE] or [constant UPDATE_STATIC] and [constant FILTER_MODE_REALTIME] is used when [member update_mode] is [constant UPDATE_ALWAYS]. This matches behavior found in Godot 4.3 and prior.
+
+
+ Uses high-quality importance sampling to process the radiance map over several frames. The number of frames is determined by [member ProjectSettings.rendering/reflections/sky_reflections/roughness_layers]. Use this when you need highest quality radiance maps, but have a reflection probe that updates slowly. Equivalent to [constant Sky.PROCESS_MODE_INCREMENTAL].
+
+
+ Uses the fast filtering algorithm to process the radiance map. In general, this results in lower quality, but substantially faster run times. Equivalent to [constant Sky.PROCESS_MODE_REALTIME].
+ [b]Note:[/b] The fast filtering algorithm is limited to 256×256 cubemaps, so [member ProjectSettings.rendering/reflections/reflection_atlas/reflection_size] must be set to [code]256[/code]. Otherwise, a warning is printed and the overridden reflection atlas size is ignored.
+
Do not apply any ambient lighting inside the [ReflectionProbe]'s box defined by its [member size].
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 91af70b565ff..b3ab5656b337 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -3094,6 +3094,14 @@
[b]Note:[/b] The equivalent node is [ReflectionProbe].
+
+
+
+
+ Queues an update of the ReflectionProbe cubemap and ambient lighting (if its ambient mode is [constant REFLECTION_PROBE_AMBIENT_ENVIRONMENT]). The update will not be visible immediately; it will take a number of frames to be visible depending on its update slicing and filter mode. [method reflection_probe_queue_update] should be called [i]after[/i] modifying nearby objects to ensure the reflection remains up-to-date.
+ [b]Note:[/b] [method reflection_probe_queue_update] only has an effect when the reflection probe's update mode is [constant REFLECTION_PROBE_UPDATE_ONCE] or [constant REFLECTION_PROBE_UPDATE_STATIC].
+
+
@@ -3150,6 +3158,14 @@
If [code]true[/code], computes shadows in the reflection probe. This makes the reflection much slower to compute. Equivalent to [member ReflectionProbe.enable_shadows].
+
+
+
+
+
+ Sets how the roughness mipmaps should be generated in the radiance map of the reflection probe. See [enum ReflectionProbeFilterMode] for options.
+
+
@@ -3214,6 +3230,14 @@
Sets how often the reflection probe updates. Can either be once or every frame. See [enum ReflectionProbeUpdateMode] for options.
+
+
+
+
+
+ Sets how fast the reflection probe updates. Can either be once or every frame. See [enum ReflectionProbeUpdateSlicing] for options.
+
+
@@ -4734,11 +4758,38 @@
Represents the size of the [enum ShadowQuality] enum.
- Reflection probe will update reflections once and then stop.
+ Reflection probe will update reflections once and then stop. Reflection probe will update after moving.
Reflection probe will update each frame. This mode is necessary to capture moving objects.
+
+ Reflection probe will update reflections once and then stop. Reflection probe will [b]not[/b] update after moving.
+
+
+ [constant REFLECTION_PROBE_UPDATE_SLICING_1_FACE_PER_FRAME] is used when the update mode is [constant REFLECTION_PROBE_UPDATE_ONCE] or [constant REFLECTION_PROBE_UPDATE_STATIC] and [constant REFLECTION_PROBE_UPDATE_SLICING_6_FACES_PER_FRAME] is used when the update mode is [constant REFLECTION_PROBE_UPDATE_ALWAYS]. This matches behavior found in Godot 4.3 and prior.
+
+
+ Render 1 face of the cubemap every frame (including post-processing steps when using [constant REFLECTION_PROBE_FILTER_MODE_INCREMENTAL]). For example, if using [constant REFLECTION_PROBE_FILTER_MODE_REALTIME] in a project that renders at 60 FPS, reflections will appear to update at 10 FPS. Low performance cost.
+
+
+ Render 2 faces of the cubemap every frame (including post-processing steps when using [constant REFLECTION_PROBE_FILTER_MODE_INCREMENTAL]). For example, if using [constant REFLECTION_PROBE_FILTER_MODE_REALTIME] in a project that renders at 60 FPS, reflections will appear to update at 20 FPS. Medium performance cost.
+
+
+ Render 3 faces of the cubemap every frame (including post-processing steps when using [constant REFLECTION_PROBE_FILTER_MODE_INCREMENTAL]). For example, if using [constant REFLECTION_PROBE_FILTER_MODE_REALTIME] in a project that renders at 60 FPS, reflections will appear to update at 30 FPS. High performance cost.
+
+
+ Render 6 faces of the cubemap every frame (including post-processing steps when using [constant REFLECTION_PROBE_FILTER_MODE_INCREMENTAL]). For example, if using [constant REFLECTION_PROBE_FILTER_MODE_REALTIME] in a project that renders at 60 FPS, reflections will appear to update at 60 FPS. Very high performance cost.
+
+
+ [constant REFLECTION_PROBE_FILTER_MODE_INCREMENTAL] is used when the update mode is [constant REFLECTION_PROBE_UPDATE_ONCE] or [constant REFLECTION_PROBE_UPDATE_STATIC] and [constant REFLECTION_PROBE_FILTER_MODE_REALTIME] is used when the update mode is [constant REFLECTION_PROBE_UPDATE_ALWAYS]. This matches behavior found in Godot 4.3 and prior.
+
+
+ Uses high-quality importance sampling to process the radiance map over several frames. The number of frames is determined by [member ProjectSettings.rendering/reflections/sky_reflections/roughness_layers]. Use this when you need highest quality radiance maps, but have a reflection probe that updates slowly. Equivalent to [constant Sky.PROCESS_MODE_INCREMENTAL].
+
+
+ Uses the fast filtering algorithm to process the radiance map. In general, this results in lower quality, but substantially faster run times. Equivalent to [constant Sky.PROCESS_MODE_REALTIME].
+
Do not apply any ambient lighting inside the reflection probe's box defined by its size.
diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp
index 9b976c220683..7fd1107b0953 100644
--- a/drivers/gles3/storage/light_storage.cpp
+++ b/drivers/gles3/storage/light_storage.cpp
@@ -447,6 +447,22 @@ void LightStorage::reflection_probe_set_update_mode(RID p_probe, RS::ReflectionP
reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
}
+void LightStorage::reflection_probe_set_update_slicing(RID p_probe, RS::ReflectionProbeUpdateSlicing p_slicing) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->update_slicing = p_slicing;
+ reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
+}
+
+void LightStorage::reflection_probe_set_filter_mode(RID p_probe, RS::ReflectionProbeFilterMode p_mode) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->filter_mode = p_mode;
+ reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
+}
+
void LightStorage::reflection_probe_set_intensity(RID p_probe, float p_intensity) {
ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
ERR_FAIL_NULL(reflection_probe);
@@ -563,6 +579,20 @@ RS::ReflectionProbeUpdateMode LightStorage::reflection_probe_get_update_mode(RID
return reflection_probe->update_mode;
}
+RS::ReflectionProbeUpdateSlicing LightStorage::reflection_probe_get_update_slicing(RID p_probe) const {
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL_V(reflection_probe, RenderingServer::REFLECTION_PROBE_UPDATE_SLICING_AUTOMATIC);
+
+ return reflection_probe->update_slicing;
+}
+
+RS::ReflectionProbeFilterMode LightStorage::reflection_probe_get_filter_mode(RID p_probe) const {
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL_V(reflection_probe, RenderingServer::REFLECTION_PROBE_FILTER_MODE_AUTOMATIC);
+
+ return reflection_probe->filter_mode;
+}
+
uint32_t LightStorage::reflection_probe_get_cull_mask(RID p_probe) const {
const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
ERR_FAIL_NULL_V(reflection_probe, 0);
@@ -620,6 +650,13 @@ float LightStorage::reflection_probe_get_mesh_lod_threshold(RID p_probe) const {
return reflection_probe->mesh_lod_threshold;
}
+void LightStorage::reflection_probe_queue_update(RID p_probe) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
+}
+
Dependency *LightStorage::reflection_probe_get_dependency(RID p_probe) const {
ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
ERR_FAIL_NULL_V(reflection_probe, nullptr);
diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h
index ed00dd235f36..3d250f389d07 100644
--- a/drivers/gles3/storage/light_storage.h
+++ b/drivers/gles3/storage/light_storage.h
@@ -115,6 +115,8 @@ struct LightInstance {
struct ReflectionProbe {
RS::ReflectionProbeUpdateMode update_mode = RS::REFLECTION_PROBE_UPDATE_ONCE;
+ RS::ReflectionProbeUpdateSlicing update_slicing = RS::REFLECTION_PROBE_UPDATE_SLICING_AUTOMATIC;
+ RS::ReflectionProbeFilterMode filter_mode = RS::REFLECTION_PROBE_FILTER_MODE_AUTOMATIC;
int resolution = 256;
float intensity = 1.0;
RS::ReflectionProbeAmbientMode ambient_mode = RS::REFLECTION_PROBE_AMBIENT_ENVIRONMENT;
@@ -638,6 +640,8 @@ class LightStorage : public RendererLightStorage {
virtual void reflection_probe_free(RID p_rid) override;
virtual void reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) override;
+ virtual void reflection_probe_set_update_slicing(RID p_probe, RS::ReflectionProbeUpdateSlicing p_slicing) override;
+ virtual void reflection_probe_set_filter_mode(RID p_probe, RS::ReflectionProbeFilterMode p_mode) override;
virtual void reflection_probe_set_intensity(RID p_probe, float p_intensity) override;
virtual void reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) override;
virtual void reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) override;
@@ -653,9 +657,12 @@ class LightStorage : public RendererLightStorage {
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;
virtual float reflection_probe_get_mesh_lod_threshold(RID p_probe) const override;
+ virtual void reflection_probe_queue_update(RID p_probe) override;
virtual AABB reflection_probe_get_aabb(RID p_probe) const override;
virtual RS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const override;
+ virtual RS::ReflectionProbeUpdateSlicing reflection_probe_get_update_slicing(RID p_probe) const override;
+ virtual RS::ReflectionProbeFilterMode reflection_probe_get_filter_mode(RID p_probe) const override;
virtual uint32_t reflection_probe_get_cull_mask(RID p_probe) const override;
virtual uint32_t reflection_probe_get_reflection_mask(RID p_probe) const override;
virtual Vector3 reflection_probe_get_size(RID p_probe) const override;
diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp
index b6ec55286dd0..c645e3cfd912 100644
--- a/scene/3d/reflection_probe.cpp
+++ b/scene/3d/reflection_probe.cpp
@@ -183,6 +183,28 @@ ReflectionProbe::UpdateMode ReflectionProbe::get_update_mode() const {
return update_mode;
}
+void ReflectionProbe::set_update_slicing(UpdateSlicing p_slicing) {
+ update_slicing = p_slicing;
+ RS::get_singleton()->reflection_probe_set_update_slicing(probe, RS::ReflectionProbeUpdateSlicing(p_slicing));
+}
+
+ReflectionProbe::UpdateSlicing ReflectionProbe::get_update_slicing() const {
+ return update_slicing;
+}
+
+void ReflectionProbe::set_filter_mode(FilterMode p_mode) {
+ filter_mode = p_mode;
+ RS::get_singleton()->reflection_probe_set_filter_mode(probe, RS::ReflectionProbeFilterMode(p_mode));
+}
+
+ReflectionProbe::FilterMode ReflectionProbe::get_filter_mode() const {
+ return filter_mode;
+}
+
+void ReflectionProbe::queue_update() {
+ RS::get_singleton()->reflection_probe_queue_update(probe);
+}
+
AABB ReflectionProbe::get_aabb() const {
AABB aabb;
aabb.position = -origin_offset;
@@ -241,7 +263,17 @@ void ReflectionProbe::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_update_mode", "mode"), &ReflectionProbe::set_update_mode);
ClassDB::bind_method(D_METHOD("get_update_mode"), &ReflectionProbe::get_update_mode);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Once (Fast),Always (Slow)"), "set_update_mode", "get_update_mode");
+ ClassDB::bind_method(D_METHOD("set_update_slicing", "slicing"), &ReflectionProbe::set_update_slicing);
+ ClassDB::bind_method(D_METHOD("get_update_slicing"), &ReflectionProbe::get_update_slicing);
+
+ ClassDB::bind_method(D_METHOD("set_filter_mode", "mode"), &ReflectionProbe::set_filter_mode);
+ ClassDB::bind_method(D_METHOD("get_filter_mode"), &ReflectionProbe::get_filter_mode);
+
+ ClassDB::bind_method(D_METHOD("queue_update"), &ReflectionProbe::queue_update);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Once when Changed (Average),Always (Slow),Static (Fast)"), "set_update_mode", "get_update_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "update_slicing", PROPERTY_HINT_ENUM, "Automatic,1 Face per Frame (Faster but More Latency),2 Faces per Frame,3 Faces per Frame,6 Faces per Frame (Slower but Less Latency)"), "set_update_slicing", "get_update_slicing");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "filter_mode", PROPERTY_HINT_ENUM, "Automatic,High-Quality Incremental,Real-Time"), "set_filter_mode", "get_filter_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "intensity", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_intensity", "get_intensity");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,16384,0.1,or_greater,exp,suffix:m"), "set_max_distance", "get_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size");
@@ -260,6 +292,17 @@ void ReflectionProbe::_bind_methods() {
BIND_ENUM_CONSTANT(UPDATE_ONCE);
BIND_ENUM_CONSTANT(UPDATE_ALWAYS);
+ BIND_ENUM_CONSTANT(UPDATE_STATIC);
+
+ BIND_ENUM_CONSTANT(UPDATE_SLICING_AUTOMATIC);
+ BIND_ENUM_CONSTANT(UPDATE_SLICING_1_FACE_PER_FRAME);
+ BIND_ENUM_CONSTANT(UPDATE_SLICING_2_FACES_PER_FRAME);
+ BIND_ENUM_CONSTANT(UPDATE_SLICING_3_FACES_PER_FRAME);
+ BIND_ENUM_CONSTANT(UPDATE_SLICING_6_FACES_PER_FRAME);
+
+ BIND_ENUM_CONSTANT(FILTER_MODE_AUTOMATIC);
+ BIND_ENUM_CONSTANT(FILTER_MODE_INCREMENTAL);
+ BIND_ENUM_CONSTANT(FILTER_MODE_REALTIME);
BIND_ENUM_CONSTANT(AMBIENT_DISABLED);
BIND_ENUM_CONSTANT(AMBIENT_ENVIRONMENT);
diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h
index 722129422894..8de167eb7f00 100644
--- a/scene/3d/reflection_probe.h
+++ b/scene/3d/reflection_probe.h
@@ -40,6 +40,21 @@ class ReflectionProbe : public VisualInstance3D {
enum UpdateMode {
UPDATE_ONCE,
UPDATE_ALWAYS,
+ UPDATE_STATIC,
+ };
+
+ enum UpdateSlicing {
+ UPDATE_SLICING_AUTOMATIC,
+ UPDATE_SLICING_1_FACE_PER_FRAME,
+ UPDATE_SLICING_2_FACES_PER_FRAME,
+ UPDATE_SLICING_3_FACES_PER_FRAME,
+ UPDATE_SLICING_6_FACES_PER_FRAME,
+ };
+
+ enum FilterMode {
+ FILTER_MODE_AUTOMATIC,
+ FILTER_MODE_INCREMENTAL,
+ FILTER_MODE_REALTIME,
};
enum AmbientMode {
@@ -65,6 +80,8 @@ class ReflectionProbe : public VisualInstance3D {
uint32_t cull_mask = (1 << 20) - 1;
uint32_t reflection_mask = (1 << 20) - 1;
UpdateMode update_mode = UPDATE_ONCE;
+ UpdateSlicing update_slicing = UPDATE_SLICING_AUTOMATIC;
+ FilterMode filter_mode = FILTER_MODE_AUTOMATIC;
protected:
static void _bind_methods();
@@ -120,6 +137,14 @@ class ReflectionProbe : public VisualInstance3D {
void set_update_mode(UpdateMode p_mode);
UpdateMode get_update_mode() const;
+ void set_update_slicing(UpdateSlicing p_slicing);
+ UpdateSlicing get_update_slicing() const;
+
+ void set_filter_mode(FilterMode p_mode);
+ FilterMode get_filter_mode() const;
+
+ void queue_update();
+
virtual AABB get_aabb() const override;
ReflectionProbe();
@@ -128,5 +153,7 @@ class ReflectionProbe : public VisualInstance3D {
VARIANT_ENUM_CAST(ReflectionProbe::AmbientMode);
VARIANT_ENUM_CAST(ReflectionProbe::UpdateMode);
+VARIANT_ENUM_CAST(ReflectionProbe::UpdateSlicing);
+VARIANT_ENUM_CAST(ReflectionProbe::FilterMode);
#endif // REFLECTION_PROBE_H
diff --git a/servers/rendering/dummy/storage/light_storage.h b/servers/rendering/dummy/storage/light_storage.h
index c3b63cdbf63f..2d838eff1af8 100644
--- a/servers/rendering/dummy/storage/light_storage.h
+++ b/servers/rendering/dummy/storage/light_storage.h
@@ -121,6 +121,8 @@ class LightStorage : public RendererLightStorage {
virtual void reflection_probe_free(RID p_rid) override {}
virtual void reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) override {}
+ virtual void reflection_probe_set_update_slicing(RID p_probe, RS::ReflectionProbeUpdateSlicing p_slicing) override {}
+ virtual void reflection_probe_set_filter_mode(RID p_probe, RS::ReflectionProbeFilterMode p_mode) override {}
virtual void reflection_probe_set_intensity(RID p_probe, float p_intensity) override {}
virtual void reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) override {}
virtual void reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) override {}
@@ -136,9 +138,12 @@ class LightStorage : public RendererLightStorage {
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 {}
virtual float reflection_probe_get_mesh_lod_threshold(RID p_probe) const override { return 0.0; }
+ virtual void reflection_probe_queue_update(RID p_probe) override {}
virtual AABB reflection_probe_get_aabb(RID p_probe) const override { return AABB(); }
virtual RS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const override { return RenderingServer::REFLECTION_PROBE_UPDATE_ONCE; }
+ virtual RS::ReflectionProbeUpdateSlicing reflection_probe_get_update_slicing(RID p_probe) const override { return RenderingServer::REFLECTION_PROBE_UPDATE_SLICING_AUTOMATIC; }
+ virtual RS::ReflectionProbeFilterMode reflection_probe_get_filter_mode(RID p_probe) const override { return RenderingServer::REFLECTION_PROBE_FILTER_MODE_AUTOMATIC; }
virtual uint32_t reflection_probe_get_cull_mask(RID p_probe) const override { return 0; }
virtual uint32_t reflection_probe_get_reflection_mask(RID p_probe) const override { return 0; }
virtual Vector3 reflection_probe_get_size(RID p_probe) const override { return Vector3(); }
diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
index b07063cfda28..fbf8808947ca 100644
--- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
@@ -1036,6 +1036,22 @@ void LightStorage::reflection_probe_set_update_mode(RID p_probe, RS::ReflectionP
reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
}
+void LightStorage::reflection_probe_set_update_slicing(RID p_probe, RS::ReflectionProbeUpdateSlicing p_slicing) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->update_slicing = p_slicing;
+ reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
+}
+
+void LightStorage::reflection_probe_set_filter_mode(RID p_probe, RS::ReflectionProbeFilterMode p_mode) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->filter_mode = p_mode;
+ reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
+}
+
void LightStorage::reflection_probe_set_intensity(RID p_probe, float p_intensity) {
ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
ERR_FAIL_NULL(reflection_probe);
@@ -1155,6 +1171,13 @@ void LightStorage::reflection_probe_set_baked_exposure(RID p_probe, float p_expo
reflection_probe->baked_exposure = p_exposure;
}
+void LightStorage::reflection_probe_queue_update(RID p_probe) {
+ ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL(reflection_probe);
+
+ reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
+}
+
AABB LightStorage::reflection_probe_get_aabb(RID p_probe) const {
const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
ERR_FAIL_NULL_V(reflection_probe, AABB());
@@ -1168,11 +1191,43 @@ AABB LightStorage::reflection_probe_get_aabb(RID p_probe) const {
RS::ReflectionProbeUpdateMode LightStorage::reflection_probe_get_update_mode(RID p_probe) const {
const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
- ERR_FAIL_NULL_V(reflection_probe, RS::REFLECTION_PROBE_UPDATE_ALWAYS);
+ ERR_FAIL_NULL_V(reflection_probe, RS::REFLECTION_PROBE_UPDATE_ONCE);
return reflection_probe->update_mode;
}
+RS::ReflectionProbeUpdateSlicing LightStorage::reflection_probe_get_update_slicing(RID p_probe) const {
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL_V(reflection_probe, RS::REFLECTION_PROBE_UPDATE_SLICING_1_FACE_PER_FRAME);
+
+ if (reflection_probe->update_slicing == RS::REFLECTION_PROBE_UPDATE_SLICING_AUTOMATIC) {
+ // Match existing behavior from Godot 4.3 and prior.
+ if (reflection_probe->update_mode == RS::REFLECTION_PROBE_UPDATE_ALWAYS) {
+ return RS::REFLECTION_PROBE_UPDATE_SLICING_6_FACES_PER_FRAME;
+ }
+
+ return RS::REFLECTION_PROBE_UPDATE_SLICING_1_FACE_PER_FRAME;
+ }
+
+ return reflection_probe->update_slicing;
+}
+
+RS::ReflectionProbeFilterMode LightStorage::reflection_probe_get_filter_mode(RID p_probe) const {
+ const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+ ERR_FAIL_NULL_V(reflection_probe, RS::REFLECTION_PROBE_FILTER_MODE_INCREMENTAL);
+
+ if (reflection_probe->filter_mode == RS::REFLECTION_PROBE_FILTER_MODE_AUTOMATIC) {
+ // Match existing behavior from Godot 4.3 and prior.
+ if (reflection_probe->update_mode == RS::REFLECTION_PROBE_UPDATE_ALWAYS) {
+ return RS::REFLECTION_PROBE_FILTER_MODE_REALTIME;
+ }
+
+ return RS::REFLECTION_PROBE_FILTER_MODE_INCREMENTAL;
+ }
+
+ return reflection_probe->filter_mode;
+}
+
uint32_t LightStorage::reflection_probe_get_cull_mask(RID p_probe) const {
const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
ERR_FAIL_NULL_V(reflection_probe, 0);
@@ -1371,7 +1426,11 @@ void LightStorage::reflection_probe_instance_set_transform(RID p_instance, const
ERR_FAIL_NULL(rpi);
rpi->transform = p_transform;
- rpi->dirty = true;
+ if (LightStorage::get_singleton()->reflection_probe_get_update_mode(rpi->probe) != RS::REFLECTION_PROBE_UPDATE_STATIC) {
+ // Moving a ReflectionProbe does not cause it to update if using the Static update mode.
+ // FIXME: This doesn't appear to suffice to prevent updates when the probe is moved.
+ rpi->dirty = true;
+ }
}
bool LightStorage::reflection_probe_has_atlas_index(RID p_instance) {
@@ -1415,6 +1474,11 @@ bool LightStorage::reflection_probe_instance_needs_redraw(RID p_instance) {
ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
ERR_FAIL_NULL_V(rpi, false);
+ if (LightStorage::get_singleton()->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_STATIC) {
+ // FIXME: This doesn't seem to have any effect, even though this line is run when the update mode is Static.
+ return false;
+ }
+
if (rpi->rendering) {
return false;
}
@@ -1451,12 +1515,12 @@ bool LightStorage::reflection_probe_instance_begin_render(RID p_instance, RID p_
RD::get_singleton()->draw_command_begin_label("Reflection probe render");
- if (LightStorage::get_singleton()->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS && atlas->reflection.is_valid() && atlas->size != 256) {
- WARN_PRINT("ReflectionProbes set to UPDATE_ALWAYS must have an atlas size of 256. Please update the atlas size in the ProjectSettings.");
+ if (LightStorage::get_singleton()->reflection_probe_get_filter_mode(rpi->probe) == RS::REFLECTION_PROBE_FILTER_MODE_REALTIME && atlas->reflection.is_valid() && atlas->size != 256) {
+ WARN_PRINT("ReflectionProbes set to the Real-Time filter mode must have an atlas size of 256. Please update the atlas size in the Project Settings.");
reflection_atlas_set_size(p_reflection_atlas, 256, atlas->count);
}
- if (LightStorage::get_singleton()->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS && atlas->reflection.is_valid() && atlas->reflections[0].data.layers[0].mipmaps.size() != 8) {
+ if (LightStorage::get_singleton()->reflection_probe_get_filter_mode(rpi->probe) == RS::REFLECTION_PROBE_FILTER_MODE_REALTIME && atlas->reflection.is_valid() && atlas->reflections[0].data.layers[0].mipmaps.size() != 8) {
// Invalidate reflection atlas, need to regenerate
RD::get_singleton()->free(atlas->reflection);
atlas->reflection = RID();
@@ -1473,7 +1537,7 @@ bool LightStorage::reflection_probe_instance_begin_render(RID p_instance, RID p_
if (atlas->reflection.is_null()) {
int mipmaps = MIN(RendererSceneRenderRD::get_singleton()->get_sky()->roughness_layers, Image::get_image_required_mipmaps(atlas->size, atlas->size, Image::FORMAT_RGBAH) + 1);
- mipmaps = LightStorage::get_singleton()->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS ? 8 : mipmaps; // always use 8 mipmaps with real time filtering
+ mipmaps = LightStorage::get_singleton()->reflection_probe_get_filter_mode(rpi->probe) == RS::REFLECTION_PROBE_FILTER_MODE_REALTIME ? 8 : mipmaps; // always use 8 mipmaps with real time filtering
{
//reflection atlas was unused, create:
RD::TextureFormat tf;
@@ -1496,8 +1560,9 @@ bool LightStorage::reflection_probe_instance_begin_render(RID p_instance, RID p_
atlas->depth_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
}
atlas->reflections.resize(atlas->count);
+
for (int i = 0; i < atlas->count; i++) {
- atlas->reflections.write[i].data.update_reflection_data(atlas->size, mipmaps, false, atlas->reflection, i * 6, LightStorage::get_singleton()->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS, RendererSceneRenderRD::get_singleton()->get_sky()->roughness_layers, RendererSceneRenderRD::get_singleton()->_render_buffers_get_color_format());
+ atlas->reflections.write[i].data.update_reflection_data(atlas->size, mipmaps, false, atlas->reflection, i * 6, LightStorage::get_singleton()->reflection_probe_get_filter_mode(rpi->probe) == RS::REFLECTION_PROBE_FILTER_MODE_REALTIME, RendererSceneRenderRD::get_singleton()->get_sky()->roughness_layers, RendererSceneRenderRD::get_singleton()->_render_buffers_get_color_format());
for (int j = 0; j < 6; j++) {
atlas->reflections.write[i].fbs[j] = RendererSceneRenderRD::get_singleton()->reflection_probe_create_framebuffer(atlas->reflections.write[i].data.layers[0].mipmaps[0].views[j], atlas->depth_buffer);
}
@@ -1566,8 +1631,8 @@ bool LightStorage::reflection_probe_instance_postprocess_step(RID p_instance) {
return false;
}
- if (LightStorage::get_singleton()->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS) {
- // Using real time reflections, all roughness is done in one step
+ if (LightStorage::get_singleton()->reflection_probe_get_filter_mode(rpi->probe) == RS::REFLECTION_PROBE_FILTER_MODE_REALTIME) {
+ // Using real-time filter mode, all roughness is done in one step.
atlas->reflections.write[rpi->atlas_index].data.create_reflection_fast_filter(false);
rpi->rendering = false;
rpi->processing_side = 0;
diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h
index 1db58d72f9d1..5a5f78d5a1c9 100644
--- a/servers/rendering/renderer_rd/storage_rd/light_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h
@@ -220,6 +220,8 @@ class LightStorage : public RendererLightStorage {
struct ReflectionProbe {
RS::ReflectionProbeUpdateMode update_mode = RS::REFLECTION_PROBE_UPDATE_ONCE;
+ RS::ReflectionProbeUpdateSlicing update_slicing = RS::REFLECTION_PROBE_UPDATE_SLICING_AUTOMATIC;
+ RS::ReflectionProbeFilterMode filter_mode = RS::REFLECTION_PROBE_FILTER_MODE_AUTOMATIC;
int resolution = 256;
float intensity = 1.0;
RS::ReflectionProbeAmbientMode ambient_mode = RS::REFLECTION_PROBE_AMBIENT_ENVIRONMENT;
@@ -810,6 +812,8 @@ class LightStorage : public RendererLightStorage {
virtual void reflection_probe_free(RID p_rid) override;
virtual void reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) override;
+ virtual void reflection_probe_set_update_slicing(RID p_probe, RS::ReflectionProbeUpdateSlicing p_slicing) override;
+ virtual void reflection_probe_set_filter_mode(RID p_probe, RS::ReflectionProbeFilterMode p_mode) override;
virtual void reflection_probe_set_intensity(RID p_probe, float p_intensity) override;
virtual void reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) override;
virtual void reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) override;
@@ -824,11 +828,16 @@ class LightStorage : public RendererLightStorage {
virtual void reflection_probe_set_reflection_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;
+ virtual void reflection_probe_queue_update(RID p_probe) override;
void reflection_probe_set_baked_exposure(RID p_probe, float p_exposure);
virtual AABB reflection_probe_get_aabb(RID p_probe) const override;
virtual RS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const override;
+ // If using the Automatic update slicing, the value that is returned is updated to match the ReflectionProbe's update mode.
+ virtual RS::ReflectionProbeUpdateSlicing reflection_probe_get_update_slicing(RID p_probe) const override;
+ // If using the Automatic filter mode, the value that is returned is updated to match the ReflectionProbe's update mode.
+ virtual RS::ReflectionProbeFilterMode reflection_probe_get_filter_mode(RID p_probe) const override;
virtual uint32_t reflection_probe_get_cull_mask(RID p_probe) const override;
virtual uint32_t reflection_probe_get_reflection_mask(RID p_probe) const override;
virtual Vector3 reflection_probe_get_size(RID p_probe) const override;
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index 43abb22e3d8e..fa84047f72f0 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -3732,31 +3732,60 @@ void RendererSceneCull::render_probes() {
SelfList *next = ref_probe->next();
RID base = ref_probe->self()->owner->base;
- switch (RSG::light_storage->reflection_probe_get_update_mode(base)) {
- case RS::REFLECTION_PROBE_UPDATE_ONCE: {
+ switch (RSG::light_storage->reflection_probe_get_update_slicing(base)) {
+ case RS::REFLECTION_PROBE_UPDATE_SLICING_6_FACES_PER_FRAME: {
+ int step = 0;
+ bool done = false;
+ while (!done) {
+ done = _render_reflection_probe_step(ref_probe->self()->owner, step);
+ step++;
+ }
+
+ done_list.push_back(ref_probe);
+ } break;
+ default: {
if (busy) { // Already rendering something.
break;
}
- bool done = _render_reflection_probe_step(ref_probe->self()->owner, ref_probe->self()->render_step);
+ int steps_per_frame = 1;
+ switch (RSG::light_storage->reflection_probe_get_update_slicing(base)) {
+ case RS::REFLECTION_PROBE_UPDATE_SLICING_AUTOMATIC:
+ // Handled to silence warning, but never returned by this method.
+ break;
+ case RS::REFLECTION_PROBE_UPDATE_SLICING_1_FACE_PER_FRAME:
+ steps_per_frame = 1;
+ break;
+ case RS::REFLECTION_PROBE_UPDATE_SLICING_2_FACES_PER_FRAME:
+ // FIXME: Not rendering correctly, some faces never get updated.
+ steps_per_frame = 2;
+ break;
+ case RS::REFLECTION_PROBE_UPDATE_SLICING_3_FACES_PER_FRAME:
+ // FIXME: Not rendering correctly, some faces never get updated.
+ steps_per_frame = 3;
+ break;
+ case RS::REFLECTION_PROBE_UPDATE_SLICING_6_FACES_PER_FRAME:
+ // Handled to silence warning, but never reached as it's handled above.
+ steps_per_frame = 6;
+ break;
+ }
+
+ bool done = false;
+ for (int i = 0; i < steps_per_frame; i++) {
+ done = _render_reflection_probe_step(ref_probe->self()->owner, ref_probe->self()->render_step + i);
+ if (done) {
+ break;
+ }
+ }
+
if (done) {
done_list.push_back(ref_probe);
} else {
- ref_probe->self()->render_step++;
+ ref_probe->self()->render_step += steps_per_frame;
}
busy = true; // Do not render another one of this kind.
} break;
- case RS::REFLECTION_PROBE_UPDATE_ALWAYS: {
- int step = 0;
- bool done = false;
- while (!done) {
- done = _render_reflection_probe_step(ref_probe->self()->owner, step);
- step++;
- }
-
- done_list.push_back(ref_probe);
- } break;
}
ref_probe = next;
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 2dcdc3f2543d..34386412793e 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -414,6 +414,8 @@ class RenderingServerDefault : public RenderingServer {
FUNCRIDSPLIT(reflection_probe)
FUNC2(reflection_probe_set_update_mode, RID, ReflectionProbeUpdateMode)
+ FUNC2(reflection_probe_set_update_slicing, RID, ReflectionProbeUpdateSlicing)
+ FUNC2(reflection_probe_set_filter_mode, RID, ReflectionProbeFilterMode)
FUNC2(reflection_probe_set_intensity, RID, float)
FUNC2(reflection_probe_set_ambient_color, RID, const Color &)
FUNC2(reflection_probe_set_ambient_energy, RID, float)
@@ -428,6 +430,7 @@ class RenderingServerDefault : public RenderingServer {
FUNC2(reflection_probe_set_reflection_mask, RID, uint32_t)
FUNC2(reflection_probe_set_resolution, RID, int)
FUNC2(reflection_probe_set_mesh_lod_threshold, RID, float)
+ FUNC1(reflection_probe_queue_update, RID)
/* LIGHTMAP */
diff --git a/servers/rendering/storage/light_storage.h b/servers/rendering/storage/light_storage.h
index 6a0adfa59662..29b21b1a4ce8 100644
--- a/servers/rendering/storage/light_storage.h
+++ b/servers/rendering/storage/light_storage.h
@@ -107,6 +107,8 @@ class RendererLightStorage {
virtual void reflection_probe_free(RID p_rid) = 0;
virtual void reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) = 0;
+ virtual void reflection_probe_set_update_slicing(RID p_probe, RS::ReflectionProbeUpdateSlicing p_slicing) = 0;
+ virtual void reflection_probe_set_filter_mode(RID p_probe, RS::ReflectionProbeFilterMode p_mode) = 0;
virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) = 0;
virtual void reflection_probe_set_intensity(RID p_probe, float p_intensity) = 0;
virtual void reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) = 0;
@@ -121,9 +123,12 @@ class RendererLightStorage {
virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) = 0;
virtual void reflection_probe_set_reflection_mask(RID p_probe, uint32_t p_layers) = 0;
virtual void reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) = 0;
+ virtual void reflection_probe_queue_update(RID p_probe) = 0;
virtual AABB reflection_probe_get_aabb(RID p_probe) const = 0;
virtual RS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const = 0;
+ virtual RS::ReflectionProbeUpdateSlicing reflection_probe_get_update_slicing(RID p_probe) const = 0;
+ virtual RS::ReflectionProbeFilterMode reflection_probe_get_filter_mode(RID p_probe) const = 0;
virtual uint32_t reflection_probe_get_cull_mask(RID p_probe) const = 0;
virtual uint32_t reflection_probe_get_reflection_mask(RID p_probe) const = 0;
virtual Vector3 reflection_probe_get_size(RID p_probe) const = 0;
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 5c8508029818..e12e37d67b50 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2556,6 +2556,8 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("reflection_probe_create"), &RenderingServer::reflection_probe_create);
ClassDB::bind_method(D_METHOD("reflection_probe_set_update_mode", "probe", "mode"), &RenderingServer::reflection_probe_set_update_mode);
+ ClassDB::bind_method(D_METHOD("reflection_probe_set_update_slicing", "probe", "slicing"), &RenderingServer::reflection_probe_set_update_slicing);
+ ClassDB::bind_method(D_METHOD("reflection_probe_set_filter_mode", "probe", "mode"), &RenderingServer::reflection_probe_set_filter_mode);
ClassDB::bind_method(D_METHOD("reflection_probe_set_intensity", "probe", "intensity"), &RenderingServer::reflection_probe_set_intensity);
ClassDB::bind_method(D_METHOD("reflection_probe_set_ambient_mode", "probe", "mode"), &RenderingServer::reflection_probe_set_ambient_mode);
ClassDB::bind_method(D_METHOD("reflection_probe_set_ambient_color", "probe", "color"), &RenderingServer::reflection_probe_set_ambient_color);
@@ -2570,9 +2572,21 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("reflection_probe_set_reflection_mask", "probe", "layers"), &RenderingServer::reflection_probe_set_reflection_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);
+ ClassDB::bind_method(D_METHOD("reflection_probe_queue_update", "probe"), &RenderingServer::reflection_probe_queue_update);
BIND_ENUM_CONSTANT(REFLECTION_PROBE_UPDATE_ONCE);
BIND_ENUM_CONSTANT(REFLECTION_PROBE_UPDATE_ALWAYS);
+ BIND_ENUM_CONSTANT(REFLECTION_PROBE_UPDATE_STATIC);
+
+ BIND_ENUM_CONSTANT(REFLECTION_PROBE_UPDATE_SLICING_AUTOMATIC);
+ BIND_ENUM_CONSTANT(REFLECTION_PROBE_UPDATE_SLICING_1_FACE_PER_FRAME);
+ BIND_ENUM_CONSTANT(REFLECTION_PROBE_UPDATE_SLICING_2_FACES_PER_FRAME);
+ BIND_ENUM_CONSTANT(REFLECTION_PROBE_UPDATE_SLICING_3_FACES_PER_FRAME);
+ BIND_ENUM_CONSTANT(REFLECTION_PROBE_UPDATE_SLICING_6_FACES_PER_FRAME);
+
+ BIND_ENUM_CONSTANT(REFLECTION_PROBE_FILTER_MODE_AUTOMATIC);
+ BIND_ENUM_CONSTANT(REFLECTION_PROBE_FILTER_MODE_INCREMENTAL);
+ BIND_ENUM_CONSTANT(REFLECTION_PROBE_FILTER_MODE_REALTIME);
BIND_ENUM_CONSTANT(REFLECTION_PROBE_AMBIENT_DISABLED);
BIND_ENUM_CONSTANT(REFLECTION_PROBE_AMBIENT_ENVIRONMENT);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 878b02eaf136..29ac432f1953 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -603,9 +603,26 @@ class RenderingServer : public Object {
enum ReflectionProbeUpdateMode {
REFLECTION_PROBE_UPDATE_ONCE,
REFLECTION_PROBE_UPDATE_ALWAYS,
+ REFLECTION_PROBE_UPDATE_STATIC,
+ };
+
+ enum ReflectionProbeUpdateSlicing {
+ REFLECTION_PROBE_UPDATE_SLICING_AUTOMATIC,
+ REFLECTION_PROBE_UPDATE_SLICING_1_FACE_PER_FRAME,
+ REFLECTION_PROBE_UPDATE_SLICING_2_FACES_PER_FRAME,
+ REFLECTION_PROBE_UPDATE_SLICING_3_FACES_PER_FRAME,
+ REFLECTION_PROBE_UPDATE_SLICING_6_FACES_PER_FRAME,
+ };
+
+ enum ReflectionProbeFilterMode {
+ REFLECTION_PROBE_FILTER_MODE_AUTOMATIC,
+ REFLECTION_PROBE_FILTER_MODE_INCREMENTAL,
+ REFLECTION_PROBE_FILTER_MODE_REALTIME,
};
virtual void reflection_probe_set_update_mode(RID p_probe, ReflectionProbeUpdateMode p_mode) = 0;
+ virtual void reflection_probe_set_update_slicing(RID p_probe, ReflectionProbeUpdateSlicing p_slicing) = 0;
+ virtual void reflection_probe_set_filter_mode(RID p_probe, ReflectionProbeFilterMode p_mode) = 0;
virtual void reflection_probe_set_intensity(RID p_probe, float p_intensity) = 0;
enum ReflectionProbeAmbientMode {
@@ -627,6 +644,7 @@ class RenderingServer : public Object {
virtual void reflection_probe_set_reflection_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;
+ virtual void reflection_probe_queue_update(RID p_probe) = 0;
/* DECAL API */
@@ -1823,6 +1841,8 @@ VARIANT_ENUM_CAST(RenderingServer::LightDirectionalShadowMode);
VARIANT_ENUM_CAST(RenderingServer::LightDirectionalSkyMode);
VARIANT_ENUM_CAST(RenderingServer::LightProjectorFilter);
VARIANT_ENUM_CAST(RenderingServer::ReflectionProbeUpdateMode);
+VARIANT_ENUM_CAST(RenderingServer::ReflectionProbeUpdateSlicing);
+VARIANT_ENUM_CAST(RenderingServer::ReflectionProbeFilterMode);
VARIANT_ENUM_CAST(RenderingServer::ReflectionProbeAmbientMode);
VARIANT_ENUM_CAST(RenderingServer::VoxelGIQuality);
VARIANT_ENUM_CAST(RenderingServer::DecalTexture);