diff --git a/doc/classes/GeometryInstance3D.xml b/doc/classes/GeometryInstance3D.xml
index d728ab4f6c85..636f826e6b51 100644
--- a/doc/classes/GeometryInstance3D.xml
+++ b/doc/classes/GeometryInstance3D.xml
@@ -61,6 +61,9 @@
The material override for the whole geometry.
If a material is assigned to this property, it will be used instead of any material set in any material slot of the mesh.
+
+ Sets the render mode for shadow maps.
+
The transparency applied to the whole geometry (as a multiplier of the materials' existing transparency). [code]0.0[/code] is fully opaque, while [code]1.0[/code] is fully transparent. Values greater than [code]0.0[/code] (exclusive) will force the geometry's materials to go through the transparent pipeline, which is slower to render and can exhibit rendering issues due to incorrect transparency sorting. However, unlike using a transparent material, setting [member transparency] to a value greater than [code]0.0[/code] (exclusive) will [i]not[/i] disable shadow rendering.
In spatial shaders, [code]1.0 - transparency[/code] is set as the default value of the [code]ALPHA[/code] built-in.
@@ -100,6 +103,16 @@
Will only show the shadows casted from this object.
In other words, the actual mesh will not be visible, only the shadows casted from the mesh will be.
+
+ Let Godot determine whether this instance is dynamic. Objects are determined to be dynamic when altered after being rendered to a static shadow map.
+
+
+ This instance is rendered to static shadow maps. This speeds up shadow map rendering for objects that do not move.
+ Note: if the objects position or orientation changes, its shadow will not be updated.
+
+
+ This instance is rendered to dynamic shadow maps. You must select this if the object changes position or orientation.
+
Disabled global illumination mode. Use for dynamic objects that do not contribute to global illumination (such as characters). When using [VoxelGI] and SDFGI, the geometry will [i]receive[/i] indirect lighting and reflections but the geometry will not be considered in GI baking. When using [LightmapGI], the object will receive indirect lighting using lightmap probes instead of using the baked lightmap texture.
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 40a475851dfd..043a8d028d42 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -2456,6 +2456,8 @@
Lower-end override for [member rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality] on mobile devices, due to performance concerns or driver support.
+
+
Enables the use of physically based units for light sources. Physically based units tend to be much larger than the arbitrary units used by Godot, but they can be used to match lighting within Godot to real-world lighting. Due to the large dynamic range of lighting conditions present in nature, Godot bakes exposure into the various lighting quantities before rendering. Most light sources bake exposure automatically at run time based on the active [CameraAttributes] resource, but [LightmapGI] and [VoxelGI] require a [CameraAttributes] resource to be set at bake time to reduce the dynamic range. At run time, Godot will automatically reconcile the baked exposure with the active exposure to ensure lighting remains consistent.
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index bb4a0f8a1622..1b7aa347ad81 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -1679,6 +1679,14 @@
Sets the per-instance shader uniform on the specified 3D geometry instance. Equivalent to [method GeometryInstance3D.set_instance_shader_parameter].
+
+
+
+
+
+ Sets the shadow mode on the specified 3D geometry instance.
+
+
@@ -5021,6 +5029,15 @@
Only render the shadows from the object. The object itself will not be drawn.
+
+ Shadow mode is automatically determined by changes to an instance.
+
+
+ Instance will be rendered to a static shadow map if applicable.
+
+
+ Instance will be rendered to a dynamic shadow map if applicable.
+
Disable visibility range fading for the given instance.
diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h
index 0f8120be1552..cffe5c411055 100644
--- a/drivers/gles3/storage/light_storage.h
+++ b/drivers/gles3/storage/light_storage.h
@@ -419,6 +419,7 @@ class LightStorage : public RendererLightStorage {
/* SHADOW ATLAS API */
+ virtual bool shadow_atlas_get_split_shadows() const override { return false; }
virtual RID shadow_atlas_create() override;
virtual void shadow_atlas_free(RID p_atlas) override;
virtual void shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits = true) override;
diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp
index 8026b12c2bc7..38fb1bef6bf0 100644
--- a/scene/3d/visual_instance_3d.cpp
+++ b/scene/3d/visual_instance_3d.cpp
@@ -331,6 +331,26 @@ GeometryInstance3D::ShadowCastingSetting GeometryInstance3D::get_cast_shadows_se
return shadow_casting_setting;
}
+void GeometryInstance3D::set_shadow_render_setting(ShadowRenderSetting p_shadow_render_setting) {
+ shadow_render_setting = p_shadow_render_setting;
+
+ switch (p_shadow_render_setting) {
+ case SHADOW_RENDER_STATIC_MAP: {
+ RS::get_singleton()->instance_geometry_set_shadow_mode(get_instance(), RS::SHADOW_MODE_STATIC);
+ } break;
+ case SHADOW_RENDER_DYNAMIC_MAP: {
+ RS::get_singleton()->instance_geometry_set_shadow_mode(get_instance(), RS::SHADOW_MODE_DYNAMIC);
+ } break;
+ default: {
+ RS::get_singleton()->instance_geometry_set_shadow_mode(get_instance(), RS::SHADOW_MODE_AUTO);
+ } break;
+ }
+}
+
+GeometryInstance3D::ShadowRenderSetting GeometryInstance3D::get_shadow_render_setting() const {
+ return shadow_render_setting;
+}
+
void GeometryInstance3D::set_extra_cull_margin(float p_margin) {
ERR_FAIL_COND(p_margin < 0);
extra_cull_margin = p_margin;
@@ -459,6 +479,9 @@ void GeometryInstance3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_cast_shadows_setting", "shadow_casting_setting"), &GeometryInstance3D::set_cast_shadows_setting);
ClassDB::bind_method(D_METHOD("get_cast_shadows_setting"), &GeometryInstance3D::get_cast_shadows_setting);
+ ClassDB::bind_method(D_METHOD("set_shadow_render_setting", "shadow_render_setting"), &GeometryInstance3D::set_shadow_render_setting);
+ ClassDB::bind_method(D_METHOD("get_shadow_render_setting"), &GeometryInstance3D::get_shadow_render_setting);
+
ClassDB::bind_method(D_METHOD("set_lod_bias", "bias"), &GeometryInstance3D::set_lod_bias);
ClassDB::bind_method(D_METHOD("get_lod_bias"), &GeometryInstance3D::get_lod_bias);
@@ -505,6 +528,7 @@ void GeometryInstance3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_overlay", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE), "set_material_overlay", "get_material_overlay");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "transparency", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_transparency", "get_transparency");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows_setting", "get_cast_shadows_setting");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "render_shadow", PROPERTY_HINT_ENUM, "Auto,Static,Dynamic"), "set_shadow_render_setting", "get_shadow_render_setting");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01,suffix:m"), "set_extra_cull_margin", "get_extra_cull_margin");
ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_custom_aabb", "get_custom_aabb");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_bias", PROPERTY_HINT_RANGE, "0.001,128,0.001"), "set_lod_bias", "get_lod_bias");
@@ -526,6 +550,10 @@ void GeometryInstance3D::_bind_methods() {
BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_DOUBLE_SIDED);
BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_SHADOWS_ONLY);
+ BIND_ENUM_CONSTANT(SHADOW_RENDER_AUTO);
+ BIND_ENUM_CONSTANT(SHADOW_RENDER_STATIC_MAP);
+ BIND_ENUM_CONSTANT(SHADOW_RENDER_DYNAMIC_MAP);
+
BIND_ENUM_CONSTANT(GI_MODE_DISABLED);
BIND_ENUM_CONSTANT(GI_MODE_STATIC);
BIND_ENUM_CONSTANT(GI_MODE_DYNAMIC);
diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h
index ef0f7966e218..af3e5f926cf5 100644
--- a/scene/3d/visual_instance_3d.h
+++ b/scene/3d/visual_instance_3d.h
@@ -91,6 +91,13 @@ class GeometryInstance3D : public VisualInstance3D {
SHADOW_CASTING_SETTING_SHADOWS_ONLY = RS::SHADOW_CASTING_SETTING_SHADOWS_ONLY
};
+ enum ShadowRenderSetting {
+ SHADOW_RENDER_AUTO,
+ SHADOW_RENDER_STATIC_MAP,
+ SHADOW_RENDER_DYNAMIC_MAP,
+ SHADOW_RENDER_MAX
+ };
+
enum GIMode {
GI_MODE_DISABLED,
GI_MODE_STATIC,
@@ -113,6 +120,7 @@ class GeometryInstance3D : public VisualInstance3D {
private:
ShadowCastingSetting shadow_casting_setting = SHADOW_CASTING_SETTING_ON;
+ ShadowRenderSetting shadow_render_setting = SHADOW_RENDER_AUTO;
Ref material_override;
Ref material_overlay;
@@ -149,6 +157,9 @@ class GeometryInstance3D : public VisualInstance3D {
void set_cast_shadows_setting(ShadowCastingSetting p_shadow_casting_setting);
ShadowCastingSetting get_cast_shadows_setting() const;
+ void set_shadow_render_setting(ShadowRenderSetting p_shadow_render_setting);
+ ShadowRenderSetting get_shadow_render_setting() const;
+
void set_transparency(float p_transparency);
float get_transparency() const;
@@ -200,6 +211,7 @@ class GeometryInstance3D : public VisualInstance3D {
};
VARIANT_ENUM_CAST(GeometryInstance3D::ShadowCastingSetting);
+VARIANT_ENUM_CAST(GeometryInstance3D::ShadowRenderSetting);
VARIANT_ENUM_CAST(GeometryInstance3D::LightmapScale);
VARIANT_ENUM_CAST(GeometryInstance3D::GIMode);
VARIANT_ENUM_CAST(GeometryInstance3D::VisibilityRangeFadeMode);
diff --git a/servers/rendering/dummy/storage/light_storage.h b/servers/rendering/dummy/storage/light_storage.h
index a9a7beb387ab..c1426128d6f3 100644
--- a/servers/rendering/dummy/storage/light_storage.h
+++ b/servers/rendering/dummy/storage/light_storage.h
@@ -168,6 +168,7 @@ class LightStorage : public RendererLightStorage {
void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override {}
/* SHADOW ATLAS API */
+ virtual bool shadow_atlas_get_split_shadows() const override { return false; }
virtual RID shadow_atlas_create() override { return RID(); }
virtual void shadow_atlas_free(RID p_atlas) override {}
virtual void shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits = true) override {}
diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp
index 834653f5c257..06ea815b17f9 100644
--- a/servers/rendering/renderer_rd/effects/copy_effects.cpp
+++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp
@@ -137,11 +137,15 @@ CopyEffects::CopyEffects(bool p_prefer_raster_effects) {
cube_to_dp.shader_version = cube_to_dp.shader.version_create();
RID shader = cube_to_dp.shader.version_get_shader(cube_to_dp.shader_version, 0);
+
RD::PipelineDepthStencilState dss;
dss.enable_depth_test = true;
dss.depth_compare_operator = RD::COMPARE_OP_ALWAYS;
dss.enable_depth_write = true;
- cube_to_dp.pipeline.setup(shader, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), dss, RD::PipelineColorBlendState(), 0);
+ cube_to_dp.pipelines[COPY_TO_DP_STATIC].setup(shader, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), dss, RD::PipelineColorBlendState(), 0);
+
+ dss.depth_compare_operator = RD::COMPARE_OP_LESS;
+ cube_to_dp.pipelines[COPY_TO_DP_DYNAMIC].setup(shader, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), dss, RD::PipelineColorBlendState(), 0);
}
{
@@ -625,7 +629,7 @@ void CopyEffects::copy_raster(RID p_source_texture, RID p_dest_framebuffer) {
RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, true);
- RD::get_singleton()->draw_list_end();
+ RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_RASTER);
}
void CopyEffects::gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, const Size2i &p_size, bool p_8bit_dst) {
@@ -958,7 +962,7 @@ void CopyEffects::set_color_raster(RID p_dest_texture, const Color &p_color, con
RD::get_singleton()->draw_list_end();
}
-void CopyEffects::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip, BitField p_post_barrier) {
+void CopyEffects::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip, bool p_is_static, BitField p_post_barrier) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
@@ -983,8 +987,10 @@ void CopyEffects::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuf
RID shader = cube_to_dp.shader.version_get_shader(cube_to_dp.shader_version, 0);
ERR_FAIL_COND(shader.is_null());
+ CopyToDPMode mode = p_is_static ? COPY_TO_DP_STATIC : COPY_TO_DP_DYNAMIC;
+
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, cube_to_dp.pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer)));
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, cube_to_dp.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
diff --git a/servers/rendering/renderer_rd/effects/copy_effects.h b/servers/rendering/renderer_rd/effects/copy_effects.h
index 470ac1acee2c..317ce9234b14 100644
--- a/servers/rendering/renderer_rd/effects/copy_effects.h
+++ b/servers/rendering/renderer_rd/effects/copy_effects.h
@@ -209,6 +209,12 @@ class CopyEffects {
// Copy to DP
+ enum CopyToDPMode {
+ COPY_TO_DP_STATIC,
+ COPY_TO_DP_DYNAMIC,
+ COPY_TO_DP_MAX
+ };
+
struct CopyToDPPushConstant {
float z_far;
float z_near;
@@ -219,7 +225,7 @@ class CopyEffects {
struct CopyToDP {
CubeToDpShaderRD shader;
RID shader_version;
- PipelineCacheRD pipeline;
+ PipelineCacheRD pipelines[COPY_TO_DP_MAX];
} cube_to_dp;
// Cubemap effects
@@ -341,7 +347,7 @@ class CopyEffects {
void set_color(RID p_dest_texture, const Color &p_color, const Rect2i &p_region, bool p_8bit_dst = false);
void set_color_raster(RID p_dest_texture, const Color &p_color, const Rect2i &p_region);
- void copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip, BitField p_post_barrier = RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_TRANSFER);
+ void copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip, bool p_is_static, BitField p_post_barrier = RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_TRANSFER);
void cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size);
void cubemap_downsample_raster(RID p_source_cubemap, RID p_dest_framebuffer, uint32_t p_face_id, const Size2i &p_size);
void cubemap_filter(RID p_source_cubemap, Vector p_dest_cubemap, bool p_use_array);
diff --git a/servers/rendering/renderer_rd/effects/debug_effects.cpp b/servers/rendering/renderer_rd/effects/debug_effects.cpp
index 8cd3c2248312..3ed9851f0009 100644
--- a/servers/rendering/renderer_rd/effects/debug_effects.cpp
+++ b/servers/rendering/renderer_rd/effects/debug_effects.cpp
@@ -51,6 +51,17 @@ DebugEffects::DebugEffects() {
raster_state.wireframe = true;
shadow_frustum.pipelines[SFP_WIREFRAME].setup(shadow_frustum.shader.version_get_shader(shadow_frustum.shader_version, 0), RD::RENDER_PRIMITIVE_LINES, raster_state, RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
}
+
+ {
+ // Combined shadow map debug shader
+
+ Vector modes;
+ modes.push_back("");
+
+ combined_shadow_map.shader.initialize(modes);
+ combined_shadow_map.shader_version = combined_shadow_map.shader.version_create();
+ combined_shadow_map.pipeline.setup(combined_shadow_map.shader.version_get_shader(combined_shadow_map.shader_version, 0), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_blend(), 0);
+ }
}
void DebugEffects::_create_frustum_arrays() {
@@ -148,6 +159,7 @@ void DebugEffects::_create_frustum_arrays() {
DebugEffects::~DebugEffects() {
shadow_frustum.shader.version_free(shadow_frustum.shader_version);
+ combined_shadow_map.shader.version_free(combined_shadow_map.shader_version);
// Destroy vertex buffer and array.
if (frustum.vertex_buffer.is_valid()) {
@@ -326,3 +338,27 @@ void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_proj
}
}
}
+
+void DebugEffects::draw_combined_shadow_map(RID p_static_texture, RID p_dynamic_texture, RID p_dest_fb, const Rect2 p_rect) {
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_static_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector({ default_sampler, p_static_texture }));
+ RD::Uniform u_dynamic_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector({ default_sampler, p_dynamic_texture }));
+
+ RID shader = combined_shadow_map.shader.version_get_shader(combined_shadow_map.shader_version, 0);
+ ERR_FAIL_COND(shader.is_null());
+
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector(), 1.0, 0, p_rect);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, combined_shadow_map.pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_fb)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_static_texture), 0);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_dynamic_texture), 1);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+}
diff --git a/servers/rendering/renderer_rd/effects/debug_effects.h b/servers/rendering/renderer_rd/effects/debug_effects.h
index 21b7b03f8492..053802be04e2 100644
--- a/servers/rendering/renderer_rd/effects/debug_effects.h
+++ b/servers/rendering/renderer_rd/effects/debug_effects.h
@@ -32,6 +32,7 @@
#define DEBUG_EFFECTS_RD_H
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
+#include "servers/rendering/renderer_rd/shaders/effects/debug_combined_shadow_map.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/shadow_frustum.glsl.gen.h"
#include "servers/rendering/renderer_scene_render.h"
@@ -41,6 +42,8 @@ namespace RendererRD {
class DebugEffects {
private:
+ /* Shadow frustum */
+
struct {
RD::VertexFormatID vertex_format;
RID vertex_buffer;
@@ -72,12 +75,20 @@ class DebugEffects {
void _create_frustum_arrays();
+ /* Combined shadow map */
+ struct {
+ DebugCombinedShadowMapShaderRD shader;
+ RID shader_version;
+ PipelineCacheRD pipeline;
+ } combined_shadow_map;
+
protected:
public:
DebugEffects();
~DebugEffects();
void draw_shadow_frustum(RID p_light, const Projection &p_cam_projection, const Transform3D &p_cam_transform, RID p_dest_fb, const Rect2 p_rect);
+ void draw_combined_shadow_map(RID p_static_texture, RID p_dynamic_texture, RID p_dest_fb, const Rect2 p_rect);
};
} // namespace RendererRD
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index 9c7990d679d2..16ebab6bdecd 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -1335,6 +1335,9 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
p_render_data->shadows.clear();
p_render_data->directional_shadows.clear();
+ LocalVector static_cube_shadows;
+ LocalVector static_positional_shadows;
+
Plane camera_plane(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z), p_render_data->scene_data->cam_transform.origin);
float lod_distance_multiplier = p_render_data->scene_data->cam_projection.get_lod_multiplier();
{
@@ -1345,17 +1348,26 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
if (light_storage->light_get_type(base) == RS::LIGHT_DIRECTIONAL) {
p_render_data->directional_shadows.push_back(i);
} else if (light_storage->light_get_type(base) == RS::LIGHT_OMNI && light_storage->light_omni_get_shadow_mode(base) == RS::LIGHT_OMNI_SHADOW_CUBE) {
+ if (p_render_data->render_shadows[i].redraw_staticmap) {
+ // Add this even if static_instances is empty, we still want to clear our map.
+ // Note that all 6 sides should be set with the same value.
+ static_cube_shadows.push_back(i);
+ }
+
+ // We render our cube maps even if we have no dynamic instances as we use an intermediate result and must render all 6 sides.
+ // We may change this if we ever rewrite this to render our cubemaps directly into our atlas.
p_render_data->cube_shadows.push_back(i);
} else {
- p_render_data->shadows.push_back(i);
+ if (p_render_data->render_shadows[i].redraw_staticmap) {
+ // Add this even if static_instances is empty, we still want to clear our map.
+ static_positional_shadows.push_back(i);
+ }
+ if (p_render_data->render_shadows[i].dynamic_instances.size() > 0) {
+ p_render_data->shadows.push_back(i);
+ }
}
}
- //cube shadows are rendered in their own way
- for (const int &index : p_render_data->cube_shadows) {
- _render_shadow_pass(p_render_data->render_shadows[index].light, p_render_data->shadow_atlas, p_render_data->render_shadows[index].pass, p_render_data->render_shadows[index].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, true, true, true, p_render_data->render_info);
- }
-
if (p_render_data->directional_shadows.size()) {
//open the pass for directional shadows
light_storage->update_directional_shadow_atlas();
@@ -1364,11 +1376,35 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
}
}
- // Render GI
+ // Render GI.
- bool render_shadows = p_render_data->directional_shadows.size() || p_render_data->shadows.size();
+ bool render_static_shadows = static_cube_shadows.size() > 0 || static_positional_shadows.size() > 0;
+ bool render_shadows = render_static_shadows || p_render_data->directional_shadows.size() || p_render_data->shadows.size() || p_render_data->cube_shadows.size();
bool render_gi = rb.is_valid() && p_use_gi;
+ // Prepare shadow rendering.
+ if (render_static_shadows) {
+ RENDER_TIMESTAMP("Render Static Shadows");
+
+ // Cube shadows are rendered in their own way so we do them first.
+ for (uint32_t i = 0; i < static_cube_shadows.size(); i++) {
+ int idx = static_cube_shadows[i];
+ _render_shadow_pass(p_render_data->render_shadows[idx].light, p_render_data->shadow_atlas, p_render_data->render_shadows[idx].pass, true, p_render_data->render_shadows[idx].static_instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, true, true, true, p_render_data->render_info);
+ }
+
+ _render_shadow_begin();
+
+ // Render positional shadows.
+ for (uint32_t i = 0; i < static_positional_shadows.size(); i++) {
+ int idx = static_positional_shadows[i];
+ _render_shadow_pass(p_render_data->render_shadows[idx].light, p_render_data->shadow_atlas, p_render_data->render_shadows[idx].pass, true, p_render_data->render_shadows[idx].static_instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, i == 0, i == (static_positional_shadows.size() - 1), true, p_render_data->render_info);
+ }
+
+ _render_shadow_process();
+
+ _render_shadow_end();
+ }
+
if (render_shadows && render_gi) {
RENDER_TIMESTAMP("Render GI + Render Shadows (Parallel)");
} else if (render_shadows) {
@@ -1377,28 +1413,45 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
RENDER_TIMESTAMP("Render GI");
}
- //prepare shadow rendering
if (render_shadows) {
+ // Note, if split shadow is false everything should have been loaded into our directional shadow lists.
+ // In this scenario we pass _render_shadow_pass.p_is_static_pass as true to trigger our correct clear logic.
+ bool split_shadows = light_storage->shadow_atlas_get_split_shadows();
+
+ if (split_shadows) {
+ // Copy our static map to our dynamic map
+ _render_shadow_copy_staticmap(p_render_data->shadow_atlas);
+ }
+
+ // Cube shadows are rendered in their own way so we do them first.
+ for (const int &idx : p_render_data->cube_shadows) {
+ _render_shadow_pass(p_render_data->render_shadows[idx].light, p_render_data->shadow_atlas, p_render_data->render_shadows[idx].pass, !split_shadows, p_render_data->render_shadows[idx].dynamic_instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, true, true, false, p_render_data->render_info);
+ }
+
+ // Now render our dynamic shadows, from here on we run parallel with GI
_render_shadow_begin();
- //render directional shadows
+ // Render directional shadows.
for (uint32_t i = 0; i < p_render_data->directional_shadows.size(); i++) {
- _render_shadow_pass(p_render_data->render_shadows[p_render_data->directional_shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->directional_shadows[i]].pass, p_render_data->render_shadows[p_render_data->directional_shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, false, i == p_render_data->directional_shadows.size() - 1, false, p_render_data->render_info);
+ // Note: directional shadows are not split between static and dynamic instances (yet).
+ int idx = p_render_data->directional_shadows[i];
+ _render_shadow_pass(p_render_data->render_shadows[idx].light, p_render_data->shadow_atlas, p_render_data->render_shadows[idx].pass, !split_shadows, p_render_data->render_shadows[idx].dynamic_instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, false, i == p_render_data->directional_shadows.size() - 1, false, p_render_data->render_info);
}
- //render positional shadows
+ // Render positional shadows.
for (uint32_t i = 0; i < p_render_data->shadows.size(); i++) {
- _render_shadow_pass(p_render_data->render_shadows[p_render_data->shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->shadows[i]].pass, p_render_data->render_shadows[p_render_data->shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, i == 0, i == p_render_data->shadows.size() - 1, true, p_render_data->render_info);
+ int idx = p_render_data->shadows[i];
+ _render_shadow_pass(p_render_data->render_shadows[idx].light, p_render_data->shadow_atlas, p_render_data->render_shadows[idx].pass, !split_shadows, p_render_data->render_shadows[idx].dynamic_instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, false, i == p_render_data->shadows.size() - 1, !split_shadows, p_render_data->render_info);
}
_render_shadow_process();
}
- //start GI
+ // Start GI.
if (render_gi) {
gi.process_gi(rb, p_normal_roughness_slices, p_voxel_gi_buffer, p_render_data->environment, p_render_data->scene_data->view_count, p_render_data->scene_data->view_projection, p_render_data->scene_data->view_eye_offset, p_render_data->scene_data->cam_transform, *p_render_data->voxel_gi_instances);
}
- //Do shadow rendering (in parallel with GI)
+ // Do shadow rendering (in parallel with GI).
if (render_shadows) {
_render_shadow_end(RD::BARRIER_MASK_NO_BARRIER);
}
@@ -1428,7 +1481,7 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
}
}
- //full barrier here, we need raster, transfer and compute and it depends from the previous work
+ // Full barrier here, we need raster, transfer and compute and it depends from the previous work.
RD::get_singleton()->barrier(RD::BARRIER_MASK_ALL_BARRIERS, RD::BARRIER_MASK_ALL_BARRIERS);
if (current_cluster_builder) {
@@ -1447,7 +1500,7 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
using_shadows = false;
}
} else {
- //do not render reflections when rendering a reflection probe
+ // Do not render reflections when rendering a reflection probe.
light_storage->update_reflection_probe_buffer(p_render_data, *p_render_data->reflection_probes, p_render_data->scene_data->cam_transform.affine_inverse(), p_render_data->environment);
}
@@ -2147,7 +2200,7 @@ void RenderForwardClustered::_render_buffers_debug_draw(const RenderDataRD *p_re
}
}
-void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray &p_instances, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, bool p_open_pass, bool p_close_pass, bool p_clear_region, RenderingMethod::RenderInfo *p_render_info) {
+void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, bool p_is_static_pass, const PagedArray &p_instances, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, bool p_open_pass, bool p_close_pass, bool p_clear_region, RenderingMethod::RenderInfo *p_render_info) {
RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton();
ERR_FAIL_COND(!light_storage->owns_light_instance(p_light));
@@ -2265,7 +2318,7 @@ void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas
light_transform = light_storage->light_instance_get_shadow_transform(p_light, p_pass);
render_cubemap = true;
finalize_cubemap = p_pass == 5;
- atlas_fb = light_storage->shadow_atlas_get_fb(p_shadow_atlas);
+ atlas_fb = light_storage->shadow_atlas_get_fb(p_shadow_atlas, p_is_static_pass);
atlas_size = shadow_atlas_size;
@@ -2286,7 +2339,7 @@ void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas
using_dual_paraboloid = true;
using_dual_paraboloid_flip = p_pass == 1;
- render_fb = light_storage->shadow_atlas_get_fb(p_shadow_atlas);
+ render_fb = light_storage->shadow_atlas_get_fb(p_shadow_atlas, p_is_static_pass);
flip_y = true;
}
@@ -2294,7 +2347,7 @@ void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas
light_projection = light_storage->light_instance_get_shadow_camera(p_light, 0);
light_transform = light_storage->light_instance_get_shadow_transform(p_light, 0);
- render_fb = light_storage->shadow_atlas_get_fb(p_shadow_atlas);
+ render_fb = light_storage->shadow_atlas_get_fb(p_shadow_atlas, p_is_static_pass);
flip_y = true;
}
@@ -2302,7 +2355,7 @@ void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas
if (render_cubemap) {
//rendering to cubemap
- _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, false, false, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info);
+ _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, false, false, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, p_render_info);
if (finalize_cubemap) {
_render_shadow_process();
_render_shadow_end();
@@ -2310,9 +2363,9 @@ void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas
Rect2 atlas_rect_norm = atlas_rect;
atlas_rect_norm.position /= float(atlas_size);
atlas_rect_norm.size /= float(atlas_size);
- copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), false);
+ copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), false, p_is_static_pass);
atlas_rect_norm.position += Vector2(dual_paraboloid_offset) * atlas_rect_norm.size;
- copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), true);
+ copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), true, p_is_static_pass);
//restore transform so it can be properly used
light_storage->light_instance_set_shadow_transform(p_light, Projection(), light_storage->light_instance_get_base_transform(p_light), zfar, 0, 0, 0);
@@ -2320,7 +2373,16 @@ void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas
} else {
//render shadow
- _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, using_dual_paraboloid, using_dual_paraboloid_flip, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, atlas_rect, flip_y, p_clear_region, p_open_pass, p_close_pass, p_render_info);
+
+ RD::InitialAction initial_depth_action;
+ if (p_is_static_pass) {
+ initial_depth_action = p_open_pass ? (p_clear_region ? RD::INITIAL_ACTION_CLEAR_REGION : RD::INITIAL_ACTION_CLEAR) : (p_clear_region ? RD::INITIAL_ACTION_CLEAR_REGION_CONTINUE : RD::INITIAL_ACTION_CONTINUE);
+ } else {
+ initial_depth_action = p_open_pass ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CONTINUE;
+ }
+ RD::FinalAction final_depth_action = p_close_pass ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE;
+
+ _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, using_dual_paraboloid, using_dual_paraboloid_flip, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, atlas_rect, flip_y, initial_depth_action, final_depth_action, p_render_info);
}
}
@@ -2333,7 +2395,7 @@ void RenderForwardClustered::_render_shadow_begin() {
scene_state.instance_data[RENDER_LIST_SECONDARY].clear();
}
-void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const PagedArray &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, const Rect2i &p_rect, bool p_flip_y, bool p_clear_region, bool p_begin, bool p_end, RenderingMethod::RenderInfo *p_render_info) {
+void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const PagedArray &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, const Rect2i &p_rect, bool p_flip_y, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, RenderingMethod::RenderInfo *p_render_info) {
uint32_t shadow_pass_index = scene_state.shadow_passes.size();
SceneState::ShadowPass shadow_pass;
@@ -2395,8 +2457,8 @@ void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const Page
shadow_pass.lod_distance_multiplier = scene_data.lod_distance_multiplier;
shadow_pass.framebuffer = p_framebuffer;
- shadow_pass.initial_depth_action = p_begin ? (p_clear_region ? RD::INITIAL_ACTION_CLEAR_REGION : RD::INITIAL_ACTION_CLEAR) : (p_clear_region ? RD::INITIAL_ACTION_CLEAR_REGION_CONTINUE : RD::INITIAL_ACTION_CONTINUE);
- shadow_pass.final_depth_action = p_end ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE;
+ shadow_pass.initial_depth_action = p_initial_depth_action;
+ shadow_pass.final_depth_action = p_final_depth_action;
shadow_pass.rect = p_rect;
scene_state.shadow_passes.push_back(shadow_pass);
@@ -2415,6 +2477,21 @@ void RenderForwardClustered::_render_shadow_process() {
RD::get_singleton()->draw_command_end_label();
}
+
+void RenderForwardClustered::_render_shadow_copy_staticmap(RID p_shadow_atlas) {
+ RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton();
+ RD::get_singleton()->draw_command_begin_label("Copy static shadow map");
+
+ // Copy static to dynamic shadow
+ RID static_atlas = light_storage->shadow_atlas_get_texture(p_shadow_atlas, true);
+ RID dynamic_atlas = light_storage->shadow_atlas_get_texture(p_shadow_atlas, false);
+ int size = light_storage->shadow_atlas_get_size(p_shadow_atlas);
+
+ RD::get_singleton()->texture_copy(static_atlas, dynamic_atlas, Vector3(), Vector3(), Vector3(size, size, 0.0), 0, 0, 0, 0, RD::BARRIER_MASK_RASTER);
+
+ RD::get_singleton()->draw_command_end_label();
+}
+
void RenderForwardClustered::_render_shadow_end(uint32_t p_barrier) {
RD::get_singleton()->draw_command_begin_label("Shadow Render");
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
index a89c77c652ee..676b68f51e33 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
@@ -581,10 +581,11 @@ class RenderForwardClustered : public RendererSceneRenderRD {
/* Render shadows */
- void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true, RenderingMethod::RenderInfo *p_render_info = nullptr);
+ void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, bool p_is_static_pass, const PagedArray &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true, RenderingMethod::RenderInfo *p_render_info = nullptr);
void _render_shadow_begin();
- void _render_shadow_append(RID p_framebuffer, const PagedArray &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr);
+ void _render_shadow_append(RID p_framebuffer, const PagedArray &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, RD::InitialAction p_initial_depth_action = RD::INITIAL_ACTION_KEEP, RD::FinalAction p_final_depth_action = RD::FINAL_ACTION_READ, RenderingMethod::RenderInfo *p_render_info = nullptr);
void _render_shadow_process();
+ void _render_shadow_copy_staticmap(RID p_shadow_atlas);
void _render_shadow_end(uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS);
/* Render Scene */
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index befb2c5504aa..296d3ac09e30 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -555,6 +555,9 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) {
p_render_data->shadows.clear();
p_render_data->directional_shadows.clear();
+ LocalVector static_cube_shadows;
+ LocalVector static_positional_shadows;
+
Plane camera_plane(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z), p_render_data->scene_data->cam_transform.origin);
float lod_distance_multiplier = p_render_data->scene_data->cam_projection.get_lod_multiplier();
{
@@ -565,40 +568,91 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) {
if (light_storage->light_get_type(base) == RS::LIGHT_DIRECTIONAL) {
p_render_data->directional_shadows.push_back(i);
} else if (light_storage->light_get_type(base) == RS::LIGHT_OMNI && light_storage->light_omni_get_shadow_mode(base) == RS::LIGHT_OMNI_SHADOW_CUBE) {
+ if (p_render_data->render_shadows[i].redraw_staticmap) {
+ // Add this even if static_instances is empty, we still want to clear our map.
+ // Note that all 6 sides should be set with the same value.
+ static_cube_shadows.push_back(i);
+ }
+
+ // We render our cube maps even if we have no dynamic instances as we use an intermediate result and must render all 6 sides.
+ // We may change this if we ever rewrite this to render our cubemaps directly into our atlas.
p_render_data->cube_shadows.push_back(i);
} else {
- p_render_data->shadows.push_back(i);
+ if (p_render_data->render_shadows[i].redraw_staticmap) {
+ // Add this even if static_instances is empty, we still want to clear our map.
+ static_positional_shadows.push_back(i);
+ }
+ if (p_render_data->render_shadows[i].dynamic_instances.size() > 0) {
+ p_render_data->shadows.push_back(i);
+ }
}
}
- //cube shadows are rendered in their own way
- for (const int &index : p_render_data->cube_shadows) {
- _render_shadow_pass(p_render_data->render_shadows[index].light, p_render_data->shadow_atlas, p_render_data->render_shadows[index].pass, p_render_data->render_shadows[index].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, true, true, true, p_render_data->render_info);
- }
-
if (p_render_data->directional_shadows.size()) {
- //open the pass for directional shadows
+ // Open the pass for directional shadows, this will clear the directional shadows too.
light_storage->update_directional_shadow_atlas();
RD::get_singleton()->draw_list_begin(light_storage->direction_shadow_get_fb(), RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_CONTINUE);
RD::get_singleton()->draw_list_end();
}
}
- bool render_shadows = p_render_data->directional_shadows.size() || p_render_data->shadows.size();
+ bool render_static_shadows = static_cube_shadows.size() > 0 || static_positional_shadows.size() > 0;
+ bool render_shadows = render_static_shadows || p_render_data->directional_shadows.size() || p_render_data->shadows.size() || p_render_data->cube_shadows.size();
+
+ // Prepare shadow rendering.
+ if (render_static_shadows) {
+ RENDER_TIMESTAMP("Render Static Shadows");
+
+ // Cube shadows are rendered in their own way so we do them first.
+ for (uint32_t i = 0; i < static_cube_shadows.size(); i++) {
+ int idx = static_cube_shadows[i];
+ _render_shadow_pass(p_render_data->render_shadows[idx].light, p_render_data->shadow_atlas, p_render_data->render_shadows[idx].pass, true, p_render_data->render_shadows[idx].static_instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, true, true, true, p_render_data->render_info);
+ }
+
+ _render_shadow_begin();
+
+ // Render positional shadows.
+ for (uint32_t i = 0; i < static_positional_shadows.size(); i++) {
+ int idx = static_positional_shadows[i];
+ _render_shadow_pass(p_render_data->render_shadows[idx].light, p_render_data->shadow_atlas, p_render_data->render_shadows[idx].pass, true, p_render_data->render_shadows[idx].static_instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, i == 0, i == static_positional_shadows.size() - 1, true, p_render_data->render_info);
+ }
+
+ _render_shadow_process();
+
+ _render_shadow_end(RD::BARRIER_MASK_RASTER); // TODO we need a fragment to fragment barrier here!
+ }
- //prepare shadow rendering
if (render_shadows) {
- RENDER_TIMESTAMP("Render Shadows");
+ // Note, if split shadow is false everything should have been loaded into our directional shadow lists.
+ // In this scenario we pass _render_shadow_pass.p_is_static_pass as true to trigger our correct clear logic.
+ bool split_shadows = light_storage->shadow_atlas_get_split_shadows();
+
+ if (split_shadows) {
+ RENDER_TIMESTAMP("Render Dynamic Shadows");
+ // Copy our static map
+ _render_shadow_copy_staticmap(p_render_data->shadow_atlas);
+ } else {
+ RENDER_TIMESTAMP("Render Shadows");
+ }
+
+ // Cube shadows are rendered in their own way so we do them first.
+ for (uint32_t i = 0; i < p_render_data->cube_shadows.size(); i++) {
+ int idx = p_render_data->cube_shadows[i];
+ _render_shadow_pass(p_render_data->render_shadows[idx].light, p_render_data->shadow_atlas, p_render_data->render_shadows[idx].pass, !split_shadows, p_render_data->render_shadows[idx].dynamic_instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, i == 0, i == p_render_data->cube_shadows.size() - 1, true, p_render_data->render_info);
+ }
_render_shadow_begin();
- //render directional shadows
+ // Render directional shadows.
for (uint32_t i = 0; i < p_render_data->directional_shadows.size(); i++) {
- _render_shadow_pass(p_render_data->render_shadows[p_render_data->directional_shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->directional_shadows[i]].pass, p_render_data->render_shadows[p_render_data->directional_shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, false, i == p_render_data->directional_shadows.size() - 1, false, p_render_data->render_info);
+ // Note: directional shadows are not split between static and dynamic instances (yet).
+ int idx = p_render_data->directional_shadows[i];
+ _render_shadow_pass(p_render_data->render_shadows[idx].light, p_render_data->shadow_atlas, p_render_data->render_shadows[idx].pass, !split_shadows, p_render_data->render_shadows[idx].dynamic_instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, false, i == p_render_data->directional_shadows.size() - 1, false, p_render_data->render_info);
}
- //render positional shadows
+ // Render positional shadows.
for (uint32_t i = 0; i < p_render_data->shadows.size(); i++) {
- _render_shadow_pass(p_render_data->render_shadows[p_render_data->shadows[i]].light, p_render_data->shadow_atlas, p_render_data->render_shadows[p_render_data->shadows[i]].pass, p_render_data->render_shadows[p_render_data->shadows[i]].instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, i == 0, i == p_render_data->shadows.size() - 1, true, p_render_data->render_info);
+ int idx = p_render_data->shadows[i];
+ _render_shadow_pass(p_render_data->render_shadows[idx].light, p_render_data->shadow_atlas, p_render_data->render_shadows[idx].pass, !split_shadows, p_render_data->render_shadows[idx].dynamic_instances, camera_plane, lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, false, i == p_render_data->shadows.size() - 1, !split_shadows, p_render_data->render_info);
}
_render_shadow_process();
@@ -606,7 +660,7 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) {
_render_shadow_end(RD::BARRIER_MASK_NO_BARRIER);
}
- //full barrier here, we need raster, transfer and compute and it depends from the previous work
+ // Full barrier here, we need raster, transfer and compute and it depends from the previous work.
RD::get_singleton()->barrier(RD::BARRIER_MASK_ALL_BARRIERS, RD::BARRIER_MASK_ALL_BARRIERS);
bool using_shadows = true;
@@ -616,7 +670,7 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) {
using_shadows = false;
}
} else {
- //do not render reflections when rendering a reflection probe
+ // Do not render reflections when rendering a reflection probe.
light_storage->update_reflection_probe_buffer(p_render_data, *p_render_data->reflection_probes, p_render_data->scene_data->cam_transform.affine_inverse(), p_render_data->environment);
}
@@ -1060,7 +1114,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
/* these are being called from RendererSceneRenderRD::_pre_opaque_render */
-void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray &p_instances, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, bool p_open_pass, bool p_close_pass, bool p_clear_region, RenderingMethod::RenderInfo *p_render_info) {
+void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, bool p_is_static_pass, const PagedArray &p_instances, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, bool p_open_pass, bool p_close_pass, bool p_clear_region, RenderingMethod::RenderInfo *p_render_info) {
RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton();
ERR_FAIL_COND(!light_storage->owns_light_instance(p_light));
@@ -1177,7 +1231,7 @@ void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, i
light_transform = light_storage->light_instance_get_shadow_transform(p_light, p_pass);
render_cubemap = true;
finalize_cubemap = p_pass == 5;
- atlas_fb = light_storage->shadow_atlas_get_fb(p_shadow_atlas);
+ atlas_fb = light_storage->shadow_atlas_get_fb(p_shadow_atlas, p_is_static_pass);
atlas_size = shadow_atlas_size;
@@ -1198,7 +1252,7 @@ void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, i
using_dual_paraboloid = true;
using_dual_paraboloid_flip = p_pass == 1;
- render_fb = light_storage->shadow_atlas_get_fb(p_shadow_atlas);
+ render_fb = light_storage->shadow_atlas_get_fb(p_shadow_atlas, p_is_static_pass);
flip_y = true;
}
@@ -1206,7 +1260,7 @@ void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, i
light_projection = light_storage->light_instance_get_shadow_camera(p_light, 0);
light_transform = light_storage->light_instance_get_shadow_transform(p_light, 0);
- render_fb = light_storage->shadow_atlas_get_fb(p_shadow_atlas);
+ render_fb = light_storage->shadow_atlas_get_fb(p_shadow_atlas, p_is_static_pass);
flip_y = true;
}
@@ -1214,7 +1268,7 @@ void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, i
if (render_cubemap) {
//rendering to cubemap
- _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, false, false, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info);
+ _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, false, false, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, p_render_info);
if (finalize_cubemap) {
_render_shadow_process();
_render_shadow_end(RD::BARRIER_MASK_FRAGMENT);
@@ -1223,17 +1277,28 @@ void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, i
Rect2 atlas_rect_norm = atlas_rect;
atlas_rect_norm.position /= float(atlas_size);
atlas_rect_norm.size /= float(atlas_size);
- copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), false, RD::BARRIER_MASK_NO_BARRIER);
+ copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), false, p_is_static_pass, RD::BARRIER_MASK_NO_BARRIER);
atlas_rect_norm.position += Vector2(dual_paraboloid_offset) * atlas_rect_norm.size;
- copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), true, RD::BARRIER_MASK_NO_BARRIER);
+ copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), true, p_is_static_pass, RD::BARRIER_MASK_NO_BARRIER);
//restore transform so it can be properly used
- light_storage->light_instance_set_shadow_transform(p_light, Projection(), light_storage->light_instance_get_base_transform(p_light), zfar, 0, 0, 0);
+ if (!p_is_static_pass) {
+ light_storage->light_instance_set_shadow_transform(p_light, Projection(), light_storage->light_instance_get_base_transform(p_light), zfar, 0, 0, 0);
+ }
}
} else {
//render shadow
- _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, using_dual_paraboloid, using_dual_paraboloid_flip, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, atlas_rect, flip_y, p_clear_region, p_open_pass, p_close_pass, p_render_info);
+
+ RD::InitialAction initial_depth_action;
+ if (p_is_static_pass) {
+ initial_depth_action = p_open_pass ? (p_clear_region ? RD::INITIAL_ACTION_CLEAR_REGION : RD::INITIAL_ACTION_CLEAR) : (p_clear_region ? RD::INITIAL_ACTION_CLEAR_REGION_CONTINUE : RD::INITIAL_ACTION_CONTINUE);
+ } else {
+ initial_depth_action = p_open_pass ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CONTINUE;
+ }
+ RD::FinalAction final_depth_action = p_close_pass ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE;
+
+ _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, using_dual_paraboloid, using_dual_paraboloid_flip, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, atlas_rect, flip_y, initial_depth_action, final_depth_action, p_render_info);
}
}
@@ -1245,7 +1310,7 @@ void RenderForwardMobile::_render_shadow_begin() {
render_list[RENDER_LIST_SECONDARY].clear();
}
-void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedArray &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, const Rect2i &p_rect, bool p_flip_y, bool p_clear_region, bool p_begin, bool p_end, RenderingMethod::RenderInfo *p_render_info) {
+void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedArray &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, const Rect2i &p_rect, bool p_flip_y, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, RenderingMethod::RenderInfo *p_render_info) {
uint32_t shadow_pass_index = scene_state.shadow_passes.size();
SceneState::ShadowPass shadow_pass;
@@ -1306,8 +1371,8 @@ void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedAr
shadow_pass.lod_distance_multiplier = scene_data.lod_distance_multiplier;
shadow_pass.framebuffer = p_framebuffer;
- shadow_pass.initial_depth_action = p_begin ? (p_clear_region ? RD::INITIAL_ACTION_CLEAR_REGION : RD::INITIAL_ACTION_CLEAR) : (p_clear_region ? RD::INITIAL_ACTION_CLEAR_REGION_CONTINUE : RD::INITIAL_ACTION_CONTINUE);
- shadow_pass.final_depth_action = p_end ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE;
+ shadow_pass.initial_depth_action = p_initial_depth_action;
+ shadow_pass.final_depth_action = p_final_depth_action;
shadow_pass.rect = p_rect;
scene_state.shadow_passes.push_back(shadow_pass);
@@ -1326,6 +1391,21 @@ void RenderForwardMobile::_render_shadow_process() {
RD::get_singleton()->draw_command_end_label();
}
+void RenderForwardMobile::_render_shadow_copy_staticmap(RID p_shadow_atlas) {
+ RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton();
+
+ RD::get_singleton()->draw_command_begin_label("Copy static shadow map");
+
+ // Copy static to dynamic shadow
+ RID static_atlas = light_storage->shadow_atlas_get_texture(p_shadow_atlas, true);
+ RID dynamic_atlas = light_storage->shadow_atlas_get_texture(p_shadow_atlas, false);
+ int size = light_storage->shadow_atlas_get_size(p_shadow_atlas);
+
+ RD::get_singleton()->texture_copy(static_atlas, dynamic_atlas, Vector3(), Vector3(), Vector3(size, size, 0.0), 0, 0, 0, 0, RD::BARRIER_MASK_RASTER);
+
+ RD::get_singleton()->draw_command_end_label();
+}
+
void RenderForwardMobile::_render_shadow_end(uint32_t p_barrier) {
RD::get_singleton()->draw_command_begin_label("Shadow Render");
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
index f2913dd18591..e068986fb313 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
@@ -187,10 +187,11 @@ class RenderForwardMobile : public RendererSceneRenderRD {
/* Render shadows */
- void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true, RenderingMethod::RenderInfo *p_render_info = nullptr);
+ void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, bool p_is_static_pass, const PagedArray &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true, RenderingMethod::RenderInfo *p_render_info = nullptr);
void _render_shadow_begin();
- void _render_shadow_append(RID p_framebuffer, const PagedArray &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr);
+ void _render_shadow_append(RID p_framebuffer, const PagedArray &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, RD::InitialAction p_initial_depth_action = RD::INITIAL_ACTION_KEEP, RD::FinalAction p_final_depth_action = RD::FINAL_ACTION_READ, RenderingMethod::RenderInfo *p_render_info = nullptr);
void _render_shadow_process();
+ void _render_shadow_copy_staticmap(RID p_shadow_atlas);
void _render_shadow_end(uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS);
/* Render Scene */
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index 9d4d266a7a79..a8c6f81bc3aa 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -674,14 +674,28 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(const RenderDataRD *p_ren
if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SHADOW_ATLAS) {
if (p_render_data->shadow_atlas.is_valid()) {
- RID shadow_atlas_texture = RendererRD::LightStorage::get_singleton()->shadow_atlas_get_texture(p_render_data->shadow_atlas);
+ RID static_shadow_atlas_texture = RendererRD::LightStorage::get_singleton()->shadow_atlas_get_texture(p_render_data->shadow_atlas, true);
+ RID dynamic_shadow_atlas_texture = RendererRD::LightStorage::get_singleton()->shadow_atlas_get_texture(p_render_data->shadow_atlas);
- if (shadow_atlas_texture.is_null()) {
- shadow_atlas_texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK);
+ if (static_shadow_atlas_texture.is_null()) {
+ static_shadow_atlas_texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK);
+ }
+
+ if (dynamic_shadow_atlas_texture.is_null()) {
+ dynamic_shadow_atlas_texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK);
}
Size2 rtsize = texture_storage->render_target_get_size(render_target);
- copy_effects->copy_to_fb_rect(shadow_atlas_texture, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2i(Vector2(), rtsize / 2), false, true);
+
+ // Determine our display size, try and keep square by using the smallest edge.
+ Size2i size = 2 * rtsize / 3;
+ if (size.x < size.y) {
+ size.y = size.x;
+ } else if (size.y < size.x) {
+ size.x = size.y;
+ }
+
+ debug_effects->draw_combined_shadow_map(static_shadow_atlas_texture, dynamic_shadow_atlas_texture, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2i(Vector2(), size));
}
}
diff --git a/servers/rendering/renderer_rd/shaders/effects/debug_combined_shadow_map.glsl b/servers/rendering/renderer_rd/shaders/effects/debug_combined_shadow_map.glsl
new file mode 100644
index 000000000000..a4dd1b561e70
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/effects/debug_combined_shadow_map.glsl
@@ -0,0 +1,38 @@
+#[vertex]
+
+#version 450
+
+#VERSION_DEFINES
+
+layout(location = 0) out vec2 uv_interp;
+
+void main() {
+ vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
+ uv_interp.xy = base_arr[gl_VertexIndex];
+ uv_interp.y = 1.0 - uv_interp.y; // invert Y
+ gl_Position = vec4(uv_interp.xy * 2.0 - 1.0, 0.0, 1.0);
+}
+
+#[fragment]
+
+#version 450
+
+#VERSION_DEFINES
+
+layout(set = 0, binding = 0) uniform sampler2D static_shadow_map;
+layout(set = 1, binding = 0) uniform sampler2D dynamic_shadow_map;
+
+layout(location = 0) in vec2 uv_interp;
+layout(location = 0) out vec4 frag_color;
+
+void main() {
+ float static_depth = textureLod(static_shadow_map, uv_interp, 0.0).r;
+ float dynamic_depth = textureLod(dynamic_shadow_map, uv_interp, 0.0).r;
+ if (static_depth <= dynamic_depth) {
+ vec3 static_color = vec3(static_depth);
+ frag_color = vec4(static_color, 1.0);
+ } else {
+ vec3 dynamic_color = vec3(0.25, 0.25, 0.5 + 0.5 * (1.0 - dynamic_depth));
+ frag_color = vec4(dynamic_color, 1.0);
+ }
+}
diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
index a5ec62b546df..a7e10d29cc42 100644
--- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
@@ -48,6 +48,7 @@ LightStorage::LightStorage() {
directional_shadow.size = GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/size");
directional_shadow.use_16_bits = GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/16_bits");
+ split_static_and_dynamic_shadows = GLOBAL_GET("rendering/lights_and_shadows/positional_shadow/split_static_and_dynamic_shadows");
using_lightmap_array = true; // high end
if (using_lightmap_array) {
@@ -1956,17 +1957,33 @@ void LightStorage::shadow_atlas_free(RID p_atlas) {
}
void LightStorage::_update_shadow_atlas(ShadowAtlas *shadow_atlas) {
- if (shadow_atlas->size > 0 && shadow_atlas->depth.is_null()) {
+ if (shadow_atlas->size > 0 && shadow_atlas->dynamic_depth.is_null()) {
RD::TextureFormat tf;
tf.format = shadow_atlas->use_16_bits ? RD::DATA_FORMAT_D16_UNORM : RD::DATA_FORMAT_D32_SFLOAT;
tf.width = shadow_atlas->size;
tf.height = shadow_atlas->size;
- tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
- shadow_atlas->depth = RD::get_singleton()->texture_create(tf, RD::TextureView());
- Vector fb_tex;
- fb_tex.push_back(shadow_atlas->depth);
- shadow_atlas->fb = RD::get_singleton()->framebuffer_create(fb_tex);
+ if (split_static_and_dynamic_shadows) {
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
+ shadow_atlas->static_depth = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(shadow_atlas->static_depth, "Positional static shadow map");
+
+ {
+ Vector fb_tex;
+ fb_tex.push_back(shadow_atlas->static_depth);
+ shadow_atlas->static_fb = RD::get_singleton()->framebuffer_create(fb_tex);
+ }
+ }
+
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
+ shadow_atlas->dynamic_depth = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(shadow_atlas->dynamic_depth, "Positional dynamic shadow map");
+
+ {
+ Vector fb_tex;
+ fb_tex.push_back(shadow_atlas->dynamic_depth);
+ shadow_atlas->dynamic_fb = RD::get_singleton()->framebuffer_create(fb_tex);
+ }
}
}
@@ -1981,10 +1998,17 @@ void LightStorage::shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits
}
// erasing atlas
- if (shadow_atlas->depth.is_valid()) {
- RD::get_singleton()->free(shadow_atlas->depth);
- shadow_atlas->depth = RID();
+ if (shadow_atlas->static_depth.is_valid()) {
+ RD::get_singleton()->free(shadow_atlas->static_depth);
+ shadow_atlas->static_depth = RID();
+ shadow_atlas->static_fb = RID();
}
+ if (shadow_atlas->dynamic_depth.is_valid()) {
+ RD::get_singleton()->free(shadow_atlas->dynamic_depth);
+ shadow_atlas->dynamic_depth = RID();
+ shadow_atlas->dynamic_fb = RID();
+ }
+
for (int i = 0; i < 4; i++) {
//clear subdivisions
shadow_atlas->quadrants[i].shadows.clear();
diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h
index 512c440ae8f3..32fea0b1d407 100644
--- a/servers/rendering/renderer_rd/storage_rd/light_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h
@@ -366,6 +366,7 @@ class LightStorage : public RendererLightStorage {
/* SHADOW ATLAS */
uint64_t shadow_atlas_realloc_tolerance_msec = 500;
+ bool split_static_and_dynamic_shadows = false;
struct ShadowShrinkStage {
RID texture;
@@ -397,8 +398,10 @@ class LightStorage : public RendererLightStorage {
int size = 0;
bool use_16_bits = true;
- RID depth;
- RID fb; //for copying
+ RID static_depth;
+ RID dynamic_depth;
+ RID static_fb; //for copying
+ RID dynamic_fb; //for copying
HashMap shadow_owners;
};
@@ -997,6 +1000,8 @@ class LightStorage : public RendererLightStorage {
/* SHADOW ATLAS API */
+ virtual bool shadow_atlas_get_split_shadows() const override { return split_static_and_dynamic_shadows; }
+
bool owns_shadow_atlas(RID p_rid) { return shadow_atlas_owner.owns(p_rid); };
virtual RID shadow_atlas_create() override;
@@ -1016,10 +1021,11 @@ class LightStorage : public RendererLightStorage {
return atlas->shadow_owners[p_light_intance];
}
- _FORCE_INLINE_ RID shadow_atlas_get_texture(RID p_atlas) {
+ _FORCE_INLINE_ RID shadow_atlas_get_texture(RID p_atlas, bool p_static = false) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_COND_V(!atlas, RID());
- return atlas->depth;
+ bool split_shadow = RSG::light_storage->shadow_atlas_get_split_shadows();
+ return (p_static && split_shadow) ? atlas->static_depth : atlas->dynamic_depth;
}
_FORCE_INLINE_ int shadow_atlas_get_size(RID p_atlas) {
@@ -1042,10 +1048,11 @@ class LightStorage : public RendererLightStorage {
return atlas->quadrants[p_quadrant].subdivision;
}
- _FORCE_INLINE_ RID shadow_atlas_get_fb(RID p_atlas) {
+ _FORCE_INLINE_ RID shadow_atlas_get_fb(RID p_atlas, bool p_static = false) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_COND_V(!atlas, RID());
- return atlas->fb;
+ bool split_shadow = RSG::light_storage->shadow_atlas_get_split_shadows();
+ return (p_static && split_shadow) ? atlas->static_fb : atlas->dynamic_fb;
}
virtual void shadow_atlas_update(RID p_atlas) override;
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index 45bbcf51c4d1..f8d2117d0c2f 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -142,7 +142,9 @@ void RendererSceneCull::_instance_pair(Instance *p_A, Instance *p_B) {
geom->lights.insert(B);
light->geometries.insert(A);
- if (geom->can_cast_shadows) {
+ // We mark our light dirty if an object is paired and it is marked static or if dynamic shadow maps are disabled.
+ // Note that static lights get paired on the first frame or if a light moves into range.
+ if (geom->can_cast_shadows && (A->dynamic_shadow_mode != RS::SHADOW_MODE_DYNAMIC || !RSG::light_storage->shadow_atlas_get_split_shadows())) {
light->shadow_dirty = true;
}
@@ -249,7 +251,9 @@ void RendererSceneCull::_instance_unpair(Instance *p_A, Instance *p_B) {
geom->lights.erase(B);
light->geometries.erase(A);
- if (geom->can_cast_shadows) {
+ // We mark our light dirty if an object is unpaired and is marked static or if dynamic shadow maps are disabled.
+ // Note that a static light can be unpaired if a light moves out of range, so don't mark as dynamic.
+ if (geom->can_cast_shadows && (A->dynamic_shadow_mode != RS::SHADOW_MODE_DYNAMIC || !RSG::light_storage->shadow_atlas_get_split_shadows())) {
light->shadow_dirty = true;
}
@@ -853,7 +857,8 @@ void RendererSceneCull::instance_set_layer_mask(RID p_instance, uint32_t p_mask)
ERR_FAIL_NULL(geom->geometry_instance);
geom->geometry_instance->set_layer_mask(p_mask);
- if (geom->can_cast_shadows) {
+ // We mark our lights dirty if an objects layer mask is changed and is marked static or if dynamic shadow maps are disabled.
+ if (geom->can_cast_shadows && (instance->dynamic_shadow_mode != RS::SHADOW_MODE_DYNAMIC || !RSG::light_storage->shadow_atlas_get_split_shadows())) {
for (HashSet::Iterator I = geom->lights.begin(); I != geom->lights.end(); ++I) {
InstanceLightData *light = static_cast((*I)->base_data);
light->shadow_dirty = true;
@@ -1253,6 +1258,15 @@ void RendererSceneCull::instance_geometry_set_cast_shadows_setting(RID p_instanc
_instance_queue_update(instance, false, true);
}
+void RendererSceneCull::instance_geometry_set_shadow_mode(RID p_instance, RS::ShadowDynamicMode p_shadow_dynamic_mode) {
+ Instance *instance = instance_owner.get_or_null(p_instance);
+ ERR_FAIL_COND(!instance);
+
+ instance->dynamic_shadow_mode = p_shadow_dynamic_mode;
+
+ _instance_queue_update(instance, false, true);
+}
+
void RendererSceneCull::instance_geometry_set_material_override(RID p_instance, RID p_material) {
Instance *instance = instance_owner.get_or_null(p_instance);
ERR_FAIL_COND(!instance);
@@ -1632,10 +1646,22 @@ void RendererSceneCull::_update_instance(Instance *p_instance) {
InstanceGeometryData *geom = static_cast(p_instance->base_data);
//make sure lights are updated if it casts shadow
- if (geom->can_cast_shadows) {
+ // If an instance marked as auto changes, it must be a dynamic object,
+ // so we change it and mark the light as dirty.
+ // If it's static, the user must have a reason not to want to update the shadow map.
+ // If it's dynamic, it's rendered to the dynamic shadow map.
+ // TODO: may need to be more precise on what changed in the instance, we may get false positives
+ bool split_shadows = RSG::light_storage->shadow_atlas_get_split_shadows();
+ if (geom->can_cast_shadows && (p_instance->dynamic_shadow_mode == RS::SHADOW_MODE_AUTO || !split_shadows)) {
for (const Instance *E : geom->lights) {
InstanceLightData *light = static_cast(E->base_data);
- light->shadow_dirty = true;
+ if (!split_shadows) {
+ light->shadow_dirty = true;
+ } else if (!light->shadow_dirty) {
+ // instance changed while lights shadow map was fully rendered? Mark object as dynamic
+ p_instance->dynamic_shadow_mode = RS::SHADOW_MODE_DYNAMIC;
+ light->shadow_split_update = true;
+ }
}
}
@@ -2275,13 +2301,22 @@ void RendererSceneCull::_light_instance_setup_directional_shadow(int p_shadow_in
}
}
-bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, const Transform3D p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_shadow_atlas, Scenario *p_scenario, float p_screen_mesh_lod_threshold, uint32_t p_visible_layers) {
+bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, const Transform3D p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_shadow_atlas, bool p_atlas_changed, Scenario *p_scenario, float p_screen_mesh_lod_threshold, uint32_t p_visible_layers) {
InstanceLightData *light = static_cast(p_instance->base_data);
Transform3D light_transform = p_instance->transform;
light_transform.orthonormalize(); //scale does not count on lights
- bool animated_material_found = false;
+ bool split_shadows = RSG::light_storage->shadow_atlas_get_split_shadows();
+
+ if (!split_shadows && !p_atlas_changed) {
+ // If we're not splitting shadows and last frame we successfully rendered our shadow map, keep our result.
+ return false;
+ }
+
+ // Note, if we're not splitting shadows into static and dynamic, we add all elements to our dynamic list and leave our static list empty.
+ // In this scenario we also return keep_dirty as true if we encounter any elements
+ bool keep_dirty = false;
switch (RSG::light_storage->light_get_type(p_instance->base)) {
case RS::LIGHT_DIRECTIONAL: {
@@ -2328,14 +2363,23 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons
p_scenario->indexers[Scenario::INDEXER_GEOMETRY].convex_query(planes.ptr(), planes.size(), points.ptr(), points.size(), cull_convex);
RendererSceneRender::RenderShadowData &shadow_data = render_shadow_data[max_shadows_used++];
+ shadow_data.redraw_staticmap = split_shadows && p_atlas_changed;
for (int j = 0; j < (int)instance_shadow_cull_result.size(); j++) {
Instance *instance = instance_shadow_cull_result[j];
+ bool use_dynamic_shadowmap = !split_shadows || instance->dynamic_shadow_mode == RS::SHADOW_MODE_DYNAMIC;
+
if (!instance->visible || !((1 << instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) || !static_cast(instance->base_data)->can_cast_shadows || !(p_visible_layers & instance->layer_mask)) {
continue;
} else {
if (static_cast(instance->base_data)->material_is_animated) {
- animated_material_found = true;
+ if (!split_shadows) {
+ keep_dirty = true;
+ } else if (!use_dynamic_shadowmap) {
+ // Always render these to our dynamic shadow map regardless of our setting!
+ instance->dynamic_shadow_mode = RS::SHADOW_MODE_DYNAMIC;
+ use_dynamic_shadowmap = true;
+ }
}
if (instance->mesh_instance.is_valid()) {
@@ -2343,7 +2387,11 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons
}
}
- shadow_data.instances.push_back(static_cast(instance->base_data)->geometry_instance);
+ if (use_dynamic_shadowmap) {
+ shadow_data.dynamic_instances.push_back(static_cast(instance->base_data)->geometry_instance);
+ } else if (p_atlas_changed) {
+ shadow_data.static_instances.push_back(static_cast(instance->base_data)->geometry_instance);
+ }
}
RSG::mesh_storage->update_mesh_instances();
@@ -2353,7 +2401,6 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons
shadow_data.pass = i;
}
} else { //shadow cube
-
if (max_shadows_used + 6 > MAX_UPDATE_SHADOWS) {
return true;
}
@@ -2406,21 +2453,35 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons
p_scenario->indexers[Scenario::INDEXER_GEOMETRY].convex_query(planes.ptr(), planes.size(), points.ptr(), points.size(), cull_convex);
RendererSceneRender::RenderShadowData &shadow_data = render_shadow_data[max_shadows_used++];
+ shadow_data.redraw_staticmap = split_shadows && p_atlas_changed;
for (int j = 0; j < (int)instance_shadow_cull_result.size(); j++) {
Instance *instance = instance_shadow_cull_result[j];
+ bool use_dynamic_shadowmap = !split_shadows || instance->dynamic_shadow_mode == RS::SHADOW_MODE_DYNAMIC;
+
if (!instance->visible || !((1 << instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) || !static_cast(instance->base_data)->can_cast_shadows || !(p_visible_layers & instance->layer_mask)) {
continue;
} else {
if (static_cast(instance->base_data)->material_is_animated) {
- animated_material_found = true;
+ if (!split_shadows) {
+ keep_dirty = true;
+ } else if (!use_dynamic_shadowmap) {
+ // Always render these to our dynamic shadow map regardless of our setting!
+ instance->dynamic_shadow_mode = RS::SHADOW_MODE_DYNAMIC;
+ use_dynamic_shadowmap = true;
+ }
}
+
if (instance->mesh_instance.is_valid()) {
RSG::mesh_storage->mesh_instance_check_for_update(instance->mesh_instance);
}
}
- shadow_data.instances.push_back(static_cast(instance->base_data)->geometry_instance);
+ if (use_dynamic_shadowmap) {
+ shadow_data.dynamic_instances.push_back(static_cast(instance->base_data)->geometry_instance);
+ } else if (p_atlas_changed) {
+ shadow_data.static_instances.push_back(static_cast(instance->base_data)->geometry_instance);
+ }
}
RSG::mesh_storage->update_mesh_instances();
@@ -2469,21 +2530,35 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons
p_scenario->indexers[Scenario::INDEXER_GEOMETRY].convex_query(planes.ptr(), planes.size(), points.ptr(), points.size(), cull_convex);
RendererSceneRender::RenderShadowData &shadow_data = render_shadow_data[max_shadows_used++];
+ shadow_data.redraw_staticmap = split_shadows && p_atlas_changed;
for (int j = 0; j < (int)instance_shadow_cull_result.size(); j++) {
Instance *instance = instance_shadow_cull_result[j];
+ bool use_dynamic_shadowmap = !split_shadows || instance->dynamic_shadow_mode == RS::SHADOW_MODE_DYNAMIC;
+
if (!instance->visible || !((1 << instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) || !static_cast(instance->base_data)->can_cast_shadows || !(p_visible_layers & instance->layer_mask)) {
continue;
} else {
if (static_cast(instance->base_data)->material_is_animated) {
- animated_material_found = true;
+ if (!split_shadows) {
+ keep_dirty = true;
+ } else if (!use_dynamic_shadowmap) {
+ // Always render these to our dynamic shadow map regardless of our setting!
+ instance->dynamic_shadow_mode = RS::SHADOW_MODE_DYNAMIC;
+ use_dynamic_shadowmap = true;
+ }
}
if (instance->mesh_instance.is_valid()) {
RSG::mesh_storage->mesh_instance_check_for_update(instance->mesh_instance);
}
}
- shadow_data.instances.push_back(static_cast(instance->base_data)->geometry_instance);
+
+ if (use_dynamic_shadowmap) {
+ shadow_data.dynamic_instances.push_back(static_cast(instance->base_data)->geometry_instance);
+ } else if (p_atlas_changed) {
+ shadow_data.static_instances.push_back(static_cast(instance->base_data)->geometry_instance);
+ }
}
RSG::mesh_storage->update_mesh_instances();
@@ -2495,7 +2570,7 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons
} break;
}
- return animated_material_found;
+ return keep_dirty;
}
void RendererSceneCull::render_camera(const Ref &p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, bool p_use_taa, float p_screen_mesh_lod_threshold, RID p_shadow_atlas, Ref &p_xr_interface, RenderInfo *r_render_info) {
@@ -3151,7 +3226,9 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c
}
render_shadow_data[max_shadows_used].light = cull.shadows[i].light_instance;
render_shadow_data[max_shadows_used].pass = j;
- render_shadow_data[max_shadows_used].instances.merge_unordered(scene_cull_result.directional_shadows[i].cascade_geometry_instances[j]);
+ render_shadow_data[max_shadows_used].redraw_staticmap = false;
+ // TODO see if we can split static instances, currently not supported yet in any renderer
+ render_shadow_data[max_shadows_used].dynamic_instances.merge_unordered(scene_cull_result.directional_shadows[i].cascade_geometry_instances[j]);
max_shadows_used++;
}
}
@@ -3237,19 +3314,26 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c
}
if (light->shadow_dirty) {
+ // A light will be marked as dirty if:
+ // - this is the first time we're rendering a shadow map for it,
+ // - any of its properties changed,
+ // - if it couldn't be updated last frame,
+ // - if we detected animated/dynamic meshes last frame (only for Omni light with cubemap shadows).
+ // We increase the last version to trigger a redraw of our static shadowmaps
light->last_version++;
light->shadow_dirty = false;
}
- bool redraw = RSG::light_storage->shadow_atlas_update_light(p_shadow_atlas, light->instance, coverage, light->last_version);
+ bool atlas_changed = RSG::light_storage->shadow_atlas_update_light(p_shadow_atlas, light->instance, coverage, light->last_version);
- if (redraw && max_shadows_used < MAX_UPDATE_SHADOWS) {
+ if (max_shadows_used < MAX_UPDATE_SHADOWS) {
//must redraw!
RENDER_TIMESTAMP("> Render Light3D " + itos(i));
- light->shadow_dirty = _light_instance_update_shadow(ins, p_camera_data->main_transform, p_camera_data->main_projection, p_camera_data->is_orthogonal, p_camera_data->vaspect, p_shadow_atlas, scenario, p_screen_mesh_lod_threshold, p_visible_layers);
+ light->shadow_dirty = _light_instance_update_shadow(ins, p_camera_data->main_transform, p_camera_data->main_projection, p_camera_data->is_orthogonal, p_camera_data->vaspect, p_shadow_atlas, atlas_changed || light->shadow_split_update, scenario, p_screen_mesh_lod_threshold, p_visible_layers);
+ light->shadow_split_update = false;
RENDER_TIMESTAMP("< Render Light3D " + itos(i));
} else {
- light->shadow_dirty = redraw;
+ light->shadow_dirty = atlas_changed;
}
}
}
@@ -3319,7 +3403,8 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c
}
for (uint32_t i = 0; i < max_shadows_used; i++) {
- render_shadow_data[i].instances.clear();
+ render_shadow_data[i].dynamic_instances.clear();
+ render_shadow_data[i].static_instances.clear();
}
max_shadows_used = 0;
@@ -4132,7 +4217,8 @@ RendererSceneCull::RendererSceneCull() {
instance_shadow_cull_result.set_page_pool(&instance_cull_page_pool);
for (uint32_t i = 0; i < MAX_UPDATE_SHADOWS; i++) {
- render_shadow_data[i].instances.set_page_pool(&geometry_instance_cull_page_pool);
+ render_shadow_data[i].dynamic_instances.set_page_pool(&geometry_instance_cull_page_pool);
+ render_shadow_data[i].static_instances.set_page_pool(&geometry_instance_cull_page_pool);
}
for (uint32_t i = 0; i < SDFGI_MAX_CASCADES * SDFGI_MAX_REGIONS_PER_CASCADE; i++) {
render_sdfgi_data[i].instances.set_page_pool(&geometry_instance_cull_page_pool);
@@ -4162,7 +4248,8 @@ RendererSceneCull::~RendererSceneCull() {
instance_shadow_cull_result.reset();
for (uint32_t i = 0; i < MAX_UPDATE_SHADOWS; i++) {
- render_shadow_data[i].instances.reset();
+ render_shadow_data[i].dynamic_instances.reset();
+ render_shadow_data[i].static_instances.reset();
}
for (uint32_t i = 0; i < SDFGI_MAX_CASCADES * SDFGI_MAX_REGIONS_PER_CASCADE; i++) {
render_sdfgi_data[i].instances.reset();
diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h
index 4745c3d1d3ac..82f427b632cc 100644
--- a/servers/rendering/renderer_scene_cull.h
+++ b/servers/rendering/renderer_scene_cull.h
@@ -400,6 +400,7 @@ class RendererSceneCull : public RenderingMethod {
Vector materials;
RS::ShadowCastingSetting cast_shadows;
+ RS::ShadowDynamicMode dynamic_shadow_mode = RS::SHADOW_MODE_AUTO;
uint32_t layer_mask;
//fit in 32 bits
@@ -676,7 +677,8 @@ class RendererSceneCull : public RenderingMethod {
uint64_t last_version;
List::Element *D; // directional light in scenario
- bool shadow_dirty;
+ bool shadow_dirty = true; // mark that shadow map needs to be updated fully
+ bool shadow_split_update = false; // if shadow split is on, marked as true if a static object moved while shadow_dirty is false
bool uses_projector = false;
bool uses_softshadow = false;
@@ -689,7 +691,6 @@ class RendererSceneCull : public RenderingMethod {
InstanceLightData() {
bake_mode = RS::LIGHT_BAKE_DISABLED;
- shadow_dirty = true;
D = nullptr;
last_version = 0;
baked_light = nullptr;
@@ -988,6 +989,7 @@ class RendererSceneCull : public RenderingMethod {
virtual void instance_geometry_set_flag(RID p_instance, RS::InstanceFlags p_flags, bool p_enabled);
virtual void instance_geometry_set_cast_shadows_setting(RID p_instance, RS::ShadowCastingSetting p_shadow_casting_setting);
+ virtual void instance_geometry_set_shadow_mode(RID p_instance, RS::ShadowDynamicMode p_shadow_dynamic_mode);
virtual void instance_geometry_set_material_override(RID p_instance, RID p_material);
virtual void instance_geometry_set_material_overlay(RID p_instance, RID p_material);
@@ -1011,7 +1013,7 @@ class RendererSceneCull : public RenderingMethod {
void _light_instance_setup_directional_shadow(int p_shadow_index, Instance *p_instance, const Transform3D p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect);
- _FORCE_INLINE_ bool _light_instance_update_shadow(Instance *p_instance, const Transform3D p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_shadow_atlas, Scenario *p_scenario, float p_scren_mesh_lod_threshold, uint32_t p_visible_layers = 0xFFFFFF);
+ _FORCE_INLINE_ bool _light_instance_update_shadow(Instance *p_instance, const Transform3D p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_shadow_atlas, bool p_atlas_changed, Scenario *p_scenario, float p_scren_mesh_lod_threshold, uint32_t p_visible_layers = 0xFFFFFF);
RID _render_get_environment(RID p_camera, RID p_scenario);
diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h
index 900738364194..407856794bfd 100644
--- a/servers/rendering/renderer_scene_render.h
+++ b/servers/rendering/renderer_scene_render.h
@@ -244,7 +244,9 @@ class RendererSceneRender {
struct RenderShadowData {
RID light;
int pass = 0;
- PagedArray instances;
+ bool redraw_staticmap = false;
+ PagedArray static_instances;
+ PagedArray dynamic_instances;
};
struct RenderSDFGIData {
diff --git a/servers/rendering/rendering_method.h b/servers/rendering/rendering_method.h
index f705603a1cbc..43ab76cb7ea8 100644
--- a/servers/rendering/rendering_method.h
+++ b/servers/rendering/rendering_method.h
@@ -96,6 +96,7 @@ class RenderingMethod {
virtual void instance_geometry_set_flag(RID p_instance, RS::InstanceFlags p_flags, bool p_enabled) = 0;
virtual void instance_geometry_set_cast_shadows_setting(RID p_instance, RS::ShadowCastingSetting p_shadow_casting_setting) = 0;
+ virtual void instance_geometry_set_shadow_mode(RID p_instance, RS::ShadowDynamicMode p_shadow_dynamic_mode) = 0;
virtual void instance_geometry_set_material_override(RID p_instance, RID p_material) = 0;
virtual void instance_geometry_set_material_overlay(RID p_instance, RID p_material) = 0;
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 9ad217533220..ec9e45fa3c9f 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -795,6 +795,7 @@ class RenderingServerDefault : public RenderingServer {
FUNC3(instance_geometry_set_flag, RID, InstanceFlags, bool)
FUNC2(instance_geometry_set_cast_shadows_setting, RID, ShadowCastingSetting)
+ FUNC2(instance_geometry_set_shadow_mode, RID, ShadowDynamicMode)
FUNC2(instance_geometry_set_material_override, RID, RID)
FUNC2(instance_geometry_set_material_overlay, RID, RID)
diff --git a/servers/rendering/storage/light_storage.h b/servers/rendering/storage/light_storage.h
index c1f79cfc4924..0de5fcf5d752 100644
--- a/servers/rendering/storage/light_storage.h
+++ b/servers/rendering/storage/light_storage.h
@@ -177,6 +177,8 @@ class RendererLightStorage {
/* SHADOW ATLAS */
+ virtual bool shadow_atlas_get_split_shadows() const = 0;
+
virtual RID shadow_atlas_create() = 0;
virtual void shadow_atlas_free(RID p_atlas) = 0;
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 45ba0b3c089b..68be524e0dd9 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2513,6 +2513,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("instance_geometry_set_flag", "instance", "flag", "enabled"), &RenderingServer::instance_geometry_set_flag);
ClassDB::bind_method(D_METHOD("instance_geometry_set_cast_shadows_setting", "instance", "shadow_casting_setting"), &RenderingServer::instance_geometry_set_cast_shadows_setting);
+ ClassDB::bind_method(D_METHOD("instance_geometry_set_shadow_mode", "instance", "shadow_mode"), &RenderingServer::instance_geometry_set_shadow_mode);
ClassDB::bind_method(D_METHOD("instance_geometry_set_material_override", "instance", "material"), &RenderingServer::instance_geometry_set_material_override);
ClassDB::bind_method(D_METHOD("instance_geometry_set_material_overlay", "instance", "material"), &RenderingServer::instance_geometry_set_material_overlay);
ClassDB::bind_method(D_METHOD("instance_geometry_set_visibility_range", "instance", "min", "max", "min_margin", "max_margin", "fade_mode"), &RenderingServer::instance_geometry_set_visibility_range);
@@ -2556,6 +2557,10 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_DOUBLE_SIDED);
BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_SHADOWS_ONLY);
+ BIND_ENUM_CONSTANT(SHADOW_MODE_AUTO);
+ BIND_ENUM_CONSTANT(SHADOW_MODE_STATIC);
+ BIND_ENUM_CONSTANT(SHADOW_MODE_DYNAMIC);
+
BIND_ENUM_CONSTANT(VISIBILITY_RANGE_FADE_DISABLED);
BIND_ENUM_CONSTANT(VISIBILITY_RANGE_FADE_SELF);
BIND_ENUM_CONSTANT(VISIBILITY_RANGE_FADE_DEPENDENCIES);
@@ -2898,6 +2903,7 @@ void RenderingServer::init() {
GLOBAL_DEF("rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality.mobile", 0);
GLOBAL_DEF("rendering/lights_and_shadows/directional_shadow/16_bits", true);
+ GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/split_static_and_dynamic_shadows", false);
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality", PROPERTY_HINT_ENUM, "Hard (Fastest),Soft Very Low (Faster),Soft Low (Fast),Soft Medium (Average),Soft High (Slow),Soft Ultra (Slowest)"), 2);
GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality.mobile", 0);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 1528a957ce45..f10cc9d5352e 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -1264,6 +1264,12 @@ class RenderingServer : public Object {
SHADOW_CASTING_SETTING_SHADOWS_ONLY,
};
+ enum ShadowDynamicMode {
+ SHADOW_MODE_AUTO,
+ SHADOW_MODE_STATIC,
+ SHADOW_MODE_DYNAMIC,
+ };
+
enum VisibilityRangeFadeMode {
VISIBILITY_RANGE_FADE_DISABLED,
VISIBILITY_RANGE_FADE_SELF,
@@ -1272,6 +1278,7 @@ class RenderingServer : public Object {
virtual void instance_geometry_set_flag(RID p_instance, InstanceFlags p_flags, bool p_enabled) = 0;
virtual void instance_geometry_set_cast_shadows_setting(RID p_instance, ShadowCastingSetting p_shadow_casting_setting) = 0;
+ virtual void instance_geometry_set_shadow_mode(RID p_instance, ShadowDynamicMode p_shadow_dynamic_mode) = 0;
virtual void instance_geometry_set_material_override(RID p_instance, RID p_material) = 0;
virtual void instance_geometry_set_material_overlay(RID p_instance, RID p_material) = 0;
virtual void instance_geometry_set_visibility_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin, VisibilityRangeFadeMode p_fade_mode) = 0;
@@ -1685,6 +1692,7 @@ VARIANT_ENUM_CAST(RenderingServer::ShadowQuality);
VARIANT_ENUM_CAST(RenderingServer::InstanceType);
VARIANT_ENUM_CAST(RenderingServer::InstanceFlags);
VARIANT_ENUM_CAST(RenderingServer::ShadowCastingSetting);
+VARIANT_ENUM_CAST(RenderingServer::ShadowDynamicMode);
VARIANT_ENUM_CAST(RenderingServer::VisibilityRangeFadeMode);
VARIANT_ENUM_CAST(RenderingServer::NinePatchAxisMode);
VARIANT_ENUM_CAST(RenderingServer::CanvasItemTextureFilter);