diff --git a/doc/classes/Light3D.xml b/doc/classes/Light3D.xml
index bda5fb69de7..966d0fdcb46 100644
--- a/doc/classes/Light3D.xml
+++ b/doc/classes/Light3D.xml
@@ -115,6 +115,9 @@
Blurs the edges of the shadow. Can be used to hide pixel artifacts in low-resolution shadow maps. A high value can impact performance, make shadows appear grainy and can cause other unwanted artifacts. Try to keep as near default as possible.
+
+ The light will only cast shadows using objects in the selected layers.
+
If [code]true[/code], the light will cast real-time shadows. This has a significant performance cost. Only enable shadow rendering when it makes a noticeable difference in the scene's appearance, and consider using [member distance_fade_enabled] to hide the light when far away from the [Camera3D].
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 3c9f0fc7aff..37c7519462f 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -2111,6 +2111,14 @@
If [code]true[/code], light will cast shadows. Equivalent to [member Light3D.shadow_enabled].
+
+
+
+
+
+ Sets the shadow caster mask for this 3D light. Shadows will only be cast using objects in the selected layers. Equivalent to [member Light3D.shadow_caster_mask].
+
+
diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp
index f9547502f4b..bbd48fe5cc9 100644
--- a/drivers/gles3/storage/light_storage.cpp
+++ b/drivers/gles3/storage/light_storage.cpp
@@ -213,6 +213,23 @@ void LightStorage::light_set_cull_mask(RID p_light, uint32_t p_mask) {
light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT);
}
+void LightStorage::light_set_shadow_caster_mask(RID p_light, uint32_t p_caster_mask) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_NULL(light);
+
+ light->shadow_caster_mask = p_caster_mask;
+
+ light->version++;
+ light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT);
+}
+
+uint32_t LightStorage::light_get_shadow_caster_mask(RID p_light) const {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_NULL_V(light, 0);
+
+ return light->shadow_caster_mask;
+}
+
void LightStorage::light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) {
Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL(light);
diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h
index b6e64c9492b..a5758e0a4c9 100644
--- a/drivers/gles3/storage/light_storage.h
+++ b/drivers/gles3/storage/light_storage.h
@@ -59,6 +59,7 @@ struct Light {
RS::LightBakeMode bake_mode = RS::LIGHT_BAKE_DYNAMIC;
uint32_t max_sdfgi_cascade = 2;
uint32_t cull_mask = 0xFFFFFFFF;
+ uint32_t shadow_caster_mask = 0xFFFFFFFF;
bool distance_fade = false;
real_t distance_fade_begin = 40.0;
real_t distance_fade_shadow = 50.0;
@@ -326,6 +327,8 @@ public:
virtual void light_set_cull_mask(RID p_light, uint32_t p_mask) override;
virtual void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) override;
virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override;
+ virtual void light_set_shadow_caster_mask(RID p_light, uint32_t p_caster_mask) override;
+ virtual uint32_t light_get_shadow_caster_mask(RID p_light) const override;
virtual void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override;
virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override {}
diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp
index 7b70986adce..2d18e62b102 100644
--- a/scene/3d/light_3d.cpp
+++ b/scene/3d/light_3d.cpp
@@ -146,6 +146,15 @@ bool Light3D::get_shadow_reverse_cull_face() const {
return reverse_cull;
}
+void Light3D::set_shadow_caster_mask(uint32_t p_caster_mask) {
+ shadow_caster_mask = p_caster_mask;
+ RS::get_singleton()->light_set_shadow_caster_mask(light, shadow_caster_mask);
+}
+
+uint32_t Light3D::get_shadow_caster_mask() const {
+ return shadow_caster_mask;
+}
+
AABB Light3D::get_aabb() const {
if (type == RenderingServer::LIGHT_DIRECTIONAL) {
return AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2));
@@ -300,7 +309,7 @@ bool Light3D::is_editor_only() const {
}
void Light3D::_validate_property(PropertyInfo &p_property) const {
- if (!shadow && (p_property.name == "shadow_bias" || p_property.name == "shadow_normal_bias" || p_property.name == "shadow_reverse_cull_face" || p_property.name == "shadow_transmittance_bias" || p_property.name == "shadow_opacity" || p_property.name == "shadow_blur" || p_property.name == "distance_fade_shadow")) {
+ if (!shadow && (p_property.name == "shadow_bias" || p_property.name == "shadow_normal_bias" || p_property.name == "shadow_reverse_cull_face" || p_property.name == "shadow_transmittance_bias" || p_property.name == "shadow_opacity" || p_property.name == "shadow_blur" || p_property.name == "distance_fade_shadow" || p_property.name == "shadow_caster_mask")) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
@@ -354,6 +363,9 @@ void Light3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shadow_reverse_cull_face", "enable"), &Light3D::set_shadow_reverse_cull_face);
ClassDB::bind_method(D_METHOD("get_shadow_reverse_cull_face"), &Light3D::get_shadow_reverse_cull_face);
+ ClassDB::bind_method(D_METHOD("set_shadow_caster_mask", "caster_mask"), &Light3D::set_shadow_caster_mask);
+ ClassDB::bind_method(D_METHOD("get_shadow_caster_mask"), &Light3D::get_shadow_caster_mask);
+
ClassDB::bind_method(D_METHOD("set_bake_mode", "bake_mode"), &Light3D::set_bake_mode);
ClassDB::bind_method(D_METHOD("get_bake_mode"), &Light3D::get_bake_mode);
@@ -388,6 +400,7 @@ void Light3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_transmittance_bias", PROPERTY_HINT_RANGE, "-16,16,0.001"), "set_param", "get_param", PARAM_TRANSMITTANCE_BIAS);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_opacity", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_OPACITY);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_blur", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_BLUR);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_caster_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_shadow_caster_mask", "get_shadow_caster_mask");
ADD_GROUP("Distance Fade", "distance_fade_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_fade_enabled"), "set_enable_distance_fade", "is_distance_fade_enabled");
diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h
index d6eca8d8b6e..5f549469c62 100644
--- a/scene/3d/light_3d.h
+++ b/scene/3d/light_3d.h
@@ -75,6 +75,7 @@ private:
bool negative = false;
bool reverse_cull = false;
uint32_t cull_mask = 0;
+ uint32_t shadow_caster_mask = 0xFFFFFFFF;
bool distance_fade_enabled = false;
real_t distance_fade_begin = 40.0;
real_t distance_fade_shadow = 50.0;
@@ -136,6 +137,9 @@ public:
void set_shadow_reverse_cull_face(bool p_enable);
bool get_shadow_reverse_cull_face() const;
+ void set_shadow_caster_mask(uint32_t p_caster_mask);
+ uint32_t get_shadow_caster_mask() const;
+
void set_bake_mode(BakeMode p_mode);
BakeMode get_bake_mode() const;
diff --git a/servers/rendering/dummy/storage/light_storage.h b/servers/rendering/dummy/storage/light_storage.h
index c3b63cdbf63..d25523753c9 100644
--- a/servers/rendering/dummy/storage/light_storage.h
+++ b/servers/rendering/dummy/storage/light_storage.h
@@ -78,6 +78,8 @@ public:
virtual void light_set_cull_mask(RID p_light, uint32_t p_mask) override {}
virtual void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) override {}
virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override {}
+ virtual void light_set_shadow_caster_mask(RID p_light, uint32_t p_caster_mask) override {}
+ virtual uint32_t light_get_shadow_caster_mask(RID p_light) const override { return 0xFFFFFFFF; }
virtual void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override {}
virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override {}
diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
index 3d294ca8cb1..b4acdbec11b 100644
--- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
@@ -285,6 +285,23 @@ void LightStorage::light_set_reverse_cull_face_mode(RID p_light, bool p_enabled)
light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT);
}
+void LightStorage::light_set_shadow_caster_mask(RID p_light, uint32_t p_caster_mask) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_NULL(light);
+
+ light->shadow_caster_mask = p_caster_mask;
+
+ light->version++;
+ light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT);
+}
+
+uint32_t LightStorage::light_get_shadow_caster_mask(RID p_light) const {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_NULL_V(light, 0);
+
+ return light->shadow_caster_mask;
+}
+
void LightStorage::light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) {
Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL(light);
diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h
index f152cc5dae6..34d8f58e2a1 100644
--- a/servers/rendering/renderer_rd/storage_rd/light_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h
@@ -72,6 +72,7 @@ private:
RS::LightBakeMode bake_mode = RS::LIGHT_BAKE_DYNAMIC;
uint32_t max_sdfgi_cascade = 2;
uint32_t cull_mask = 0xFFFFFFFF;
+ uint32_t shadow_caster_mask = 0xFFFFFFFF;
bool distance_fade = false;
real_t distance_fade_begin = 40.0;
real_t distance_fade_shadow = 50.0;
@@ -474,6 +475,8 @@ public:
virtual void light_set_cull_mask(RID p_light, uint32_t p_mask) override;
virtual void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) override;
virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override;
+ virtual void light_set_shadow_caster_mask(RID p_light, uint32_t p_caster_mask) override;
+ virtual uint32_t light_get_shadow_caster_mask(RID p_light) const override;
virtual void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override;
virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override;
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index b02d3def88f..74c79dbce46 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -2139,6 +2139,7 @@ void RendererSceneCull::_light_instance_setup_directional_shadow(int p_shadow_in
cull.shadow_count = p_shadow_index + 1;
cull.shadows[p_shadow_index].cascade_count = splits;
cull.shadows[p_shadow_index].light_instance = light->instance;
+ cull.shadows[p_shadow_index].caster_mask = RSG::light_storage->light_get_shadow_caster_mask(p_instance->base);
for (int i = 0; i < splits; i++) {
RENDER_TIMESTAMP("Cull DirectionalLight3D, Split " + itos(i));
@@ -2369,7 +2370,7 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons
for (int j = 0; j < (int)instance_shadow_cull_result.size(); j++) {
Instance *instance = instance_shadow_cull_result[j];
- 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)) {
+ 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 & RSG::light_storage->light_get_shadow_caster_mask(p_instance->base))) {
continue;
} else {
if (static_cast(instance->base_data)->material_is_animated) {
@@ -2451,7 +2452,7 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons
for (int j = 0; j < (int)instance_shadow_cull_result.size(); j++) {
Instance *instance = instance_shadow_cull_result[j];
- 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)) {
+ 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 & RSG::light_storage->light_get_shadow_caster_mask(p_instance->base))) {
continue;
} else {
if (static_cast(instance->base_data)->material_is_animated) {
@@ -2518,7 +2519,7 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons
for (int j = 0; j < (int)instance_shadow_cull_result.size(); j++) {
Instance *instance = instance_shadow_cull_result[j];
- 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)) {
+ 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 & RSG::light_storage->light_get_shadow_caster_mask(p_instance->base))) {
continue;
} else {
if (static_cast(instance->base_data)->material_is_animated) {
@@ -2978,7 +2979,7 @@ void RendererSceneCull::_scene_cull(CullData &cull_data, InstanceCullResult &cul
if (IN_FRUSTUM(cull_data.cull->shadows[j].cascades[k].frustum) && VIS_CHECK) {
uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK;
- if (((1 << base_type) & RS::INSTANCE_GEOMETRY_MASK) && idata.flags & InstanceData::FLAG_CAST_SHADOWS && LAYER_CHECK) {
+ if (((1 << base_type) & RS::INSTANCE_GEOMETRY_MASK) && idata.flags & InstanceData::FLAG_CAST_SHADOWS && (LAYER_CHECK & cull_data.cull->shadows[j].caster_mask)) {
cull_result.directional_shadows[j].cascade_geometry_instances[k].push_back(idata.instance_geometry);
mesh_visible = true;
}
diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h
index 0039d144751..8f02860118d 100644
--- a/servers/rendering/renderer_scene_cull.h
+++ b/servers/rendering/renderer_scene_cull.h
@@ -1084,6 +1084,7 @@ public:
struct Cull {
struct Shadow {
RID light_instance;
+ uint32_t caster_mask;
struct Cascade {
Frustum frustum;
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 164ec3cc09f..73c4706beed 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -377,6 +377,7 @@ public:
FUNC2(light_set_cull_mask, RID, uint32_t)
FUNC5(light_set_distance_fade, RID, bool, float, float, float)
FUNC2(light_set_reverse_cull_face_mode, RID, bool)
+ FUNC2(light_set_shadow_caster_mask, RID, uint32_t)
FUNC2(light_set_bake_mode, RID, LightBakeMode)
FUNC2(light_set_max_sdfgi_cascade, RID, uint32_t)
diff --git a/servers/rendering/storage/light_storage.h b/servers/rendering/storage/light_storage.h
index 6a0adfa5966..1e149e3a97c 100644
--- a/servers/rendering/storage/light_storage.h
+++ b/servers/rendering/storage/light_storage.h
@@ -59,6 +59,8 @@ public:
virtual void light_set_cull_mask(RID p_light, uint32_t p_mask) = 0;
virtual void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) = 0;
virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) = 0;
+ virtual void light_set_shadow_caster_mask(RID p_light, uint32_t p_caster_mask) = 0;
+ virtual uint32_t light_get_shadow_caster_mask(RID p_light) const = 0;
virtual void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) = 0;
virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) = 0;
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 70b585d6834..2549bfa7a3f 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2467,6 +2467,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("light_set_cull_mask", "light", "mask"), &RenderingServer::light_set_cull_mask);
ClassDB::bind_method(D_METHOD("light_set_distance_fade", "decal", "enabled", "begin", "shadow", "length"), &RenderingServer::light_set_distance_fade);
ClassDB::bind_method(D_METHOD("light_set_reverse_cull_face_mode", "light", "enabled"), &RenderingServer::light_set_reverse_cull_face_mode);
+ ClassDB::bind_method(D_METHOD("light_set_shadow_caster_mask", "light", "mask"), &RenderingServer::light_set_shadow_caster_mask);
ClassDB::bind_method(D_METHOD("light_set_bake_mode", "light", "bake_mode"), &RenderingServer::light_set_bake_mode);
ClassDB::bind_method(D_METHOD("light_set_max_sdfgi_cascade", "light", "cascade"), &RenderingServer::light_set_max_sdfgi_cascade);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 693c8224885..9e26710166e 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -508,6 +508,7 @@ public:
virtual void light_set_cull_mask(RID p_light, uint32_t p_mask) = 0;
virtual void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) = 0;
virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) = 0;
+ virtual void light_set_shadow_caster_mask(RID p_light, uint32_t p_caster_mask) = 0;
enum LightBakeMode {
LIGHT_BAKE_DISABLED,