diff --git a/crates/bevy_pbr/src/light.rs b/crates/bevy_pbr/src/light.rs index 8922a42e81..cb563a3d22 100644 --- a/crates/bevy_pbr/src/light.rs +++ b/crates/bevy_pbr/src/light.rs @@ -606,6 +606,18 @@ pub struct NotShadowCaster; #[reflect(Component, Default)] pub struct NotShadowReceiver; +/// Add this component to make a [`Mesh`](bevy_render::mesh::Mesh) not receive shadows +/// on its diffuse transmission lobe. +/// +/// Useful, for example, when a thick translucent mesh using [`StandardMaterial::diffuse_transmission`] +/// has a complicated shape that's hard to model via [`StandardMaterial::thickness`], and you'd like +/// to avoid self-shadows from affecting the transmitted light. +/// +/// **Note:** Setting [`NotShadowReceiver`] disables both regular and transmitted shadows. +#[derive(Component, Reflect, Default)] +#[reflect(Component, Default)] +pub struct NotTransmittedShadowReceiver; + #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] pub enum SimulationLightSystems { AddClusters, diff --git a/crates/bevy_pbr/src/pbr_material.rs b/crates/bevy_pbr/src/pbr_material.rs index cca4275833..234792946d 100644 --- a/crates/bevy_pbr/src/pbr_material.rs +++ b/crates/bevy_pbr/src/pbr_material.rs @@ -153,7 +153,8 @@ pub struct StandardMaterial { /// values higher than 0.5 will cause more diffuse light to be transmitted than reflected, resulting in a “darker” appearance /// for the front Lambertian lobe when an object is illuminated primarily from a single direction. /// - /// **Note:** Typically used in conjunction with [`StandardMaterial::thickness`]. + /// **Note:** Typically used in conjunction with [`StandardMaterial::thickness`]. To avoid self-shadows on complicated + /// mesh shapes without having to fine tune the thickness, consider using the [`NotTransmittedShadowReceiver`] component. pub diffuse_transmission: f32, /// The amount of light transmitted _specularly_ through the material (i.e. via refraction) diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index a180041c4b..aabbc83583 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -1,9 +1,9 @@ use crate::{ environment_map, prepass, EnvironmentMapLight, FogMeta, GlobalLightMeta, GpuFog, GpuLights, - GpuPointLights, LightMeta, NotShadowCaster, NotShadowReceiver, PreviousGlobalTransform, - ShadowSamplers, ViewClusterBindings, ViewFogUniformOffset, ViewLightsUniformOffset, - ViewShadowBindings, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, MAX_CASCADES_PER_LIGHT, - MAX_DIRECTIONAL_LIGHTS, + GpuPointLights, LightMeta, NotShadowCaster, NotShadowReceiver, NotTransmittedShadowReceiver, + PreviousGlobalTransform, ShadowSamplers, ViewClusterBindings, ViewFogUniformOffset, + ViewLightsUniformOffset, ViewShadowBindings, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, + MAX_CASCADES_PER_LIGHT, MAX_DIRECTIONAL_LIGHTS, }; use bevy_app::Plugin; use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped}; @@ -134,12 +134,13 @@ pub struct MeshUniform { bitflags::bitflags! { #[repr(transparent)] struct MeshFlags: u32 { - const SHADOW_RECEIVER = (1 << 0); + const SHADOW_RECEIVER = (1 << 0); + const TRANSMITTED_SHADOW_RECEIVER = (1 << 1); // Indicates the sign of the determinant of the 3x3 model matrix. If the sign is positive, // then the flag should be set, else it should not be set. - const SIGN_DETERMINANT_MODEL_3X3 = (1 << 31); - const NONE = 0; - const UNINITIALIZED = 0xFFFF; + const SIGN_DETERMINANT_MODEL_3X3 = (1 << 31); + const NONE = 0; + const UNINITIALIZED = 0xFFFF; } } @@ -155,6 +156,7 @@ pub fn extract_meshes( Option<&PreviousGlobalTransform>, &Handle, Option>, + Option>, Option>, )>, >, @@ -163,8 +165,16 @@ pub fn extract_meshes( let mut not_caster_commands = Vec::with_capacity(*prev_not_caster_commands_len); let visible_meshes = meshes_query.iter().filter(|(_, vis, ..)| vis.is_visible()); - for (entity, _, transform, previous_transform, handle, not_receiver, not_caster) in - visible_meshes + for ( + entity, + _, + transform, + previous_transform, + handle, + not_receiver, + not_transmitted_receiver, + not_caster, + ) in visible_meshes { let transform = transform.compute_matrix(); let previous_transform = previous_transform.map(|t| t.0).unwrap_or(transform); @@ -173,6 +183,9 @@ pub fn extract_meshes( } else { MeshFlags::SHADOW_RECEIVER }; + if not_transmitted_receiver.is_none() { + flags |= MeshFlags::TRANSMITTED_SHADOW_RECEIVER; + } if Mat3A::from_mat4(transform).determinant().is_sign_positive() { flags |= MeshFlags::SIGN_DETERMINANT_MODEL_3X3; } diff --git a/crates/bevy_pbr/src/render/mesh_types.wgsl b/crates/bevy_pbr/src/render/mesh_types.wgsl index d2e3276fe6..850dee84d4 100644 --- a/crates/bevy_pbr/src/render/mesh_types.wgsl +++ b/crates/bevy_pbr/src/render/mesh_types.wgsl @@ -15,5 +15,6 @@ struct SkinnedMesh { #endif const MESH_FLAGS_SHADOW_RECEIVER_BIT: u32 = 1u; +const MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT: u32 = 2u; // 2^31 - if the flag is set, the sign is positive, else it is negative const MESH_FLAGS_SIGN_DETERMINANT_MODEL_3X3_BIT: u32 = 2147483648u; diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index aaaf20f73a..f9b7a8221a 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -235,12 +235,13 @@ fn pbr( // R = vec3(0.0) // doesn't really matter // f_ab = vec2(0.1) // F0 = vec3(0.0) - if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u + var transmitted_shadow: f32 = 1.0; + if ((in.flags & (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT)) == (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT) && (point_lights.data[light_id].flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { - shadow = fetch_point_shadow(light_id, diffuse_transmissive_lobe_world_position, -in.world_normal); + transmitted_shadow = fetch_point_shadow(light_id, diffuse_transmissive_lobe_world_position, -in.world_normal); } let light_contrib = point_light(diffuse_transmissive_lobe_world_position.xyz, light_id, 1.0, 1.0, -in.N, -in.V, vec3(0.0), vec3(0.0), vec2(0.1), diffuse_transmissive_color); - transmitted_light += light_contrib * shadow; + transmitted_light += light_contrib * transmitted_shadow; } } @@ -265,12 +266,13 @@ fn pbr( // R = vec3(0.0) // doesn't really matter // f_ab = vec2(0.1) // F0 = vec3(0.0) - if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u + var transmitted_shadow: f32 = 1.0; + if ((in.flags & (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT)) == (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT) && (point_lights.data[light_id].flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { - shadow = fetch_spot_shadow(light_id, diffuse_transmissive_lobe_world_position, -in.world_normal); + transmitted_shadow = fetch_spot_shadow(light_id, diffuse_transmissive_lobe_world_position, -in.world_normal); } let light_contrib = spot_light(diffuse_transmissive_lobe_world_position.xyz, light_id, 1.0, 1.0, -in.N, -in.V, vec3(0.0), vec3(0.0), vec2(0.1), diffuse_transmissive_color); - transmitted_light += light_contrib * shadow; + transmitted_light += light_contrib * transmitted_shadow; } } @@ -298,12 +300,13 @@ fn pbr( // R = vec3(0.0) // doesn't really matter // f_ab = vec2(0.1) // F0 = vec3(0.0) - if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u + var transmitted_shadow: f32 = 1.0; + if ((in.flags & (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT)) == (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT) && (lights.directional_lights[i].flags & DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { - shadow = fetch_directional_shadow(i, diffuse_transmissive_lobe_world_position, -in.world_normal, view_z); + transmitted_shadow = fetch_directional_shadow(i, diffuse_transmissive_lobe_world_position, -in.world_normal, view_z); } let light_contrib = directional_light(i, 1.0, 1.0, -in.N, -in.V, vec3(0.0), vec3(0.0), vec2(0.1), diffuse_transmissive_color); - transmitted_light += light_contrib * shadow; + transmitted_light += light_contrib * transmitted_shadow; } }