Add ability to independently suppress shadows in diffuse transmission lobe

This commit is contained in:
Marco Buono 2023-04-16 00:33:26 -03:00
parent e2b816bc7c
commit 927cfa24fc
5 changed files with 50 additions and 20 deletions

View file

@ -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,

View file

@ -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)

View file

@ -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<Mesh>,
Option<With<NotShadowReceiver>>,
Option<With<NotTransmittedShadowReceiver>>,
Option<With<NotShadowCaster>>,
)>,
>,
@ -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;
}

View file

@ -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;

View file

@ -235,12 +235,13 @@ fn pbr(
// R = vec3<f32>(0.0) // doesn't really matter
// f_ab = vec2<f32>(0.1)
// F0 = vec3<f32>(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<f32>(0.0), vec3<f32>(0.0), vec2<f32>(0.1), diffuse_transmissive_color);
transmitted_light += light_contrib * shadow;
transmitted_light += light_contrib * transmitted_shadow;
}
}
@ -265,12 +266,13 @@ fn pbr(
// R = vec3<f32>(0.0) // doesn't really matter
// f_ab = vec2<f32>(0.1)
// F0 = vec3<f32>(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<f32>(0.0), vec3<f32>(0.0), vec2<f32>(0.1), diffuse_transmissive_color);
transmitted_light += light_contrib * shadow;
transmitted_light += light_contrib * transmitted_shadow;
}
}
@ -298,12 +300,13 @@ fn pbr(
// R = vec3<f32>(0.0) // doesn't really matter
// f_ab = vec2<f32>(0.1)
// F0 = vec3<f32>(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<f32>(0.0), vec3<f32>(0.0), vec2<f32>(0.1), diffuse_transmissive_color);
transmitted_light += light_contrib * shadow;
transmitted_light += light_contrib * transmitted_shadow;
}
}