diff --git a/Cargo.toml b/Cargo.toml index dd904e1217590..b7388b8ed8094 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1250,6 +1250,19 @@ description = "Showcases different blend modes" category = "3D Rendering" wasm = true +[[example]] +name = "contact_shadows" +path = "examples/3d/contact_shadows.rs" +# Causes an ICE on docs.rs +doc-scrape-examples = false +required-features = ["bluenoise_texture"] + +[package.metadata.example.contact_shadows] +name = "Contact Shadows" +description = "Showcases how contact shadows add shadow detail" +category = "3D Rendering" +wasm = true + [[example]] name = "lighting" path = "examples/3d/lighting.rs" diff --git a/crates/bevy_light/src/cascade.rs b/crates/bevy_light/src/cascade.rs index a6ef7d4945e53..cd2058d226719 100644 --- a/crates/bevy_light/src/cascade.rs +++ b/crates/bevy_light/src/cascade.rs @@ -185,7 +185,7 @@ pub struct Cascade { pub fn clear_directional_light_cascades(mut lights: Query<(&DirectionalLight, &mut Cascades)>) { for (directional_light, mut cascades) in lights.iter_mut() { - if !directional_light.shadows_enabled { + if !directional_light.shadow_maps_enabled { continue; } cascades.cascades.clear(); @@ -214,7 +214,7 @@ pub fn build_directional_light_cascades( .collect::>(); for (transform, directional_light, cascades_config, mut cascades) in &mut lights { - if !directional_light.shadows_enabled { + if !directional_light.shadow_maps_enabled { continue; } diff --git a/crates/bevy_light/src/cluster/assign.rs b/crates/bevy_light/src/cluster/assign.rs index e629ccf7db88e..69f068db15a98 100644 --- a/crates/bevy_light/src/cluster/assign.rs +++ b/crates/bevy_light/src/cluster/assign.rs @@ -60,7 +60,7 @@ pub enum ClusterableObjectType { /// Whether shadows are enabled for this point light. /// /// This is used for sorting the light list. - shadows_enabled: bool, + shadow_maps_enabled: bool, /// Whether this light interacts with volumetrics. /// @@ -73,7 +73,7 @@ pub enum ClusterableObjectType { /// Whether shadows are enabled for this spot light. /// /// This is used for sorting the light list. - shadows_enabled: bool, + shadow_maps_enabled: bool, /// Whether this light interacts with volumetrics. /// @@ -105,14 +105,14 @@ impl ClusterableObjectType { pub fn ordering(&self) -> (u8, bool, bool) { match *self { ClusterableObjectType::PointLight { - shadows_enabled, + shadow_maps_enabled, volumetric, - } => (0, !shadows_enabled, !volumetric), + } => (0, !shadow_maps_enabled, !volumetric), ClusterableObjectType::SpotLight { - shadows_enabled, + shadow_maps_enabled, volumetric, .. - } => (1, !shadows_enabled, !volumetric), + } => (1, !shadow_maps_enabled, !volumetric), ClusterableObjectType::ReflectionProbe => (2, false, false), ClusterableObjectType::IrradianceVolume => (3, false, false), ClusterableObjectType::Decal => (4, false, false), @@ -178,7 +178,7 @@ pub(crate) fn assign_objects_to_clusters( transform: GlobalTransform::from_translation(transform.translation()), range: point_light.range, object_type: ClusterableObjectType::PointLight { - shadows_enabled: point_light.shadows_enabled, + shadow_maps_enabled: point_light.shadow_maps_enabled, volumetric: volumetric.is_some(), }, render_layers: maybe_layers.unwrap_or_default().clone(), @@ -198,7 +198,7 @@ pub(crate) fn assign_objects_to_clusters( range: spot_light.range, object_type: ClusterableObjectType::SpotLight { outer_angle: spot_light.outer_angle, - shadows_enabled: spot_light.shadows_enabled, + shadow_maps_enabled: spot_light.shadow_maps_enabled, volumetric: volumetric.is_some(), }, render_layers: maybe_layers.unwrap_or_default().clone(), diff --git a/crates/bevy_light/src/directional_light.rs b/crates/bevy_light/src/directional_light.rs index c3360514779e4..adc085525ff87 100644 --- a/crates/bevy_light/src/directional_light.rs +++ b/crates/bevy_light/src/directional_light.rs @@ -47,7 +47,7 @@ use super::{ /// /// ## Shadows /// -/// To enable shadows, set the `shadows_enabled` property to `true`. +/// To enable shadows, set the `shadow_maps_enabled` property to `true`. /// /// Shadows are produced via [cascaded shadow maps](https://developer.download.nvidia.com/SDK/10.5/opengl/src/cascaded_shadow_maps/doc/cascaded_shadow_maps.pdf). /// @@ -86,7 +86,10 @@ pub struct DirectionalLight { /// Note that shadows are rather expensive and become more so with every /// light that casts them. In general, it's best to aggressively limit the /// number of lights with shadows enabled to one or two at most. - pub shadows_enabled: bool, + pub shadow_maps_enabled: bool, + + /// Whether this light casts contact shadows. + pub contact_shadows_enabled: bool, /// Whether soft shadows are enabled, and if so, the size of the light. /// @@ -142,7 +145,8 @@ impl Default for DirectionalLight { DirectionalLight { color: Color::WHITE, illuminance: light_consts::lux::AMBIENT_DAYLIGHT, - shadows_enabled: false, + shadow_maps_enabled: false, + contact_shadows_enabled: false, shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS, shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS, affects_lightmapped_mesh_diffuse: true, @@ -221,7 +225,7 @@ pub fn update_directional_light_frusta( // The frustum is used for culling meshes to the light for shadow mapping // so if shadow mapping is disabled for this light, then the frustum is // not needed. - if !directional_light.shadows_enabled || !visibility.get() { + if !directional_light.shadow_maps_enabled || !visibility.get() { continue; } diff --git a/crates/bevy_light/src/lib.rs b/crates/bevy_light/src/lib.rs index 8432fdac75fcb..d909ac478da0d 100644 --- a/crates/bevy_light/src/lib.rs +++ b/crates/bevy_light/src/lib.rs @@ -358,7 +358,7 @@ pub fn check_dir_light_mesh_visibility( } // NOTE: If shadow mapping is disabled for the light then it must have no visible entities - if !directional_light.shadows_enabled || !light_view_visibility.get() { + if !directional_light.shadow_maps_enabled || !light_view_visibility.get() { continue; } @@ -524,7 +524,7 @@ pub fn check_point_light_mesh_visibility( } // NOTE: If shadow mapping is disabled for the light then it must have no visible entities - if !point_light.shadows_enabled { + if !point_light.shadow_maps_enabled { continue; } @@ -613,7 +613,7 @@ pub fn check_point_light_mesh_visibility( visible_entities.clear(); // NOTE: If shadow mapping is disabled for the light then it must have no visible entities - if !point_light.shadows_enabled { + if !point_light.shadow_maps_enabled { continue; } diff --git a/crates/bevy_light/src/point_light.rs b/crates/bevy_light/src/point_light.rs index 714920c09e70f..16760f7a252f8 100644 --- a/crates/bevy_light/src/point_light.rs +++ b/crates/bevy_light/src/point_light.rs @@ -35,7 +35,7 @@ use crate::{ /// /// ## Shadows /// -/// To enable shadows, set the `shadows_enabled` property to `true`. +/// To enable shadows, set the `shadow_maps_enabled` property to `true`. /// /// To control the resolution of the shadow maps, use the [`PointLightShadowMap`] resource. #[derive(Component, Debug, Clone, Copy, Reflect)] @@ -70,7 +70,10 @@ pub struct PointLight { pub radius: f32, /// Whether this light casts shadows. - pub shadows_enabled: bool, + pub shadow_maps_enabled: bool, + + /// Whether this light casts contact shadows. + pub contact_shadows_enabled: bool, /// Whether soft shadows are enabled. /// @@ -132,7 +135,8 @@ impl Default for PointLight { intensity: light_consts::lumens::VERY_LARGE_CINEMA_LIGHT, range: 20.0, radius: 0.0, - shadows_enabled: false, + shadow_maps_enabled: false, + contact_shadows_enabled: false, affects_lightmapped_mesh_diffuse: true, shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS, shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS, @@ -214,7 +218,7 @@ pub fn update_point_light_frusta( // not needed. // Also, if the light is not relevant for any cluster, it will not be in the // global lights set and so there is no need to update its frusta. - if !point_light.shadows_enabled || !global_lights.entities.contains(&entity) { + if !point_light.shadow_maps_enabled || !global_lights.entities.contains(&entity) { continue; } diff --git a/crates/bevy_light/src/spot_light.rs b/crates/bevy_light/src/spot_light.rs index 00026ff0328e9..d8000d2fb3940 100644 --- a/crates/bevy_light/src/spot_light.rs +++ b/crates/bevy_light/src/spot_light.rs @@ -53,7 +53,11 @@ pub struct SpotLight { /// Note that shadows are rather expensive and become more so with every /// light that casts them. In general, it's best to aggressively limit the /// number of lights with shadows enabled to one or two at most. - pub shadows_enabled: bool, + pub shadow_maps_enabled: bool, + + /// Whether this light casts contact shadows. Cameras must also have the `ContactShadows` + /// component. + pub contact_shadows_enabled: bool, /// Whether soft shadows are enabled. /// @@ -142,7 +146,8 @@ impl Default for SpotLight { intensity: 1_000_000.0, range: 20.0, radius: 0.0, - shadows_enabled: false, + shadow_maps_enabled: false, + contact_shadows_enabled: false, affects_lightmapped_mesh_diffuse: true, shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS, shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS, @@ -214,7 +219,7 @@ pub fn update_spot_light_frusta( // not needed. // Also, if the light is not relevant for any cluster, it will not be in the // global lights set and so there is no need to update its frusta. - if !spot_light.shadows_enabled || !global_lights.entities.contains(&entity) { + if !spot_light.shadow_maps_enabled || !global_lights.entities.contains(&entity) { continue; } diff --git a/crates/bevy_light/src/volumetric.rs b/crates/bevy_light/src/volumetric.rs index 31c73db6bffdb..080617bfc9469 100644 --- a/crates/bevy_light/src/volumetric.rs +++ b/crates/bevy_light/src/volumetric.rs @@ -8,7 +8,7 @@ use bevy_reflect::prelude::*; use bevy_transform::components::Transform; /// Add this component to a [`DirectionalLight`](crate::DirectionalLight) with a shadow map -/// (`shadows_enabled: true`) to make volumetric fog interact with it. +/// (`shadow_maps_enabled: true`) to make volumetric fog interact with it. /// /// This allows the light to generate light shafts/god rays. #[derive(Clone, Copy, Component, Default, Debug, Reflect)] diff --git a/crates/bevy_pbr/src/contact_shadows.rs b/crates/bevy_pbr/src/contact_shadows.rs new file mode 100644 index 0000000000000..d443b88e9f8bb --- /dev/null +++ b/crates/bevy_pbr/src/contact_shadows.rs @@ -0,0 +1,140 @@ +//! Contact shadows implemented via screenspace raymarching. + +use bevy_app::{App, Plugin}; +use bevy_derive::{Deref, DerefMut}; +use bevy_ecs::{ + component::Component, + entity::Entity, + query::{QueryItem, With}, + reflect::ReflectComponent, + resource::Resource, + schedule::IntoScheduleConfigs, + system::{Commands, Query, Res, ResMut}, +}; +use bevy_reflect::{std_traits::ReflectDefault, Reflect}; +use bevy_render::{ + extract_component::{ExtractComponent, ExtractComponentPlugin}, + render_resource::{DynamicUniformBuffer, ShaderType}, + renderer::{RenderDevice, RenderQueue}, + view::ExtractedView, + Render, RenderApp, RenderSystems, +}; +use bevy_utils::default; + +/// Enables contact shadows for a camera. +pub struct ContactShadowsPlugin; + +/// Add this component to a camera to enable contact shadows. +/// +/// Contact shadows are a screen-space technique that adds small-scale shadows +/// in areas where traditional shadow maps may lack detail, such as where +/// objects touch the ground. +/// +/// This can be used in forward or deferred rendering, but the depth prepass is required. +#[derive(Clone, Copy, Component, Reflect)] +#[reflect(Component, Default, Clone)] +#[require(bevy_core_pipeline::prepass::DepthPrepass)] +pub struct ContactShadows { + /// The number of steps to be taken at regular intervals to find an initial + /// intersection. + pub linear_steps: u32, + /// When marching the depth buffer, we only have 2.5D information and don't + /// know how thick surfaces are. We shall assume that the depth buffer + /// fragments are cuboids with a constant thickness defined by this + /// parameter. + pub thickness: f32, + /// The length of the contact shadow ray in world space. + pub length: f32, +} + +impl Default for ContactShadows { + fn default() -> Self { + Self { + linear_steps: 16, + thickness: 0.1, + length: 0.3, + } + } +} + +/// A version of [`ContactShadows`] for upload to the GPU. +#[derive(Clone, Copy, Component, ShaderType, Default)] +pub struct ContactShadowsUniform { + pub linear_steps: u32, + pub thickness: f32, + pub length: f32, +} + +impl From for ContactShadowsUniform { + fn from(settings: ContactShadows) -> Self { + Self { + linear_steps: settings.linear_steps, + thickness: settings.thickness, + length: settings.length, + } + } +} + +impl ExtractComponent for ContactShadows { + type QueryData = &'static ContactShadows; + type QueryFilter = (); + type Out = ContactShadows; + + fn extract_component(settings: QueryItem<'_, '_, Self::QueryData>) -> Option { + Some(*settings) + } +} + +/// A GPU buffer that stores the contact shadow settings for each view. +#[derive(Resource, Default)] +pub struct ContactShadowsBuffer(pub DynamicUniformBuffer); + +impl Plugin for ContactShadowsPlugin { + fn build(&self, app: &mut App) { + app.register_type::() + .add_plugins(ExtractComponentPlugin::::default()); + + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { + return; + }; + + render_app + .init_resource::() + .add_systems( + Render, + prepare_contact_shadows_settings.in_set(RenderSystems::PrepareResources), + ); + } +} + +fn prepare_contact_shadows_settings( + mut commands: Commands, + views: Query<(Entity, Option<&ContactShadows>), With>, + mut contact_shadows_buffer: ResMut, + render_device: Res, + render_queue: Res, +) { + contact_shadows_buffer.0.clear(); + for (entity, settings) in &views { + let uniform = if let Some(settings) = settings { + ContactShadowsUniform::from(*settings) + } else { + ContactShadowsUniform { + linear_steps: 0, + ..default() + } + }; + let offset = contact_shadows_buffer.0.push(&uniform); + commands + .entity(entity) + .insert(ViewContactShadowsUniformOffset(offset)); + } + contact_shadows_buffer + .0 + .write_buffer(&render_device, &render_queue); +} + +/// A component that stores the offset within the [`ContactShadowsBuffer`] for +/// each view. +#[derive(Component, Default, Deref, DerefMut)] +pub struct ViewContactShadowsUniformOffset(pub u32); diff --git a/crates/bevy_pbr/src/deferred/mod.rs b/crates/bevy_pbr/src/deferred/mod.rs index 41766372d9f36..fdac9335e15d9 100644 --- a/crates/bevy_pbr/src/deferred/mod.rs +++ b/crates/bevy_pbr/src/deferred/mod.rs @@ -1,8 +1,9 @@ use crate::{ graph::NodePbr, MeshPipeline, MeshViewBindGroup, RenderViewLightProbes, - ScreenSpaceAmbientOcclusion, ScreenSpaceReflectionsUniform, ViewEnvironmentMapUniformOffset, - ViewLightProbesUniformOffset, ViewScreenSpaceReflectionsUniformOffset, - TONEMAPPING_LUT_SAMPLER_BINDING_INDEX, TONEMAPPING_LUT_TEXTURE_BINDING_INDEX, + ScreenSpaceAmbientOcclusion, ScreenSpaceReflectionsUniform, ViewContactShadowsUniformOffset, + ViewEnvironmentMapUniformOffset, ViewLightProbesUniformOffset, + ViewScreenSpaceReflectionsUniformOffset, TONEMAPPING_LUT_SAMPLER_BINDING_INDEX, + TONEMAPPING_LUT_TEXTURE_BINDING_INDEX, }; use crate::{ DistanceFog, ExtractedAtmosphere, MeshPipelineKey, ViewFogUniformOffset, @@ -139,6 +140,7 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode { &'static ViewFogUniformOffset, &'static ViewLightProbesUniformOffset, &'static ViewScreenSpaceReflectionsUniformOffset, + &'static ViewContactShadowsUniformOffset, &'static ViewEnvironmentMapUniformOffset, &'static MeshViewBindGroup, &'static ViewTarget, @@ -156,6 +158,7 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode { view_fog_offset, view_light_probes_offset, view_ssr_offset, + view_contact_shadows_offset, view_environment_map_offset, mesh_view_bind_group, target, @@ -215,6 +218,7 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode { view_fog_offset.offset, **view_light_probes_offset, **view_ssr_offset, + **view_contact_shadows_offset, **view_environment_map_offset, ], ); diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 79163cb648134..4c6eb726101b4 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -27,6 +27,11 @@ pub mod experimental { mod atmosphere; mod cluster; mod components; +pub mod contact_shadows; +pub use contact_shadows::{ + ContactShadows, ContactShadowsBuffer, ContactShadowsPlugin, ContactShadowsUniform, + ViewContactShadowsUniformOffset, +}; pub mod decal; pub mod deferred; pub mod diagnostic; @@ -79,6 +84,7 @@ pub use volumetric_fog::VolumetricFogPlugin; pub mod prelude { #[doc(hidden)] pub use crate::{ + contact_shadows::ContactShadowsPlugin, fog::{DistanceFog, FogFalloff}, material::{Material, MaterialPlugin}, mesh_material::MeshMaterial3d, @@ -153,8 +159,8 @@ fn shader_ref(path: PathBuf) -> ShaderRef { ShaderRef::Path(AssetPath::from_path_buf(path).with_source("embedded")) } -pub const TONEMAPPING_LUT_TEXTURE_BINDING_INDEX: u32 = 18; -pub const TONEMAPPING_LUT_SAMPLER_BINDING_INDEX: u32 = 19; +pub const TONEMAPPING_LUT_TEXTURE_BINDING_INDEX: u32 = 19; +pub const TONEMAPPING_LUT_SAMPLER_BINDING_INDEX: u32 = 20; /// Sets up the entire PBR infrastructure of bevy. pub struct PbrPlugin { @@ -241,6 +247,7 @@ impl Plugin for PbrPlugin { VolumetricFogPlugin, ScreenSpaceReflectionsPlugin, ClusteredDecalPlugin, + ContactShadowsPlugin, )) .add_plugins(( decal::ForwardDecalPlugin, diff --git a/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs b/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs index 890df912f4e93..dbe097391c99a 100644 --- a/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs +++ b/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs @@ -7,8 +7,9 @@ use super::{ InstanceManager, }; use crate::{ - MeshViewBindGroup, PrepassViewBindGroup, ViewEnvironmentMapUniformOffset, ViewFogUniformOffset, - ViewLightProbesUniformOffset, ViewLightsUniformOffset, ViewScreenSpaceReflectionsUniformOffset, + MeshViewBindGroup, PrepassViewBindGroup, ViewContactShadowsUniformOffset, + ViewEnvironmentMapUniformOffset, ViewFogUniformOffset, ViewLightProbesUniformOffset, + ViewLightsUniformOffset, ViewScreenSpaceReflectionsUniformOffset, }; use bevy_camera::MainPassResolutionOverride; use bevy_camera::Viewport; @@ -44,6 +45,7 @@ impl ViewNode for MeshletMainOpaquePass3dNode { &'static ViewFogUniformOffset, &'static ViewLightProbesUniformOffset, &'static ViewScreenSpaceReflectionsUniformOffset, + &'static ViewContactShadowsUniformOffset, &'static ViewEnvironmentMapUniformOffset, Option<&'static MainPassResolutionOverride>, &'static MeshletViewMaterialsMainOpaquePass, @@ -64,6 +66,7 @@ impl ViewNode for MeshletMainOpaquePass3dNode { view_fog_offset, view_light_probes_offset, view_ssr_offset, + view_contact_shadows_offset, view_environment_map_offset, resolution_override, meshlet_view_materials, @@ -123,6 +126,7 @@ impl ViewNode for MeshletMainOpaquePass3dNode { view_fog_offset.offset, **view_light_probes_offset, **view_ssr_offset, + **view_contact_shadows_offset, **view_environment_map_offset, ], ); diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 9c95f0711493d..0415f7691c815 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -74,7 +74,8 @@ pub struct ExtractedPointLight { pub range: f32, pub radius: f32, pub transform: GlobalTransform, - pub shadows_enabled: bool, + pub shadow_maps_enabled: bool, + pub contact_shadows_enabled: bool, pub shadow_depth_bias: f32, pub shadow_normal_bias: f32, pub shadow_map_near_z: f32, @@ -90,7 +91,8 @@ pub struct ExtractedDirectionalLight { pub color: LinearRgba, pub illuminance: f32, pub transform: GlobalTransform, - pub shadows_enabled: bool, + pub shadow_maps_enabled: bool, + pub contact_shadows_enabled: bool, pub volumetric: bool, /// whether this directional light contributes diffuse light to lightmapped /// meshes @@ -112,10 +114,11 @@ pub struct ExtractedDirectionalLight { bitflags::bitflags! { #[repr(transparent)] struct PointLightFlags: u32 { - const SHADOWS_ENABLED = 1 << 0; + const SHADOW_MAPS_ENABLED = 1 << 0; const SPOT_LIGHT_Y_NEGATIVE = 1 << 1; const VOLUMETRIC = 1 << 2; const AFFECTS_LIGHTMAPPED_MESH_DIFFUSE = 1 << 3; + const CONTACT_SHADOWS_ENABLED = 1 << 4; const NONE = 0; const UNINITIALIZED = 0xFFFF; } @@ -149,9 +152,10 @@ pub struct GpuDirectionalLight { bitflags::bitflags! { #[repr(transparent)] struct DirectionalLightFlags: u32 { - const SHADOWS_ENABLED = 1 << 0; + const SHADOW_MAPS_ENABLED = 1 << 0; const VOLUMETRIC = 1 << 1; const AFFECTS_LIGHTMAPPED_MESH_DIFFUSE = 1 << 2; + const CONTACT_SHADOWS_ENABLED = 1 << 3; const NONE = 0; const UNINITIALIZED = 0xFFFF; } @@ -410,7 +414,8 @@ pub fn extract_lights( range: point_light.range, radius: point_light.radius, transform: *transform, - shadows_enabled: point_light.shadows_enabled, + shadow_maps_enabled: point_light.shadow_maps_enabled, + contact_shadows_enabled: point_light.contact_shadows_enabled, shadow_depth_bias: point_light.shadow_depth_bias, // The factor of SQRT_2 is for the worst-case diagonal offset shadow_normal_bias: point_light.shadow_normal_bias @@ -475,7 +480,8 @@ pub fn extract_lights( range: spot_light.range, radius: spot_light.radius, transform: *transform, - shadows_enabled: spot_light.shadows_enabled, + shadow_maps_enabled: spot_light.shadow_maps_enabled, + contact_shadows_enabled: spot_light.contact_shadows_enabled, shadow_depth_bias: spot_light.shadow_depth_bias, // The factor of SQRT_2 is for the worst-case diagonal offset shadow_normal_bias: spot_light.shadow_normal_bias @@ -571,7 +577,8 @@ pub fn extract_lights( soft_shadow_size: directional_light.soft_shadow_size, #[cfg(not(feature = "experimental_pbr_pcss"))] soft_shadow_size: None, - shadows_enabled: directional_light.shadows_enabled, + shadow_maps_enabled: directional_light.shadow_maps_enabled, + contact_shadows_enabled: directional_light.contact_shadows_enabled, shadow_depth_bias: directional_light.shadow_depth_bias, // The factor of SQRT_2 is for the worst-case diagonal offset shadow_normal_bias: directional_light.shadow_normal_bias @@ -816,7 +823,7 @@ pub fn prepare_lights( let point_light_shadow_maps_count = point_lights .iter() - .filter(|light| light.2.shadows_enabled && light.2.spot_light_angles.is_none()) + .filter(|light| light.2.shadow_maps_enabled && light.2.spot_light_angles.is_none()) .count() .min(max_texture_cubes); @@ -830,7 +837,7 @@ pub fn prepare_lights( let directional_shadow_enabled_count = directional_lights .iter() .take(MAX_DIRECTIONAL_LIGHTS) - .filter(|(_, _, light)| light.shadows_enabled) + .filter(|(_, _, light)| light.shadow_maps_enabled) .count() .min(max_texture_array_layers / MAX_CASCADES_PER_LIGHT); @@ -848,7 +855,7 @@ pub fn prepare_lights( let spot_light_shadow_maps_count = point_lights .iter() - .filter(|(_, _, light, _)| light.shadows_enabled && light.spot_light_angles.is_some()) + .filter(|(_, _, light, _)| light.shadow_maps_enabled && light.spot_light_angles.is_some()) .count() .min(max_texture_array_layers - directional_shadow_enabled_count * MAX_CASCADES_PER_LIGHT); @@ -875,7 +882,7 @@ pub fn prepare_lights( // - because entities are unique, we can use `sort_unstable_by_key` // and still end up with a stable order. directional_lights.sort_unstable_by_key(|(entity, _, light)| { - (light.volumetric, light.shadows_enabled, *entity) + (light.volumetric, light.shadow_maps_enabled, *entity) }); if global_light_meta.entity_to_index.capacity() < point_lights.len() { @@ -889,12 +896,16 @@ pub fn prepare_lights( let mut flags = PointLightFlags::NONE; // Lights are sorted, shadow enabled lights are first - if light.shadows_enabled + if light.shadow_maps_enabled && (index < point_light_shadow_maps_count || (light.spot_light_angles.is_some() && index - point_light_count < spot_light_shadow_maps_count)) { - flags |= PointLightFlags::SHADOWS_ENABLED; + flags |= PointLightFlags::SHADOW_MAPS_ENABLED; + } + + if light.contact_shadows_enabled { + flags |= PointLightFlags::CONTACT_SHADOWS_ENABLED; } let cube_face_projection = Mat4::perspective_infinite_reverse_rh( @@ -902,7 +913,7 @@ pub fn prepare_lights( 1.0, light.shadow_map_near_z, ); - if light.shadows_enabled + if light.shadow_maps_enabled && light.volumetric && (index < point_light_volumetric_enabled_count || (light.spot_light_angles.is_some() @@ -995,7 +1006,7 @@ pub fn prepare_lights( let render_layers = maybe_layers.unwrap_or_default(); for (_light_entity, _, light) in directional_lights.iter() { - if light.shadows_enabled && light.render_layers.intersects(render_layers) { + if light.shadow_maps_enabled && light.render_layers.intersects(render_layers) { num_directional_cascades_for_this_view += light .cascade_shadow_config .bounds @@ -1172,7 +1183,7 @@ pub fn prepare_lights( // Lights are sorted, volumetric and shadow enabled lights are first if light.volumetric - && light.shadows_enabled + && light.shadow_maps_enabled && (index < directional_volumetric_enabled_count) { flags |= DirectionalLightFlags::VOLUMETRIC; @@ -1180,7 +1191,7 @@ pub fn prepare_lights( // Shadow enabled lights are second let mut num_cascades = 0; - if light.shadows_enabled { + if light.shadow_maps_enabled { let cascades = light .cascade_shadow_config .bounds @@ -1190,11 +1201,15 @@ pub fn prepare_lights( if num_directional_cascades_enabled_for_this_view + cascades <= max_texture_array_layers { - flags |= DirectionalLightFlags::SHADOWS_ENABLED; + flags |= DirectionalLightFlags::SHADOW_MAPS_ENABLED; num_cascades += cascades; } } + if light.contact_shadows_enabled { + flags |= DirectionalLightFlags::CONTACT_SHADOWS_ENABLED; + } + if light.affects_lightmapped_mesh_diffuse { flags |= DirectionalLightFlags::AFFECTS_LIGHTMAPPED_MESH_DIFFUSE; } @@ -1256,7 +1271,7 @@ pub fn prepare_lights( continue; }; - if !light.shadows_enabled { + if !light.shadow_maps_enabled { if let Some(entities) = light_view_entities.remove(&entity) { despawn_entities(&mut commands, entities); } @@ -1380,7 +1395,7 @@ pub fn prepare_lights( continue; }; - if !light.shadows_enabled { + if !light.shadow_maps_enabled { if let Some(entities) = light_view_entities.remove(&entity) { despawn_entities(&mut commands, entities); } @@ -1495,7 +1510,7 @@ pub fn prepare_lights( let gpu_light = &mut gpu_lights.directional_lights[light_index]; // Only deal with cascades when shadows are enabled. - if (gpu_light.flags & DirectionalLightFlags::SHADOWS_ENABLED.bits()) == 0u32 { + if (gpu_light.flags & DirectionalLightFlags::SHADOW_MAPS_ENABLED.bits()) == 0u32 { if let Some(entities) = light_view_entities.remove(&entity) { despawn_entities(&mut commands, entities); } @@ -2363,11 +2378,11 @@ fn point_or_spot_light_to_clusterable(point_light: &ExtractedPointLight) -> Clus match point_light.spot_light_angles { Some((_, outer_angle)) => ClusterableObjectType::SpotLight { outer_angle, - shadows_enabled: point_light.shadows_enabled, + shadow_maps_enabled: point_light.shadow_maps_enabled, volumetric: point_light.volumetric, }, None => ClusterableObjectType::PointLight { - shadows_enabled: point_light.shadows_enabled, + shadow_maps_enabled: point_light.shadow_maps_enabled, volumetric: point_light.volumetric, }, } diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 3e3842a23faf5..c38c9a53e9566 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -1,3 +1,4 @@ +use crate::contact_shadows::ViewContactShadowsUniformOffset; use crate::{ material_bind_groups::{MaterialBindGroupIndex, MaterialBindGroupSlot}, resources::write_atmosphere_buffer, @@ -2430,6 +2431,9 @@ impl SpecializedMeshPipeline for MeshPipeline { if cfg!(feature = "pbr_specular_textures") { shader_defs.push("PBR_SPECULAR_TEXTURES_SUPPORTED".into()); } + if cfg!(feature = "bluenoise_texture") { + shader_defs.push("BLUE_NOISE_TEXTURE".into()); + } let bind_group_layout = self.get_view_layout(key.into()); let mut bind_group_layout = vec![ @@ -2998,6 +3002,7 @@ impl RenderCommand

for SetMeshViewBindGroup Read, Read, Read, + Read, Read, Read, Option>, @@ -3013,6 +3018,7 @@ impl RenderCommand

for SetMeshViewBindGroup view_fog, view_light_probes, view_ssr, + view_contact_shadows, view_environment_map, mesh_view_bind_group, maybe_oit_layers_count_offset, @@ -3027,6 +3033,7 @@ impl RenderCommand

for SetMeshViewBindGroup view_fog.offset, **view_light_probes, **view_ssr, + **view_contact_shadows, **view_environment_map, ]; if let Some(layers_count_offset) = maybe_oit_layers_count_offset { diff --git a/crates/bevy_pbr/src/render/mesh_view_bindings.rs b/crates/bevy_pbr/src/render/mesh_view_bindings.rs index 78e7f3e6d7dcb..579e6517e57f8 100644 --- a/crates/bevy_pbr/src/render/mesh_view_bindings.rs +++ b/crates/bevy_pbr/src/render/mesh_view_bindings.rs @@ -33,6 +33,9 @@ use bevy_render::{ use core::{array, num::NonZero}; use crate::{ + contact_shadows::{ + ContactShadowsBuffer, ContactShadowsUniform, ViewContactShadowsUniformOffset, + }, decal::{ self, clustered::{ @@ -45,11 +48,12 @@ use crate::{ }, prepass, resources::{AtmosphereBuffer, AtmosphereData, AtmosphereSampler, AtmosphereTextures}, - EnvironmentMapUniformBuffer, ExtractedAtmosphere, FogMeta, GlobalClusterableObjectMeta, - GpuClusterableObjects, GpuFog, GpuLights, LightMeta, LightProbesBuffer, LightProbesUniform, - MeshPipeline, MeshPipelineKey, RenderViewLightProbes, ScreenSpaceAmbientOcclusionResources, - ScreenSpaceReflectionsBuffer, ScreenSpaceReflectionsUniform, ShadowSamplers, - ViewClusterBindings, ViewShadowBindings, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, + Bluenoise, EnvironmentMapUniformBuffer, ExtractedAtmosphere, FogMeta, + GlobalClusterableObjectMeta, GpuClusterableObjects, GpuFog, GpuLights, LightMeta, + LightProbesBuffer, LightProbesUniform, MeshPipeline, MeshPipelineKey, RenderViewLightProbes, + ScreenSpaceAmbientOcclusionResources, ScreenSpaceReflectionsBuffer, + ScreenSpaceReflectionsUniform, ShadowSamplers, ViewClusterBindings, ViewShadowBindings, + CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, }; #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] @@ -84,6 +88,7 @@ bitflags::bitflags! { const DEFERRED_PREPASS = 1 << 4; const OIT_ENABLED = 1 << 5; const ATMOSPHERE = 1 << 6; + const STBN = 1 << 7; } } @@ -96,7 +101,7 @@ impl MeshPipelineViewLayoutKey { use MeshPipelineViewLayoutKey as Key; format!( - "mesh_view_layout{}{}{}{}{}{}{}", + "mesh_view_layout{}{}{}{}{}{}{}{}", if self.contains(Key::MULTISAMPLED) { "_multisampled" } else { @@ -132,6 +137,11 @@ impl MeshPipelineViewLayoutKey { } else { Default::default() }, + if self.contains(Key::STBN) { + "_stbn" + } else { + Default::default() + }, ) } } @@ -162,6 +172,10 @@ impl From for MeshPipelineViewLayoutKey { result |= MeshPipelineViewLayoutKey::ATMOSPHERE; } + if cfg!(feature = "bluenoise_texture") { + result |= MeshPipelineViewLayoutKey::STBN; + } + result } } @@ -336,20 +350,22 @@ fn layout_entries( ), // Screen space reflection settings (15, uniform_buffer::(true)), + // Contact shadows settings + (16, uniform_buffer::(true)), // Screen space ambient occlusion texture ( - 16, + 17, texture_2d(TextureSampleType::Float { filterable: false }), ), - (17, environment_map_entries[3]), + (18, environment_map_entries[3]), ), ); // Tonemapping let tonemapping_lut_entries = get_lut_bind_group_layout_entries(); entries = entries.extend_with_indices(( - (18, tonemapping_lut_entries[0]), - (19, tonemapping_lut_entries[1]), + (19, tonemapping_lut_entries[0]), + (20, tonemapping_lut_entries[1]), )); // Prepass @@ -359,7 +375,7 @@ fn layout_entries( { for (entry, binding) in prepass::get_bind_group_layout_entries(layout_key) .iter() - .zip([20, 21, 22, 23]) + .zip([21, 22, 23, 24]) { if let Some(entry) = entry { entries = entries.extend_with_indices(((binding as u32, *entry),)); @@ -370,10 +386,10 @@ fn layout_entries( // View Transmission Texture entries = entries.extend_with_indices(( ( - 24, + 25, texture_2d(TextureSampleType::Float { filterable: true }), ), - (25, sampler(SamplerBindingType::Filtering)), + (26, sampler(SamplerBindingType::Filtering)), )); // OIT @@ -384,12 +400,12 @@ fn layout_entries( if is_oit_supported(render_adapter, render_device, false) { entries = entries.extend_with_indices(( // oit_layers - (26, storage_buffer_sized(false, None)), - // oit_layer_ids, (27, storage_buffer_sized(false, None)), + // oit_layer_ids, + (28, storage_buffer_sized(false, None)), // oit_layer_count ( - 28, + 29, uniform_buffer::(true), ), )); @@ -401,15 +417,23 @@ fn layout_entries( entries = entries.extend_with_indices(( // transmittance LUT ( - 29, + 30, texture_2d(TextureSampleType::Float { filterable: true }), ), - (30, sampler(SamplerBindingType::Filtering)), + (31, sampler(SamplerBindingType::Filtering)), // atmosphere data buffer - (31, storage_buffer_read_only::(false)), + (32, storage_buffer_read_only::(false)), )); } + // Blue noise + if layout_key.contains(MeshPipelineViewLayoutKey::STBN) { + entries = entries.extend_with_indices((( + 33, + texture_2d_array(TextureSampleType::Float { filterable: false }), + ),)); + } + let mut binding_array_entries = DynamicBindGroupLayoutEntries::new(ShaderStages::FRAGMENT); binding_array_entries = binding_array_entries.extend_with_indices(( (0, environment_map_entries[0]), @@ -586,6 +610,7 @@ pub fn prepare_mesh_view_bind_groups( Has, Option<&AtmosphereTextures>, Has, + Option<&ViewContactShadowsUniformOffset>, )>, (images, mut fallback_images, fallback_image, fallback_image_zero): ( Res>, @@ -597,13 +622,17 @@ pub fn prepare_mesh_view_bind_groups( tonemapping_luts: Res, light_probes_buffer: Res, visibility_ranges: Res, - ssr_buffer: Res, + (ssr_buffer, contact_shadows_buffer): ( + Res, + Res, + ), oit_buffers: Res, - (decals_buffer, render_decals, atmosphere_buffer, atmosphere_sampler): ( + (decals_buffer, render_decals, atmosphere_buffer, atmosphere_sampler, blue_noise): ( Res, Res, Option>, Option>, + Res, ), ) { if let ( @@ -615,6 +644,7 @@ pub fn prepare_mesh_view_bind_groups( Some(light_probes_binding), Some(visibility_ranges_buffer), Some(ssr_binding), + Some(contact_shadows_binding), Some(environment_map_binding), ) = ( view_uniforms.uniforms.binding(), @@ -625,6 +655,7 @@ pub fn prepare_mesh_view_bind_groups( light_probes_buffer.binding(), visibility_ranges.buffer().buffer(), ssr_buffer.binding(), + contact_shadows_buffer.0.binding(), environment_map_uniform.binding(), ) { for ( @@ -641,6 +672,7 @@ pub fn prepare_mesh_view_bind_groups( has_oit, atmosphere_textures, has_atmosphere, + _contact_shadows_offset, ) in &views { let fallback_ssao = fallback_images @@ -659,6 +691,9 @@ pub fn prepare_mesh_view_bind_groups( if has_atmosphere { layout_key |= MeshPipelineViewLayoutKey::ATMOSPHERE; } + if cfg!(feature = "bluenoise_texture") { + layout_key |= MeshPipelineViewLayoutKey::STBN; + } let layout = mesh_pipeline.get_view_layout(layout_key); @@ -686,14 +721,15 @@ pub fn prepare_mesh_view_bind_groups( (13, light_probes_binding.clone()), (14, visibility_ranges_buffer.as_entire_binding()), (15, ssr_binding.clone()), - (16, ssao_view), + (16, contact_shadows_binding.clone()), + (17, ssao_view), )); - entries = entries.extend_with_indices(((17, environment_map_binding.clone()),)); + entries = entries.extend_with_indices(((18, environment_map_binding.clone()),)); let lut_bindings = get_lut_bindings(&images, &tonemapping_luts, tonemapping, &fallback_image); - entries = entries.extend_with_indices(((18, lut_bindings.0), (19, lut_bindings.1))); + entries = entries.extend_with_indices(((19, lut_bindings.0), (20, lut_bindings.1))); // When using WebGL, we can't have a depth texture with multisampling let prepass_bindings; @@ -703,7 +739,7 @@ pub fn prepare_mesh_view_bind_groups( for (binding, index) in prepass_bindings .iter() .map(Option::as_ref) - .zip([20, 21, 22, 23]) + .zip([21, 22, 23, 24]) .flat_map(|(b, i)| b.map(|b| (b, i))) { entries = entries.extend_with_indices(((index, binding),)); @@ -719,7 +755,7 @@ pub fn prepare_mesh_view_bind_groups( .unwrap_or(&fallback_image_zero.sampler); entries = - entries.extend_with_indices(((24, transmission_view), (25, transmission_sampler))); + entries.extend_with_indices(((25, transmission_view), (26, transmission_sampler))); if has_oit && let ( @@ -733,9 +769,9 @@ pub fn prepare_mesh_view_bind_groups( ) { entries = entries.extend_with_indices(( - (26, oit_layers_binding.clone()), - (27, oit_layer_ids_binding.clone()), - (28, oit_settings_binding.clone()), + (27, oit_layers_binding.clone()), + (28, oit_layer_ids_binding.clone()), + (29, oit_settings_binding.clone()), )); } @@ -746,12 +782,20 @@ pub fn prepare_mesh_view_bind_groups( && let Some(atmosphere_buffer_binding) = atmosphere_buffer.buffer.binding() { entries = entries.extend_with_indices(( - (29, &atmosphere_textures.transmittance_lut.default_view), - (30, &***atmosphere_sampler), - (31, atmosphere_buffer_binding), + (30, &atmosphere_textures.transmittance_lut.default_view), + (31, &***atmosphere_sampler), + (32, atmosphere_buffer_binding), )); } + if layout_key.contains(MeshPipelineViewLayoutKey::STBN) { + let stbn_view = &images + .get(&blue_noise.texture) + .expect("STBN texture is added unconditionally with at least a placeholder") + .texture_view; + entries = entries.extend_with_indices(((33, stbn_view),)); + } + let mut entries_binding_array = DynamicBindGroupEntries::new(); let environment_map_bind_group_entries = RenderViewEnvironmentMapBindGroupEntries::get( diff --git a/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl b/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl index 5aef30ba05637..8309b49db4bb7 100644 --- a/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl +++ b/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl @@ -50,56 +50,60 @@ const VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE: u32 = 64u; #endif @group(0) @binding(15) var ssr_settings: types::ScreenSpaceReflectionsSettings; -@group(0) @binding(16) var screen_space_ambient_occlusion_texture: texture_2d; -@group(0) @binding(17) var environment_map_uniform: types::EnvironmentMapUniform; +@group(0) @binding(16) var contact_shadows_settings: types::ContactShadowsSettings; +@group(0) @binding(17) var screen_space_ambient_occlusion_texture: texture_2d; +@group(0) @binding(18) var environment_map_uniform: types::EnvironmentMapUniform; // NB: If you change these, make sure to update `tonemapping_shared.wgsl` too. -@group(0) @binding(18) var dt_lut_texture: texture_3d; -@group(0) @binding(19) var dt_lut_sampler: sampler; +@group(0) @binding(19) var dt_lut_texture: texture_3d; +@group(0) @binding(20) var dt_lut_sampler: sampler; #ifdef MULTISAMPLED #ifdef DEPTH_PREPASS -@group(0) @binding(20) var depth_prepass_texture: texture_depth_multisampled_2d; +@group(0) @binding(21) var depth_prepass_texture: texture_depth_multisampled_2d; #endif // DEPTH_PREPASS #ifdef NORMAL_PREPASS -@group(0) @binding(21) var normal_prepass_texture: texture_multisampled_2d; +@group(0) @binding(22) var normal_prepass_texture: texture_multisampled_2d; #endif // NORMAL_PREPASS #ifdef MOTION_VECTOR_PREPASS -@group(0) @binding(22) var motion_vector_prepass_texture: texture_multisampled_2d; +@group(0) @binding(23) var motion_vector_prepass_texture: texture_multisampled_2d; #endif // MOTION_VECTOR_PREPASS #else // MULTISAMPLED #ifdef DEPTH_PREPASS -@group(0) @binding(20) var depth_prepass_texture: texture_depth_2d; +@group(0) @binding(21) var depth_prepass_texture: texture_depth_2d; #endif // DEPTH_PREPASS #ifdef NORMAL_PREPASS -@group(0) @binding(21) var normal_prepass_texture: texture_2d; +@group(0) @binding(22) var normal_prepass_texture: texture_2d; #endif // NORMAL_PREPASS #ifdef MOTION_VECTOR_PREPASS -@group(0) @binding(22) var motion_vector_prepass_texture: texture_2d; +@group(0) @binding(23) var motion_vector_prepass_texture: texture_2d; #endif // MOTION_VECTOR_PREPASS #endif // MULTISAMPLED #ifdef DEFERRED_PREPASS -@group(0) @binding(23) var deferred_prepass_texture: texture_2d; +@group(0) @binding(24) var deferred_prepass_texture: texture_2d; #endif // DEFERRED_PREPASS -@group(0) @binding(24) var view_transmission_texture: texture_2d; -@group(0) @binding(25) var view_transmission_sampler: sampler; +@group(0) @binding(25) var view_transmission_texture: texture_2d; +@group(0) @binding(26) var view_transmission_sampler: sampler; #ifdef OIT_ENABLED -@group(0) @binding(26) var oit_layers: array>; -@group(0) @binding(27) var oit_layer_ids: array>; -@group(0) @binding(28) var oit_settings: types::OrderIndependentTransparencySettings; +@group(0) @binding(27) var oit_layers: array>; +@group(0) @binding(28) var oit_layer_ids: array>; +@group(0) @binding(29) var oit_settings: types::OrderIndependentTransparencySettings; #endif // OIT_ENABLED #ifdef ATMOSPHERE -@group(0) @binding(29) var atmosphere_transmittance_texture: texture_2d; -@group(0) @binding(30) var atmosphere_transmittance_sampler: sampler; -@group(0) @binding(31) var atmosphere_data: atmosphere::AtmosphereData; +@group(0) @binding(30) var atmosphere_transmittance_texture: texture_2d; +@group(0) @binding(31) var atmosphere_transmittance_sampler: sampler; +@group(0) @binding(32) var atmosphere_data: atmosphere::AtmosphereData; #endif // ATMOSPHERE +#ifdef BLUE_NOISE_TEXTURE +@group(0) @binding(33) var blue_noise_texture: texture_2d_array; +#endif // BLUE_NOISE_TEXTURE #ifdef MULTIPLE_LIGHT_PROBES_IN_ARRAY @group(1) @binding(0) var diffuse_environment_maps: binding_array, 8u>; diff --git a/crates/bevy_pbr/src/render/mesh_view_types.wgsl b/crates/bevy_pbr/src/render/mesh_view_types.wgsl index 112556ee1eb76..1f022eeba2493 100644 --- a/crates/bevy_pbr/src/render/mesh_view_types.wgsl +++ b/crates/bevy_pbr/src/render/mesh_view_types.wgsl @@ -21,6 +21,7 @@ const POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT: u32 = 1u << 0u; const POINT_LIGHT_FLAGS_SPOT_LIGHT_Y_NEGATIVE: u32 = 1u << 1u; const POINT_LIGHT_FLAGS_VOLUMETRIC_BIT: u32 = 1u << 2u; const POINT_LIGHT_FLAGS_AFFECTS_LIGHTMAPPED_MESH_DIFFUSE_BIT: u32 = 1u << 3u; +const POINT_LIGHT_FLAGS_CONTACT_SHADOWS_ENABLED_BIT: u32 = 1u << 4u; struct DirectionalCascade { clip_from_world: mat4x4, @@ -48,6 +49,7 @@ struct DirectionalLight { const DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT: u32 = 1u << 0u; const DIRECTIONAL_LIGHT_FLAGS_VOLUMETRIC_BIT: u32 = 1u << 1u; const DIRECTIONAL_LIGHT_FLAGS_AFFECTS_LIGHTMAPPED_MESH_DIFFUSE_BIT: u32 = 1u << 2u; +const DIRECTIONAL_LIGHT_FLAGS_CONTACT_SHADOWS_ENABLED_BIT: u32 = 1u << 3u; struct Lights { // NOTE: this array size must be kept in sync with the constants defined in bevy_pbr/src/render/light.rs @@ -163,6 +165,13 @@ struct ScreenSpaceReflectionsSettings { use_secant: u32, }; +// See the `ContactShadows` rust struct. +struct ContactShadowsSettings { + linear_steps: u32, + thickness: f32, + length: f32, +}; + struct EnvironmentMapUniform { // Transformation matrix for the environment cubemaps in world space. transform: mat4x4, diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index c53820ab0ba0a..e321607dca6d9 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -13,8 +13,12 @@ ambient, irradiance_volume, view_transformations, + raymarch, + utils, mesh_types::{MESH_FLAGS_SHADOW_RECEIVER_BIT, MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT}, } +#import bevy_pbr::mesh_view_bindings::globals +#import bevy_pbr::view_transformations::{position_world_to_ndc} #import bevy_render::maths::{E, powsafe} #ifdef MESHLET_MESH_MATERIAL_PASS @@ -278,6 +282,43 @@ fn calculate_F0(base_color: vec3, metallic: f32, reflectance: vec3) -> return 0.16 * reflectance * reflectance * (1.0 - metallic) + base_color * metallic; } +#ifdef DEPTH_PREPASS +fn calculate_contact_shadow( + world_position: vec3, + frag_coord: vec2, + light_dir: vec3, + contact_shadow_steps: u32, +) -> f32 { +#ifdef BLUE_NOISE_TEXTURE + let noise_size = textureDimensions(view_bindings::blue_noise_texture, 0); + let noise_layers = textureNumLayers(view_bindings::blue_noise_texture); + let noise = textureLoad( + view_bindings::blue_noise_texture, + vec2(frag_coord) % vec2(noise_size), + i32(view_bindings::globals.frame_count % noise_layers), + 0 + ).x; +#else + let noise = utils::interleaved_gradient_noise(frag_coord, view_bindings::globals.frame_count); +#endif + + let depth_size = vec2(textureDimensions(view_bindings::depth_prepass_texture)); + var rm = raymarch::depth_ray_march_new_from_depth(depth_size); + raymarch::depth_ray_march_from_cs(&rm, position_world_to_ndc(world_position)); + raymarch::depth_ray_march_to_ws(&rm, world_position + light_dir * view_bindings::contact_shadows_settings.length); + rm.linear_steps = contact_shadow_steps; + rm.depth_thickness_linear_z = view_bindings::contact_shadows_settings.thickness; + rm.march_behind_surfaces = true; + rm.jitter = noise; + + let rm_result = raymarch::depth_ray_march_march(&rm); + if rm_result.hit { + return clamp((rm_result.hit_penetration_frac - 0.5) / (1.0 - 0.5), 0.0, 1.0); + } + return 1.0; +} +#endif + #ifndef PREPASS_FRAGMENT fn apply_pbr_lighting( in: pbr_types::PbrInput, @@ -401,6 +442,9 @@ fn apply_pbr_lighting( var clusterable_object_index_ranges = clustering::unpack_clusterable_object_index_ranges(cluster_index); + let contact_shadow_steps = view_bindings::contact_shadows_settings.linear_steps; + let contact_shadow_enabled = contact_shadow_steps > 0u; + // Point lights (direct) for (var i: u32 = clusterable_object_index_ranges.first_point_light_index_offset; i < clusterable_object_index_ranges.first_spot_light_index_offset; @@ -423,6 +467,15 @@ fn apply_pbr_lighting( shadow = shadows::fetch_point_shadow(light_id, in.world_position, in.world_normal, in.frag_coord.xy); } +#ifdef DEPTH_PREPASS + if contact_shadow_enabled && (in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u && shadow > 0.0 && + (view_bindings::clusterable_objects.data[light_id].flags & + mesh_view_types::POINT_LIGHT_FLAGS_CONTACT_SHADOWS_ENABLED_BIT) != 0u { + let L = normalize(view_bindings::clusterable_objects.data[light_id].position_radius.xyz - in.world_position.xyz); + shadow *= calculate_contact_shadow(in.world_position.xyz, in.frag_coord.xy, L, contact_shadow_steps); + } +#endif + let light_contrib = lighting::point_light(light_id, &lighting_input, enable_diffuse, true); direct_light += light_contrib * shadow; @@ -477,6 +530,15 @@ fn apply_pbr_lighting( ); } +#ifdef DEPTH_PREPASS + if contact_shadow_enabled && (in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u && shadow > 0.0 && + (view_bindings::clusterable_objects.data[light_id].flags & + mesh_view_types::POINT_LIGHT_FLAGS_CONTACT_SHADOWS_ENABLED_BIT) != 0u { + let L = normalize(view_bindings::clusterable_objects.data[light_id].position_radius.xyz - in.world_position.xyz); + shadow *= calculate_contact_shadow(in.world_position.xyz, in.frag_coord.xy, L, contact_shadow_steps); + } +#endif + let light_contrib = lighting::spot_light(light_id, &lighting_input, enable_diffuse); direct_light += light_contrib * shadow; @@ -532,6 +594,15 @@ fn apply_pbr_lighting( shadow = shadows::fetch_directional_shadow(i, in.world_position, in.world_normal, view_z, in.frag_coord.xy); } +#ifdef DEPTH_PREPASS + if contact_shadow_enabled && (in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u && shadow > 0.0 && + (view_bindings::lights.directional_lights[i].flags & + mesh_view_types::DIRECTIONAL_LIGHT_FLAGS_CONTACT_SHADOWS_ENABLED_BIT) != 0u { + let L = view_bindings::lights.directional_lights[i].direction_to_light; + shadow *= calculate_contact_shadow(in.world_position.xyz, in.frag_coord.xy, L, contact_shadow_steps); + } +#endif + var light_contrib = lighting::directional_light(i, &lighting_input, enable_diffuse); #ifdef DIRECTIONAL_LIGHT_SHADOW_MAP_DEBUG_CASCADES diff --git a/crates/bevy_pbr/src/ssr/mod.rs b/crates/bevy_pbr/src/ssr/mod.rs index 7bd9d02c529de..3984b82933685 100644 --- a/crates/bevy_pbr/src/ssr/mod.rs +++ b/crates/bevy_pbr/src/ssr/mod.rs @@ -47,10 +47,10 @@ use bevy_utils::{once, prelude::default}; use tracing::info; use crate::{ - binding_arrays_are_usable, graph::NodePbr, ExtractedAtmosphere, MeshPipelineViewLayoutKey, - MeshPipelineViewLayouts, MeshViewBindGroup, RenderViewLightProbes, - ViewEnvironmentMapUniformOffset, ViewFogUniformOffset, ViewLightProbesUniformOffset, - ViewLightsUniformOffset, + binding_arrays_are_usable, contact_shadows::ViewContactShadowsUniformOffset, graph::NodePbr, + ExtractedAtmosphere, MeshPipelineViewLayoutKey, MeshPipelineViewLayouts, MeshViewBindGroup, + RenderViewLightProbes, ViewEnvironmentMapUniformOffset, ViewFogUniformOffset, + ViewLightProbesUniformOffset, ViewLightsUniformOffset, }; /// Enables screen-space reflections for a camera. @@ -258,6 +258,7 @@ impl ViewNode for ScreenSpaceReflectionsNode { Read, Read, Read, + Read, Read, Read, Read, @@ -274,6 +275,7 @@ impl ViewNode for ScreenSpaceReflectionsNode { view_fog_offset, view_light_probes_offset, view_ssr_offset, + view_contact_shadows_offset, view_environment_map_offset, view_bind_group, ssr_pipeline_id, @@ -330,6 +332,7 @@ impl ViewNode for ScreenSpaceReflectionsNode { view_fog_offset.offset, **view_light_probes_offset, **view_ssr_offset, + **view_contact_shadows_offset, **view_environment_map_offset, ], ); diff --git a/crates/bevy_pbr/src/volumetric_fog/render.rs b/crates/bevy_pbr/src/volumetric_fog/render.rs index e0ddc3ccefe74..44f6b81d0ce82 100644 --- a/crates/bevy_pbr/src/volumetric_fog/render.rs +++ b/crates/bevy_pbr/src/volumetric_fog/render.rs @@ -51,8 +51,8 @@ use bitflags::bitflags; use crate::{ ExtractedAtmosphere, MeshPipelineViewLayoutKey, MeshPipelineViewLayouts, MeshViewBindGroup, - ViewEnvironmentMapUniformOffset, ViewFogUniformOffset, ViewLightProbesUniformOffset, - ViewLightsUniformOffset, ViewScreenSpaceReflectionsUniformOffset, + ViewContactShadowsUniformOffset, ViewEnvironmentMapUniformOffset, ViewFogUniformOffset, + ViewLightProbesUniformOffset, ViewLightsUniformOffset, ViewScreenSpaceReflectionsUniformOffset, }; use super::FogAssets; @@ -315,6 +315,7 @@ impl ViewNode for VolumetricFogNode { Read, Read, Read, + Read, Read, Read, ); @@ -334,6 +335,7 @@ impl ViewNode for VolumetricFogNode { view_fog_volumes, view_bind_group, view_ssr_offset, + view_contact_shadows_offset, msaa, view_environment_map_offset, ): QueryItem<'w, '_, Self::ViewQuery>, @@ -466,6 +468,7 @@ impl ViewNode for VolumetricFogNode { view_fog_offset.offset, **view_light_probes_offset, **view_ssr_offset, + **view_contact_shadows_offset, **view_environment_map_offset, ], ); diff --git a/crates/bevy_solari/src/realtime/mod.rs b/crates/bevy_solari/src/realtime/mod.rs index b13915a731c33..1297808228802 100644 --- a/crates/bevy_solari/src/realtime/mod.rs +++ b/crates/bevy_solari/src/realtime/mod.rs @@ -29,7 +29,7 @@ use tracing::warn; /// Raytraced direct and indirect lighting. /// -/// When using this plugin, it's highly recommended to set `shadows_enabled: false` on all lights, as Solari replaces +/// When using this plugin, it's highly recommended to set `shadow_maps_enabled: false` on all lights, as Solari replaces /// traditional shadow mapping. pub struct SolariLightingPlugin; diff --git a/examples/3d/3d_scene.rs b/examples/3d/3d_scene.rs index 5ea7e20b29662..b98ca52ae9a5b 100644 --- a/examples/3d/3d_scene.rs +++ b/examples/3d/3d_scene.rs @@ -30,7 +30,7 @@ fn setup( // light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/3d/3d_shapes.rs b/examples/3d/3d_shapes.rs index a256ae1ce8b32..6dfe42bf121a5 100644 --- a/examples/3d/3d_shapes.rs +++ b/examples/3d/3d_shapes.rs @@ -173,7 +173,7 @@ fn setup( commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, intensity: 10_000_000., range: 100.0, shadow_depth_bias: 0.2, diff --git a/examples/3d/anti_aliasing.rs b/examples/3d/anti_aliasing.rs index 08745ee53e197..46502a18da51e 100644 --- a/examples/3d/anti_aliasing.rs +++ b/examples/3d/anti_aliasing.rs @@ -445,7 +445,7 @@ fn setup( commands.spawn(( DirectionalLight { illuminance: light_consts::lux::FULL_DAYLIGHT, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, PI * -0.15, PI * -0.15)), diff --git a/examples/3d/atmosphere.rs b/examples/3d/atmosphere.rs index e264716cc3714..be13b7383dac4 100644 --- a/examples/3d/atmosphere.rs +++ b/examples/3d/atmosphere.rs @@ -187,7 +187,7 @@ fn setup_terrain_scene( // Sun commands.spawn(( DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, // lux::RAW_SUNLIGHT is recommended for use with this feature, since // other values approximate sunlight *post-scattering* in various // conditions. RAW_SUNLIGHT in comparison is the illuminance of the diff --git a/examples/3d/atmospheric_fog.rs b/examples/3d/atmospheric_fog.rs index 70c1fc2b5528e..d09c7747431ea 100644 --- a/examples/3d/atmospheric_fog.rs +++ b/examples/3d/atmospheric_fog.rs @@ -58,7 +58,7 @@ fn setup_terrain_scene( commands.spawn(( DirectionalLight { color: Color::srgb(0.98, 0.95, 0.82), - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(0.0, 0.0, 0.0).looking_at(Vec3::new(-0.15, -0.05, 0.25), Vec3::Y), diff --git a/examples/3d/camera_sub_view.rs b/examples/3d/camera_sub_view.rs index 73cf870707b8c..b6f9a8f39c342 100644 --- a/examples/3d/camera_sub_view.rs +++ b/examples/3d/camera_sub_view.rs @@ -46,7 +46,7 @@ fn setup( // Light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/3d/color_grading.rs b/examples/3d/color_grading.rs index 8e6f49a08156a..15ffb840d4dd5 100644 --- a/examples/3d/color_grading.rs +++ b/examples/3d/color_grading.rs @@ -360,7 +360,7 @@ fn add_basic_scene(commands: &mut Commands, asset_server: &AssetServer) { commands.spawn(( DirectionalLight { illuminance: 15000.0, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, PI * -0.15, PI * -0.15)), diff --git a/examples/3d/contact_shadows.rs b/examples/3d/contact_shadows.rs new file mode 100644 index 0000000000000..e57d2c3b3a457 --- /dev/null +++ b/examples/3d/contact_shadows.rs @@ -0,0 +1,457 @@ +//! Demonstrates contact shadows, also known as screen-space shadows. + +use crate::widgets::{RadioButton, RadioButtonText, WidgetClickEvent, WidgetClickSender}; +use bevy::anti_alias::taa::TemporalAntiAliasing; +use bevy::core_pipeline::tonemapping::Tonemapping; +use bevy::core_pipeline::Skybox; +use bevy::pbr::ScreenSpaceAmbientOcclusion; +use bevy::post_process::motion_blur::MotionBlur; +use bevy::window::{CursorIcon, PrimaryWindow, SystemCursorIcon}; +use bevy::{ + ecs::message::MessageReader, light::NotShadowReceiver, pbr::ContactShadows, + post_process::bloom::Bloom, prelude::*, render::view::Hdr, +}; + +#[path = "../helpers/widgets.rs"] +mod widgets; + +#[derive(Clone, Copy, PartialEq, Default, Debug)] +enum ContactShadowState { + #[default] + Enabled, + Disabled, +} + +#[derive(Clone, Copy, PartialEq, Default, Debug)] +enum ShadowMaps { + #[default] + Enabled, + Disabled, +} + +#[derive(Clone, Copy, PartialEq, Default, Debug)] +enum LightRotation { + Stationary, + #[default] + Rotating, +} + +#[derive(Clone, Copy, PartialEq, Default, Debug)] +enum LightType { + Directional, + #[default] + Point, + Spot, +} + +#[derive(Clone, Copy, PartialEq, Default, Debug)] +enum ReceiveShadows { + #[default] + Enabled, + Disabled, +} + +/// Each example setting that can be toggled in the UI. +#[derive(Clone, Copy, PartialEq)] +enum ExampleSetting { + ContactShadows(ContactShadowState), + ShadowMaps(ShadowMaps), + LightRotation(LightRotation), + LightType(LightType), + ReceiveShadows(ReceiveShadows), +} + +const LIGHT_ROTATION_SPEED: f32 = 0.002; + +#[derive(Resource, Default)] +struct AppStatus { + contact_shadows: ContactShadowState, + shadow_maps: ShadowMaps, + light_rotation: LightRotation, + light_type: LightType, + receive_shadows: ReceiveShadows, +} + +#[derive(Component)] +struct LightContainer; + +#[derive(Component)] +struct GroundPlane; + +fn main() { + App::new() + .add_plugins(( + DefaultPlugins.set(WindowPlugin { + primary_window: Some(Window { + title: "Bevy Contact Shadows Example".into(), + ..default() + }), + ..default() + }), + MeshPickingPlugin, + )) + .init_resource::() + .insert_resource(GlobalAmbientLight::NONE) + .add_message::>() + .add_systems(Startup, setup) + .add_systems(Update, rotate_light) + .add_systems( + Update, + ( + widgets::handle_ui_interactions::, + update_radio_buttons.after(widgets::handle_ui_interactions::), + handle_setting_change.after(widgets::handle_ui_interactions::), + ), + ) + .run(); +} + +fn setup(mut commands: Commands, asset_server: Res) { + commands.spawn(( + Camera3d::default(), + Transform::from_xyz(-0.8, 0.6, -0.8).looking_at(Vec3::new(0.0, 0.35, 0.0), Vec3::Y), + ContactShadows::default(), + TemporalAntiAliasing::default(), // Contact shadows and AO benefit from TAA + // Everything past this point is extra to look pretty. + Bloom::default(), + Hdr, + Skybox { + brightness: 500.0, + image: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"), + ..default() + }, + EnvironmentMapLight { + diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"), + specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), + intensity: 1000.0, + ..default() + }, + ScreenSpaceAmbientOcclusion::default(), + Msaa::Off, + Tonemapping::AcesFitted, + MotionBlur { + shutter_angle: 2.0, // This is really just for fun when spinning the model + ..default() + }, + )); + + let directional_light = commands + .spawn(( + DirectionalLight { + shadow_maps_enabled: true, + contact_shadows_enabled: true, + ..default() + }, + Visibility::Hidden, + )) + .id(); + + let point_light = commands + .spawn(( + PointLight { + intensity: light_consts::lumens::VERY_LARGE_CINEMA_LIGHT * 0.4, + shadow_maps_enabled: true, + contact_shadows_enabled: true, + ..default() + }, + Visibility::Visible, + )) + .id(); + + let spot_light = commands + .spawn(( + SpotLight { + intensity: light_consts::lumens::VERY_LARGE_CINEMA_LIGHT * 0.4, + shadow_maps_enabled: true, + contact_shadows_enabled: true, + ..default() + }, + Visibility::Hidden, + )) + .id(); + + commands + .spawn(( + Transform::from_xyz(-0.8, 1.5, 1.2).looking_at(Vec3::ZERO, Vec3::Y), + Visibility::default(), + LightContainer, + )) + .add_child(directional_light) + .add_child(point_light) + .add_child(spot_light); + + commands + .spawn(( + SceneRoot(asset_server.load( + GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"), + )), + Transform::from_rotation(Quat::from_rotation_y(std::f32::consts::PI)), + )) + .observe( + |event: On>, + mut query: Query<&mut Transform, With>, + mut commands: Commands, + mut window: Query>| { + for mut transform in query.iter_mut() { + transform.rotate_y(event.delta.x * 0.01); + } + commands + .entity(window.single_mut().unwrap()) + .insert(CursorIcon::System(SystemCursorIcon::Grabbing)); + }, + ) + .observe( + |_: On>, + mut commands: Commands, + mut window: Query>| { + commands + .entity(window.single_mut().unwrap()) + .insert(CursorIcon::System(SystemCursorIcon::Grab)); + }, + ) + .observe( + |_: On>, + mut commands: Commands, + mut window: Query>| { + commands + .entity(window.single_mut().unwrap()) + .insert(CursorIcon::System(SystemCursorIcon::Default)); + }, + ) + .observe( + |_: On>, + mut commands: Commands, + mut window: Query>| { + commands + .entity(window.single_mut().unwrap()) + .insert(CursorIcon::System(SystemCursorIcon::Default)); + }, + ); + + commands.spawn(( + Mesh3d(asset_server.add(Circle::default().mesh().into())), + MeshMaterial3d(asset_server.add(StandardMaterial { + base_color: Color::srgb(0.06, 0.06, 0.06), + ..default() + })), + Transform::from_rotation(Quat::from_axis_angle(Vec3::X, -std::f32::consts::FRAC_PI_2)), + GroundPlane, + )); + + spawn_buttons(&mut commands); + + commands.spawn(( + Node { + position_type: PositionType::Absolute, + top: px(12.0), + left: px(0.0), + right: px(0.0), + justify_content: JustifyContent::Center, + ..default() + }, + children![( + Text::new("Drag model to spin"), + TextFont { + font_size: 18.0, + ..default() + }, + )], + )); +} + +fn rotate_light( + mut lights: Query<&mut Transform, With>, + app_status: Res, +) { + if app_status.light_rotation != LightRotation::Rotating { + return; + } + + for mut transform in lights.iter_mut() { + transform.rotate_around(Vec3::ZERO, Quat::from_rotation_y(LIGHT_ROTATION_SPEED)); + } +} + +fn spawn_buttons(commands: &mut Commands) { + commands.spawn(( + widgets::main_ui_node(), + children![ + widgets::option_buttons( + "Contact Shadows", + &[ + ( + ExampleSetting::ContactShadows(ContactShadowState::Enabled), + "On" + ), + ( + ExampleSetting::ContactShadows(ContactShadowState::Disabled), + "Off" + ), + ], + ), + widgets::option_buttons( + "Shadow Maps", + &[ + (ExampleSetting::ShadowMaps(ShadowMaps::Enabled), "On"), + (ExampleSetting::ShadowMaps(ShadowMaps::Disabled), "Off"), + ], + ), + widgets::option_buttons( + "Light Rotation", + &[ + (ExampleSetting::LightRotation(LightRotation::Rotating), "On"), + ( + ExampleSetting::LightRotation(LightRotation::Stationary), + "Off" + ), + ], + ), + widgets::option_buttons( + "Light Type", + &[ + ( + ExampleSetting::LightType(LightType::Directional), + "Directional" + ), + (ExampleSetting::LightType(LightType::Point), "Point"), + (ExampleSetting::LightType(LightType::Spot), "Spot"), + ], + ), + widgets::option_buttons( + "Receive Shadows", + &[ + ( + ExampleSetting::ReceiveShadows(ReceiveShadows::Enabled), + "On" + ), + ( + ExampleSetting::ReceiveShadows(ReceiveShadows::Disabled), + "Off" + ), + ], + ), + ], + )); +} + +fn update_radio_buttons( + mut widgets: Query< + ( + Entity, + Option<&mut BackgroundColor>, + Has, + &WidgetClickSender, + ), + Or<(With, With)>, + >, + app_status: Res, + mut writer: TextUiWriter, +) { + for (entity, background_color, has_text, sender) in widgets.iter_mut() { + let selected = match **sender { + ExampleSetting::ContactShadows(value) => value == app_status.contact_shadows, + ExampleSetting::ShadowMaps(value) => value == app_status.shadow_maps, + ExampleSetting::LightRotation(value) => value == app_status.light_rotation, + ExampleSetting::LightType(value) => value == app_status.light_type, + ExampleSetting::ReceiveShadows(value) => value == app_status.receive_shadows, + }; + + if let Some(mut background_color) = background_color { + widgets::update_ui_radio_button(&mut background_color, selected); + } + if has_text { + widgets::update_ui_radio_button_text(entity, &mut writer, selected); + } + } +} + +fn handle_setting_change( + mut lights: Query< + ( + &mut Visibility, + Option<&mut DirectionalLight>, + Option<&mut PointLight>, + Option<&mut SpotLight>, + ), + Or<(With, With, With)>, + >, + mut ground_plane: Query>, + mut events: MessageReader>, + mut app_status: ResMut, + mut commands: Commands, +) { + for event in events.read() { + match **event { + ExampleSetting::ContactShadows(value) => { + app_status.contact_shadows = value; + for (_, maybe_directional_light, maybe_point_light, maybe_spot_light) in + lights.iter_mut() + { + if let Some(mut directional_light) = maybe_directional_light { + directional_light.contact_shadows_enabled = + value == ContactShadowState::Enabled; + } + if let Some(mut point_light) = maybe_point_light { + point_light.contact_shadows_enabled = value == ContactShadowState::Enabled; + } + if let Some(mut spot_light) = maybe_spot_light { + spot_light.contact_shadows_enabled = value == ContactShadowState::Enabled; + } + } + } + ExampleSetting::ShadowMaps(value) => { + app_status.shadow_maps = value; + for (_, maybe_directional_light, maybe_point_light, maybe_spot_light) in + lights.iter_mut() + { + if let Some(mut directional_light) = maybe_directional_light { + directional_light.shadow_maps_enabled = value == ShadowMaps::Enabled; + } + if let Some(mut point_light) = maybe_point_light { + point_light.shadow_maps_enabled = value == ShadowMaps::Enabled; + } + if let Some(mut spot_light) = maybe_spot_light { + spot_light.shadow_maps_enabled = value == ShadowMaps::Enabled; + } + } + } + ExampleSetting::LightRotation(value) => { + app_status.light_rotation = value; + } + ExampleSetting::LightType(value) => { + app_status.light_type = value; + for ( + mut visibility, + maybe_directional_light, + maybe_point_light, + maybe_spot_light, + ) in lights.iter_mut() + { + let is_visible = match value { + LightType::Directional => maybe_directional_light.is_some(), + LightType::Point => maybe_point_light.is_some(), + LightType::Spot => maybe_spot_light.is_some(), + }; + *visibility = if is_visible { + Visibility::Visible + } else { + Visibility::Hidden + }; + } + } + ExampleSetting::ReceiveShadows(value) => { + app_status.receive_shadows = value; + for entity in ground_plane.iter_mut() { + match value { + ReceiveShadows::Enabled => { + commands.entity(entity).remove::(); + } + ReceiveShadows::Disabled => { + commands.entity(entity).insert(NotShadowReceiver); + } + } + } + } + } + } +} diff --git a/examples/3d/decal.rs b/examples/3d/decal.rs index 623227cf2ffd9..e81124275b2c5 100644 --- a/examples/3d/decal.rs +++ b/examples/3d/decal.rs @@ -92,7 +92,7 @@ fn setup( commands.spawn(( Name::new("Light"), PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/3d/deferred_rendering.rs b/examples/3d/deferred_rendering.rs index ff3656d7a6ecb..4a65ce1eedf3c 100644 --- a/examples/3d/deferred_rendering.rs +++ b/examples/3d/deferred_rendering.rs @@ -59,7 +59,7 @@ fn setup( commands.spawn(( DirectionalLight { illuminance: 15_000., - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, CascadeShadowConfigBuilder { @@ -122,7 +122,7 @@ fn setup( PointLight { intensity: 800.0, radius: 0.125, - shadows_enabled: true, + shadow_maps_enabled: true, color: sphere_color, ..default() }, diff --git a/examples/3d/depth_of_field.rs b/examples/3d/depth_of_field.rs index 1ffbc31c34ed2..8925e81b1ca3d 100644 --- a/examples/3d/depth_of_field.rs +++ b/examples/3d/depth_of_field.rs @@ -189,7 +189,7 @@ fn tweak_scene( ) { // Turn on shadows. for mut light in lights.iter_mut() { - light.shadows_enabled = true; + light.shadow_maps_enabled = true; } // Add a nice lightmap to the circuit board. diff --git a/examples/3d/fog.rs b/examples/3d/fog.rs index 8d23035b738c2..d33968cddda94 100644 --- a/examples/3d/fog.rs +++ b/examples/3d/fog.rs @@ -115,7 +115,7 @@ fn setup_pyramid_scene( // light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(0.0, 1.0, 0.0), diff --git a/examples/3d/fog_volumes.rs b/examples/3d/fog_volumes.rs index 9d52903e4e94a..17bad1bd5a9ec 100644 --- a/examples/3d/fog_volumes.rs +++ b/examples/3d/fog_volumes.rs @@ -47,7 +47,7 @@ fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(( Transform::from_xyz(1.0, 1.0, -0.3).looking_at(vec3(0.0, 0.5, 0.0), Vec3::Y), DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, illuminance: 32000.0, ..default() }, diff --git a/examples/3d/irradiance_volumes.rs b/examples/3d/irradiance_volumes.rs index 1937dc3f2e754..bb45ae6bfac4b 100644 --- a/examples/3d/irradiance_volumes.rs +++ b/examples/3d/irradiance_volumes.rs @@ -258,7 +258,7 @@ fn spawn_light(commands: &mut Commands) { commands.spawn(( PointLight { intensity: 250000.0, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0762, 5.9039, 1.0055), diff --git a/examples/3d/light_textures.rs b/examples/3d/light_textures.rs index 079fbb77daa87..d307e9cfc361c 100644 --- a/examples/3d/light_textures.rs +++ b/examples/3d/light_textures.rs @@ -237,7 +237,7 @@ fn spawn_light_textures( intensity: 10e6, outer_angle: 0.25, inner_angle: 0.25, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_translation(Vec3::new(6.0, 1.0, 2.0)).looking_at(Vec3::ZERO, Vec3::Y), @@ -267,7 +267,7 @@ fn spawn_light_textures( PointLight { color: Color::srgb(0.0, 0.0, 1.0), intensity: 1e6, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, PointLightTexture { diff --git a/examples/3d/lighting.rs b/examples/3d/lighting.rs index 71bc4a00c049e..2fa77fb037cba 100644 --- a/examples/3d/lighting.rs +++ b/examples/3d/lighting.rs @@ -131,7 +131,7 @@ fn setup( PointLight { intensity: 100_000.0, color: RED.into(), - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(1.0, 2.0, 0.0), @@ -150,7 +150,7 @@ fn setup( SpotLight { intensity: 100_000.0, color: LIME.into(), - shadows_enabled: true, + shadow_maps_enabled: true, inner_angle: 0.6, outer_angle: 0.8, ..default() @@ -172,7 +172,7 @@ fn setup( PointLight { intensity: 100_000.0, color: BLUE.into(), - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(0.0, 4.0, 0.0), @@ -190,7 +190,7 @@ fn setup( commands.spawn(( DirectionalLight { illuminance: light_consts::lux::OVERCAST_DAY, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform { diff --git a/examples/3d/meshlet.rs b/examples/3d/meshlet.rs index 299df303cf263..ab43a908057ce 100644 --- a/examples/3d/meshlet.rs +++ b/examples/3d/meshlet.rs @@ -52,7 +52,7 @@ fn setup( commands.spawn(( DirectionalLight { illuminance: light_consts::lux::FULL_DAYLIGHT, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, CascadeShadowConfigBuilder { diff --git a/examples/3d/mixed_lighting.rs b/examples/3d/mixed_lighting.rs index 58594ad14e1c1..4cf033459469d 100644 --- a/examples/3d/mixed_lighting.rs +++ b/examples/3d/mixed_lighting.rs @@ -352,7 +352,7 @@ fn update_directional_light( for mut light in &mut lights { light.affects_lightmapped_mesh_diffuse = scenery_is_lit_in_real_time; // Don't bother enabling shadows if they won't show up on the scenery. - light.shadows_enabled = scenery_is_lit_in_real_time; + light.shadow_maps_enabled = scenery_is_lit_in_real_time; } } diff --git a/examples/3d/motion_blur.rs b/examples/3d/motion_blur.rs index ef9349ee7b19d..d5a03cadefcdb 100644 --- a/examples/3d/motion_blur.rs +++ b/examples/3d/motion_blur.rs @@ -66,7 +66,7 @@ fn setup_scene( commands.spawn(( DirectionalLight { illuminance: 3_000.0, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::default().looking_to(Vec3::new(-1.0, -0.7, -1.0), Vec3::X), diff --git a/examples/3d/order_independent_transparency.rs b/examples/3d/order_independent_transparency.rs index afec7d9a0506d..8bc161204eac8 100644 --- a/examples/3d/order_independent_transparency.rs +++ b/examples/3d/order_independent_transparency.rs @@ -38,7 +38,7 @@ fn setup( // light commands.spawn(( PointLight { - shadows_enabled: false, + shadow_maps_enabled: false, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/3d/parallax_mapping.rs b/examples/3d/parallax_mapping.rs index dad166adf61ef..323d691eeb770 100644 --- a/examples/3d/parallax_mapping.rs +++ b/examples/3d/parallax_mapping.rs @@ -226,7 +226,7 @@ fn setup( // light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(2.0, 1.0, -1.1), diff --git a/examples/3d/pcss.rs b/examples/3d/pcss.rs index ac939f924678c..e606d65ca8dfe 100644 --- a/examples/3d/pcss.rs +++ b/examples/3d/pcss.rs @@ -368,7 +368,7 @@ fn handle_pcss_toggle( /// Creates the [`DirectionalLight`] component with the appropriate settings. fn create_directional_light(app_status: &AppStatus) -> DirectionalLight { DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, soft_shadow_size: if app_status.soft_shadows { Some(LIGHT_RADIUS) } else { @@ -384,7 +384,7 @@ fn create_point_light(app_status: &AppStatus) -> PointLight { PointLight { intensity: POINT_LIGHT_INTENSITY, range: POINT_LIGHT_RANGE, - shadows_enabled: true, + shadow_maps_enabled: true, radius: LIGHT_RADIUS, soft_shadows_enabled: app_status.soft_shadows, shadow_depth_bias: POINT_SHADOW_DEPTH_BIAS, @@ -399,7 +399,7 @@ fn create_spot_light(app_status: &AppStatus) -> SpotLight { intensity: POINT_LIGHT_INTENSITY, range: POINT_LIGHT_RANGE, radius: LIGHT_RADIUS, - shadows_enabled: true, + shadow_maps_enabled: true, soft_shadows_enabled: app_status.soft_shadows, shadow_depth_bias: DIRECTIONAL_SHADOW_DEPTH_BIAS, shadow_map_near_z: SHADOW_MAP_NEAR_Z, diff --git a/examples/3d/post_processing.rs b/examples/3d/post_processing.rs index ae9c1144422da..bde92f0363aff 100644 --- a/examples/3d/post_processing.rs +++ b/examples/3d/post_processing.rs @@ -105,7 +105,7 @@ fn spawn_scene(commands: &mut Commands, asset_server: &AssetServer) { commands.spawn(( DirectionalLight { illuminance: 15000.0, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, PI * -0.15, PI * -0.15)), diff --git a/examples/3d/scrolling_fog.rs b/examples/3d/scrolling_fog.rs index 2edc2c358d81b..9dbdc050cf04e 100644 --- a/examples/3d/scrolling_fog.rs +++ b/examples/3d/scrolling_fog.rs @@ -61,7 +61,7 @@ fn setup( // Spawn a directional light shining at the camera with the VolumetricLight component. commands.spawn(( DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(-5.0, 5.0, -7.0).looking_at(Vec3::new(0.0, 0.0, 0.0), Vec3::Y), diff --git a/examples/3d/shadow_biases.rs b/examples/3d/shadow_biases.rs index 2e07209ffa41a..6bbb74bed45f6 100644 --- a/examples/3d/shadow_biases.rs +++ b/examples/3d/shadow_biases.rs @@ -54,11 +54,11 @@ fn setup( intensity: 0.0, range: spawn_plane_depth, color: Color::WHITE, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }), (DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }) ], diff --git a/examples/3d/shadow_caster_receiver.rs b/examples/3d/shadow_caster_receiver.rs index fa1984086426a..00c4242e506bd 100644 --- a/examples/3d/shadow_caster_receiver.rs +++ b/examples/3d/shadow_caster_receiver.rs @@ -76,7 +76,7 @@ fn setup( intensity: 0.0, range: spawn_plane_depth, color: Color::WHITE, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(5.0, 5.0, 0.0), @@ -85,7 +85,7 @@ fn setup( commands.spawn(( DirectionalLight { illuminance: light_consts::lux::OVERCAST_DAY, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, PI / 2., -PI / 4.)), diff --git a/examples/3d/solari.rs b/examples/3d/solari.rs index 4f74e33eceb0e..dc2b48dd72ad9 100644 --- a/examples/3d/solari.rs +++ b/examples/3d/solari.rs @@ -120,7 +120,7 @@ fn setup_pica_pica( commands.spawn(( DirectionalLight { illuminance: light_consts::lux::FULL_DAYLIGHT, - shadows_enabled: false, // Solari replaces shadow mapping + shadow_maps_enabled: false, // Solari replaces shadow mapping ..default() }, Transform::from_rotation(Quat::from_xyzw( @@ -457,7 +457,7 @@ fn toggle_lights( commands.spawn(( DirectionalLight { illuminance: light_consts::lux::FULL_DAYLIGHT, - shadows_enabled: false, // Solari replaces shadow mapping + shadow_maps_enabled: false, // Solari replaces shadow mapping ..default() }, Transform::from_rotation(Quat::from_xyzw( diff --git a/examples/3d/split_screen.rs b/examples/3d/split_screen.rs index 60f581ec457fe..ea30922d3e541 100644 --- a/examples/3d/split_screen.rs +++ b/examples/3d/split_screen.rs @@ -35,7 +35,7 @@ fn setup( commands.spawn(( Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)), DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, CascadeShadowConfigBuilder { diff --git a/examples/3d/spotlight.rs b/examples/3d/spotlight.rs index f3270fdefbfdb..25f5c21f87371 100644 --- a/examples/3d/spotlight.rs +++ b/examples/3d/spotlight.rs @@ -93,7 +93,7 @@ fn setup( SpotLight { intensity: 40_000.0, // lumens color: Color::WHITE, - shadows_enabled: true, + shadow_maps_enabled: true, inner_angle: PI / 4.0 * 0.85, outer_angle: PI / 4.0, ..default() diff --git a/examples/3d/ssao.rs b/examples/3d/ssao.rs index d9f59aa4cce5a..e713e329e3941 100644 --- a/examples/3d/ssao.rs +++ b/examples/3d/ssao.rs @@ -69,7 +69,7 @@ fn setup( commands.spawn(( DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, PI * -0.15, PI * -0.15)), diff --git a/examples/3d/tonemapping.rs b/examples/3d/tonemapping.rs index 5d8b19e4367e1..83dff0f00a139 100644 --- a/examples/3d/tonemapping.rs +++ b/examples/3d/tonemapping.rs @@ -119,7 +119,7 @@ fn setup_basic_scene(mut commands: Commands, asset_server: Res) { commands.spawn(( DirectionalLight { illuminance: 15_000., - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, PI * -0.15, PI * -0.15)), diff --git a/examples/3d/transmission.rs b/examples/3d/transmission.rs index cd9754b70e3b3..4157df7360bf6 100644 --- a/examples/3d/transmission.rs +++ b/examples/3d/transmission.rs @@ -289,7 +289,7 @@ fn setup( intensity: 4_000.0, radius: 0.2, range: 5.0, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Flicker, diff --git a/examples/3d/transparency_3d.rs b/examples/3d/transparency_3d.rs index 37fa25b529c74..2fa4a56fcdc2b 100644 --- a/examples/3d/transparency_3d.rs +++ b/examples/3d/transparency_3d.rs @@ -83,7 +83,7 @@ fn setup( // Light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/3d/two_passes.rs b/examples/3d/two_passes.rs index f80125472e7b0..7e6658a066556 100644 --- a/examples/3d/two_passes.rs +++ b/examples/3d/two_passes.rs @@ -31,7 +31,7 @@ fn setup( // Light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/3d/vertex_colors.rs b/examples/3d/vertex_colors.rs index 3a5c31f94a7bf..7550192ac94f9 100644 --- a/examples/3d/vertex_colors.rs +++ b/examples/3d/vertex_colors.rs @@ -44,7 +44,7 @@ fn setup( // Light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 5.0, 4.0).looking_at(Vec3::ZERO, Vec3::Y), diff --git a/examples/3d/visibility_range.rs b/examples/3d/visibility_range.rs index 23618cd25706e..26f953ec07aa5 100644 --- a/examples/3d/visibility_range.rs +++ b/examples/3d/visibility_range.rs @@ -131,7 +131,7 @@ fn setup( commands.spawn(( DirectionalLight { illuminance: FULL_DAYLIGHT, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, PI * -0.15, PI * -0.15)), diff --git a/examples/3d/volumetric_fog.rs b/examples/3d/volumetric_fog.rs index 53e99313e8489..2f0d5e085c129 100644 --- a/examples/3d/volumetric_fog.rs +++ b/examples/3d/volumetric_fog.rs @@ -86,7 +86,7 @@ fn setup(mut commands: Commands, asset_server: Res, app_settings: R commands.spawn(( Transform::from_xyz(-0.4, 1.9, 1.0), PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, range: 150.0, color: RED.into(), intensity: 10_000.0, @@ -106,7 +106,7 @@ fn setup(mut commands: Commands, asset_server: Res, app_settings: R SpotLight { intensity: 50_000.0, // lumens color: Color::WHITE, - shadows_enabled: true, + shadow_maps_enabled: true, inner_angle: 0.76, outer_angle: 0.94, ..default() @@ -158,7 +158,7 @@ fn tweak_scene( ) { for (light, mut directional_light) in lights.iter_mut() { // Shadows are needed for volumetric lights to work. - directional_light.shadows_enabled = true; + directional_light.shadow_maps_enabled = true; commands.entity(light).insert(VolumetricLight); } } diff --git a/examples/README.md b/examples/README.md index 487832ecb3543..3614007af59cc 100644 --- a/examples/README.md +++ b/examples/README.md @@ -158,6 +158,7 @@ Example | Description [Clustered Decal Maps](../examples/3d/clustered_decal_maps.rs) | Demonstrates normal and metallic-roughness maps of decals [Clustered Decals](../examples/3d/clustered_decals.rs) | Demonstrates clustered decals [Color grading](../examples/3d/color_grading.rs) | Demonstrates color grading +[Contact Shadows](../examples/3d/contact_shadows.rs) | Showcases how contact shadows add shadow detail [Decal](../examples/3d/decal.rs) | Decal rendering [Deferred Rendering](../examples/3d/deferred_rendering.rs) | Renders meshes with both forward and deferred pipelines [Depth of field](../examples/3d/depth_of_field.rs) | Demonstrates depth of field diff --git a/examples/animation/animated_mesh.rs b/examples/animation/animated_mesh.rs index d23910352175e..56172b1b9ab56 100644 --- a/examples/animation/animated_mesh.rs +++ b/examples/animation/animated_mesh.rs @@ -116,7 +116,7 @@ fn setup_camera_and_environment( commands.spawn(( Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)), DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, CascadeShadowConfigBuilder { diff --git a/examples/animation/animated_mesh_control.rs b/examples/animation/animated_mesh_control.rs index 0feaa2759c03a..16825934625e0 100644 --- a/examples/animation/animated_mesh_control.rs +++ b/examples/animation/animated_mesh_control.rs @@ -64,7 +64,7 @@ fn setup( commands.spawn(( Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)), DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, CascadeShadowConfigBuilder { diff --git a/examples/animation/animated_mesh_events.rs b/examples/animation/animated_mesh_events.rs index 03c38ed22ad9d..35ad97a182794 100644 --- a/examples/animation/animated_mesh_events.rs +++ b/examples/animation/animated_mesh_events.rs @@ -113,7 +113,7 @@ fn setup( commands.spawn(( Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)), DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, CascadeShadowConfigBuilder { diff --git a/examples/animation/animation_graph.rs b/examples/animation/animation_graph.rs index 7393105058283..0b5b445c0907d 100644 --- a/examples/animation/animation_graph.rs +++ b/examples/animation/animation_graph.rs @@ -232,7 +232,7 @@ fn setup_scene( commands.spawn(( PointLight { intensity: 10_000_000.0, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(-4.0, 8.0, 13.0), diff --git a/examples/animation/animation_masks.rs b/examples/animation/animation_masks.rs index d357850a6319d..2baa8246c6709 100644 --- a/examples/animation/animation_masks.rs +++ b/examples/animation/animation_masks.rs @@ -132,7 +132,7 @@ fn setup_scene( commands.spawn(( PointLight { intensity: 10_000_000.0, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(-4.0, 8.0, 13.0), diff --git a/examples/animation/eased_motion.rs b/examples/animation/eased_motion.rs index cf44dfdd3b60e..3a946199a1740 100644 --- a/examples/animation/eased_motion.rs +++ b/examples/animation/eased_motion.rs @@ -54,7 +54,7 @@ fn setup( // Some light to see something commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, intensity: 10_000_000., range: 100.0, ..default() diff --git a/examples/app/headless_renderer.rs b/examples/app/headless_renderer.rs index 2ab79e7660a98..2d4f42682f70b 100644 --- a/examples/app/headless_renderer.rs +++ b/examples/app/headless_renderer.rs @@ -186,7 +186,7 @@ fn setup( // light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/asset/multi_asset_sync.rs b/examples/asset/multi_asset_sync.rs index 8330be603a2a3..94a061be4a776 100644 --- a/examples/asset/multi_asset_sync.rs +++ b/examples/asset/multi_asset_sync.rs @@ -196,7 +196,7 @@ fn setup_scene( // Light commands.spawn(( DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)), diff --git a/examples/asset/repeated_texture.rs b/examples/asset/repeated_texture.rs index 34664a9e42b5c..1f003cd083b69 100644 --- a/examples/asset/repeated_texture.rs +++ b/examples/asset/repeated_texture.rs @@ -80,7 +80,7 @@ fn setup( // light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/async_tasks/async_channel_pattern.rs b/examples/async_tasks/async_channel_pattern.rs index 97f18d46c6521..533776d4fb06d 100644 --- a/examples/async_tasks/async_channel_pattern.rs +++ b/examples/async_tasks/async_channel_pattern.rs @@ -143,7 +143,7 @@ fn setup_env( // Spawn a point light with shadows enabled commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(0.0, LIGHT_RADIUS, 4.0), diff --git a/examples/camera/custom_projection.rs b/examples/camera/custom_projection.rs index bdd5b71d5ca82..5a8f41fbbb107 100644 --- a/examples/camera/custom_projection.rs +++ b/examples/camera/custom_projection.rs @@ -76,7 +76,7 @@ fn setup( )); commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/camera/first_person_view_model.rs b/examples/camera/first_person_view_model.rs index 304b3bb3c07a0..eb6aeb7fcb5c4 100644 --- a/examples/camera/first_person_view_model.rs +++ b/examples/camera/first_person_view_model.rs @@ -178,7 +178,7 @@ fn spawn_lights(mut commands: Commands) { commands.spawn(( PointLight { color: Color::from(tailwind::ROSE_300), - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(-2.0, 4.0, -0.75), diff --git a/examples/camera/free_camera_controller.rs b/examples/camera/free_camera_controller.rs index d7ebde14629aa..1e4978ac9e6f6 100644 --- a/examples/camera/free_camera_controller.rs +++ b/examples/camera/free_camera_controller.rs @@ -195,7 +195,7 @@ fn spawn_lights(mut commands: Commands) { commands.spawn(( PointLight { color: Color::from(tailwind::ORANGE_300), - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(0.0, 3.0, 0.0), @@ -204,7 +204,7 @@ fn spawn_lights(mut commands: Commands) { commands.spawn(( PointLight { color: Color::WHITE, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(-3.5, 3.0, 0.0), @@ -213,7 +213,7 @@ fn spawn_lights(mut commands: Commands) { commands.spawn(( PointLight { color: Color::from(tailwind::RED_300), - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(0.0, -0.5, 0.0), diff --git a/examples/diagnostics/log_diagnostics.rs b/examples/diagnostics/log_diagnostics.rs index 02cc5af473ba8..af6a39c269016 100644 --- a/examples/diagnostics/log_diagnostics.rs +++ b/examples/diagnostics/log_diagnostics.rs @@ -75,7 +75,7 @@ fn setup( // light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/ecs/error_handling.rs b/examples/ecs/error_handling.rs index d1c14a5ea8dad..90f0f6815c6a0 100644 --- a/examples/ecs/error_handling.rs +++ b/examples/ecs/error_handling.rs @@ -73,7 +73,7 @@ fn setup( // Spawn a light: commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/games/alien_cake_addict.rs b/examples/games/alien_cake_addict.rs index 2e9f33bf739bb..3a0c41fdc6d2b 100644 --- a/examples/games/alien_cake_addict.rs +++ b/examples/games/alien_cake_addict.rs @@ -123,7 +123,7 @@ fn setup(mut commands: Commands, asset_server: Res, mut game: ResMu DespawnOnExit(GameState::Playing), PointLight { intensity: 2_000_000.0, - shadows_enabled: true, + shadow_maps_enabled: true, range: 30.0, ..default() }, diff --git a/examples/games/loading_screen.rs b/examples/games/loading_screen.rs index 5f9e4c5c6bd76..69e8acbfb5ea3 100644 --- a/examples/games/loading_screen.rs +++ b/examples/games/loading_screen.rs @@ -153,7 +153,7 @@ fn load_level_1( // Spawn the light. commands.spawn(( DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(3.0, 3.0, 2.0).looking_at(Vec3::ZERO, Vec3::Y), @@ -184,7 +184,7 @@ fn load_level_2( // Spawn the light. commands.spawn(( DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(3.0, 3.0, 2.0).looking_at(Vec3::ZERO, Vec3::Y), diff --git a/examples/gizmos/3d_gizmos.rs b/examples/gizmos/3d_gizmos.rs index aac66225779b8..9da5f5a30a72a 100644 --- a/examples/gizmos/3d_gizmos.rs +++ b/examples/gizmos/3d_gizmos.rs @@ -68,7 +68,7 @@ fn setup( // light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/gizmos/axes.rs b/examples/gizmos/axes.rs index aaebac05fdb33..91301b5da74d1 100644 --- a/examples/gizmos/axes.rs +++ b/examples/gizmos/axes.rs @@ -49,7 +49,7 @@ fn setup( // Lights... commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(2., 6., 0.), diff --git a/examples/gizmos/light_gizmos.rs b/examples/gizmos/light_gizmos.rs index 1a4dcd1970c96..d0df047b04d16 100644 --- a/examples/gizmos/light_gizmos.rs +++ b/examples/gizmos/light_gizmos.rs @@ -65,7 +65,7 @@ fn setup( { commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, range: 2.0, color: DARK_CYAN.into(), ..default() @@ -74,7 +74,7 @@ fn setup( )); commands.spawn(( SpotLight { - shadows_enabled: true, + shadow_maps_enabled: true, range: 3.5, color: PURPLE.into(), outer_angle: PI / 4.0, @@ -87,7 +87,7 @@ fn setup( DirectionalLight { color: GOLD.into(), illuminance: DirectionalLight::default().illuminance * 0.05, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(-4.0, 2.0, 0.0).looking_at(Vec3::NEG_X * 1.5, Vec3::Y), diff --git a/examples/gltf/gltf_extension_animation_graph.rs b/examples/gltf/gltf_extension_animation_graph.rs index 7c0d8d0748426..483fc1001ad35 100644 --- a/examples/gltf/gltf_extension_animation_graph.rs +++ b/examples/gltf/gltf_extension_animation_graph.rs @@ -98,7 +98,7 @@ fn setup_camera_and_environment( commands.spawn(( Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)), DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, CascadeShadowConfigBuilder { diff --git a/examples/gltf/load_gltf.rs b/examples/gltf/load_gltf.rs index 827bd5d695ba2..2e6f790a95802 100644 --- a/examples/gltf/load_gltf.rs +++ b/examples/gltf/load_gltf.rs @@ -29,7 +29,7 @@ fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(( DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, // This is a relatively small scene, so use tighter shadow diff --git a/examples/gltf/load_gltf_extras.rs b/examples/gltf/load_gltf_extras.rs index f9102106c0e0b..0082335aa665d 100644 --- a/examples/gltf/load_gltf_extras.rs +++ b/examples/gltf/load_gltf_extras.rs @@ -23,7 +23,7 @@ fn setup(mut commands: Commands, asset_server: Res) { )); commands.spawn(DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }); diff --git a/examples/gltf/update_gltf_scene.rs b/examples/gltf/update_gltf_scene.rs index f29b68944f336..4feead1775659 100644 --- a/examples/gltf/update_gltf_scene.rs +++ b/examples/gltf/update_gltf_scene.rs @@ -19,7 +19,7 @@ fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(( Transform::from_xyz(4.0, 25.0, 8.0).looking_at(Vec3::ZERO, Vec3::Y), DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, )); diff --git a/examples/helpers/widgets.rs b/examples/helpers/widgets.rs index 665e64f29734b..e7ff3cd0bc82d 100644 --- a/examples/helpers/widgets.rs +++ b/examples/helpers/widgets.rs @@ -139,9 +139,9 @@ where }, Children::spawn(( Spawn(( - ui_text(title, Color::BLACK), + ui_text(title, Color::WHITE), Node { - width: px(125), + width: px(150), ..default() }, )), diff --git a/examples/large_scenes/bistro/src/main.rs b/examples/large_scenes/bistro/src/main.rs index 304e8a47d74bb..54c87d8f4f583 100644 --- a/examples/large_scenes/bistro/src/main.rs +++ b/examples/large_scenes/bistro/src/main.rs @@ -9,6 +9,7 @@ use std::{ }; use argh::FromArgs; +use bevy::pbr::ContactShadows; use bevy::{ anti_alias::taa::TemporalAntiAliasing, camera::visibility::{NoCpuCulling, NoFrustumCulling}, @@ -241,7 +242,8 @@ pub fn setup(mut commands: Commands, asset_server: Res, args: Res, args: Res) { commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(-1.0, 2.0, -3.0), diff --git a/examples/large_scenes/mipmap_generator/examples/test_image.rs b/examples/large_scenes/mipmap_generator/examples/test_image.rs index d08d0730fc645..250a63441e36a 100644 --- a/examples/large_scenes/mipmap_generator/examples/test_image.rs +++ b/examples/large_scenes/mipmap_generator/examples/test_image.rs @@ -35,7 +35,7 @@ fn setup( commands.spawn(( PointLight { intensity: 1500.0 * 1000.0, - shadows_enabled: false, + shadow_maps_enabled: false, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/math/custom_primitives.rs b/examples/math/custom_primitives.rs index ba2976d7a8472..553a91a78d8a2 100644 --- a/examples/math/custom_primitives.rs +++ b/examples/math/custom_primitives.rs @@ -243,7 +243,7 @@ fn setup( // Point light for 3D commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, intensity: 10_000_000., range: 100.0, shadow_depth_bias: 0.2, diff --git a/examples/math/random_sampling.rs b/examples/math/random_sampling.rs index b20594b652a08..2771153e2880a 100644 --- a/examples/math/random_sampling.rs +++ b/examples/math/random_sampling.rs @@ -82,7 +82,7 @@ fn setup( // A light: commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/math/sampling_primitives.rs b/examples/math/sampling_primitives.rs index b5b815f416064..04bfdce859755 100644 --- a/examples/math/sampling_primitives.rs +++ b/examples/math/sampling_primitives.rs @@ -317,7 +317,7 @@ fn setup( range: 4.0, radius: 0.6, intensity: 1.0, - shadows_enabled: false, + shadow_maps_enabled: false, color: Color::LinearRgba(INSIDE_POINT_COLOR), ..default() }, @@ -331,7 +331,7 @@ fn setup( PointLight { color: SKY_COLOR, intensity: 2_000.0, - shadows_enabled: false, + shadow_maps_enabled: false, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/mobile/src/lib.rs b/examples/mobile/src/lib.rs index b70da4c672a0f..d9f68bc18880f 100644 --- a/examples/mobile/src/lib.rs +++ b/examples/mobile/src/lib.rs @@ -122,7 +122,7 @@ fn setup_scene( // Shadows makes some Android devices segfault, this is under investigation // https://github.com/bevyengine/bevy/issues/8214 #[cfg(not(target_os = "android"))] - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/movement/smooth_follow.rs b/examples/movement/smooth_follow.rs index ccf31e7cf66ee..dfb4e2cde1c4e 100644 --- a/examples/movement/smooth_follow.rs +++ b/examples/movement/smooth_follow.rs @@ -70,7 +70,7 @@ fn setup( commands.spawn(( PointLight { intensity: 15_000_000.0, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/picking/debug_picking.rs b/examples/picking/debug_picking.rs index 2f784ba8e0ece..94696455b370f 100644 --- a/examples/picking/debug_picking.rs +++ b/examples/picking/debug_picking.rs @@ -70,7 +70,7 @@ fn setup_scene( // Light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/picking/mesh_picking.rs b/examples/picking/mesh_picking.rs index f51b544ee3f2a..4766d43a0a86c 100644 --- a/examples/picking/mesh_picking.rs +++ b/examples/picking/mesh_picking.rs @@ -129,7 +129,7 @@ fn setup_scene( // Light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, intensity: 10_000_000., range: 100.0, shadow_depth_bias: 0.2, diff --git a/examples/picking/simple_picking.rs b/examples/picking/simple_picking.rs index ede13012535f3..cc47fc68f2f95 100644 --- a/examples/picking/simple_picking.rs +++ b/examples/picking/simple_picking.rs @@ -46,7 +46,7 @@ fn setup_scene( // Light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/remote/server.rs b/examples/remote/server.rs index 0acf76dba7cfa..e240caae9e255 100644 --- a/examples/remote/server.rs +++ b/examples/remote/server.rs @@ -52,7 +52,7 @@ fn setup( // light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/shader/shader_prepass.rs b/examples/shader/shader_prepass.rs index 58b6bf7ed7360..21ef394078d6b 100644 --- a/examples/shader/shader_prepass.rs +++ b/examples/shader/shader_prepass.rs @@ -114,7 +114,7 @@ fn setup( // light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/shader_advanced/custom_render_phase.rs b/examples/shader_advanced/custom_render_phase.rs index 8fd1cdc837539..01537a37cefea 100644 --- a/examples/shader_advanced/custom_render_phase.rs +++ b/examples/shader_advanced/custom_render_phase.rs @@ -97,7 +97,7 @@ fn setup( // light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/shader_advanced/manual_material.rs b/examples/shader_advanced/manual_material.rs index f7ac7c66cdec7..f855cc4d14431 100644 --- a/examples/shader_advanced/manual_material.rs +++ b/examples/shader_advanced/manual_material.rs @@ -228,7 +228,7 @@ fn setup( // light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/examples/stress_tests/bevymark_3d.rs b/examples/stress_tests/bevymark_3d.rs index f71c69671bfb1..6442c905667bd 100644 --- a/examples/stress_tests/bevymark_3d.rs +++ b/examples/stress_tests/bevymark_3d.rs @@ -224,7 +224,7 @@ fn setup( commands.spawn(( DirectionalLight { illuminance: 10000.0, - shadows_enabled: false, + shadow_maps_enabled: false, ..default() }, Transform::from_xyz(1.0, 2.0, 3.0).looking_at(Vec3::ZERO, Vec3::Y), diff --git a/examples/stress_tests/many_cameras_lights.rs b/examples/stress_tests/many_cameras_lights.rs index 2e10098c06252..596ae93e4dd7e 100644 --- a/examples/stress_tests/many_cameras_lights.rs +++ b/examples/stress_tests/many_cameras_lights.rs @@ -58,7 +58,7 @@ fn setup( PointLight { color: Color::hsv(angle.to_degrees(), 1.0, 1.0), intensity: 2_000_000.0 / NUM_LIGHTS as f32, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(sin(angle) * 4.0, 2.0, cos(angle) * 4.0), diff --git a/examples/stress_tests/many_cubes.rs b/examples/stress_tests/many_cubes.rs index a1d95acf19532..73107629e624f 100644 --- a/examples/stress_tests/many_cubes.rs +++ b/examples/stress_tests/many_cubes.rs @@ -293,7 +293,7 @@ fn setup( commands.spawn(( DirectionalLight { - shadows_enabled: args.shadows, + shadow_maps_enabled: args.shadows, ..default() }, Transform::IDENTITY.looking_at(Vec3::new(0.0, -1.0, -1.0), Vec3::Y), diff --git a/examples/stress_tests/many_foxes.rs b/examples/stress_tests/many_foxes.rs index ea68282caa529..da2c4a8ba0a71 100644 --- a/examples/stress_tests/many_foxes.rs +++ b/examples/stress_tests/many_foxes.rs @@ -209,7 +209,7 @@ fn setup( commands.spawn(( Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)), DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, CascadeShadowConfigBuilder { diff --git a/examples/stress_tests/many_materials.rs b/examples/stress_tests/many_materials.rs index 65f5ecc5f545e..be50740b4a9d8 100644 --- a/examples/stress_tests/many_materials.rs +++ b/examples/stress_tests/many_materials.rs @@ -71,7 +71,7 @@ fn setup( Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)), DirectionalLight { illuminance: 3000.0, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, )); diff --git a/examples/testbed/3d.rs b/examples/testbed/3d.rs index 01c5c5bd40a43..c1f1152057af6 100644 --- a/examples/testbed/3d.rs +++ b/examples/testbed/3d.rs @@ -140,7 +140,7 @@ mod light { PointLight { intensity: 100_000.0, color: RED.into(), - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(1.0, 2.0, 0.0), @@ -151,7 +151,7 @@ mod light { SpotLight { intensity: 100_000.0, color: LIME.into(), - shadows_enabled: true, + shadow_maps_enabled: true, inner_angle: 0.6, outer_angle: 0.8, ..default() @@ -163,7 +163,7 @@ mod light { commands.spawn(( DirectionalLight { illuminance: light_consts::lux::OVERCAST_DAY, - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform { @@ -248,7 +248,7 @@ mod gltf { commands.spawn(( DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, DespawnOnExit(CURRENT_SCENE), @@ -300,7 +300,7 @@ mod animation { commands.spawn(( Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)), DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, DespawnOnExit(CURRENT_SCENE), diff --git a/examples/tools/scene_viewer/scene_viewer_plugin.rs b/examples/tools/scene_viewer/scene_viewer_plugin.rs index 7565307003c66..4f3ab56011e7d 100644 --- a/examples/tools/scene_viewer/scene_viewer_plugin.rs +++ b/examples/tools/scene_viewer/scene_viewer_plugin.rs @@ -147,7 +147,7 @@ fn update_lights( ) { for (_, mut light) in &mut query { if key_input.just_pressed(KeyCode::KeyU) { - light.shadows_enabled = !light.shadows_enabled; + light.shadow_maps_enabled = !light.shadow_maps_enabled; } } diff --git a/examples/transforms/align.rs b/examples/transforms/align.rs index 1f2ba48400b6a..01496377cf0c6 100644 --- a/examples/transforms/align.rs +++ b/examples/transforms/align.rs @@ -68,7 +68,7 @@ fn setup( // A light source commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 7.0, -4.0), diff --git a/examples/window/screenshot.rs b/examples/window/screenshot.rs index 61323be78f42f..c864f5763f5ca 100644 --- a/examples/window/screenshot.rs +++ b/examples/window/screenshot.rs @@ -66,7 +66,7 @@ fn setup( // light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/release-content/migration-guides/contact_shadows.md b/release-content/migration-guides/contact_shadows.md new file mode 100644 index 0000000000000..74bcddcbe6fb6 --- /dev/null +++ b/release-content/migration-guides/contact_shadows.md @@ -0,0 +1,8 @@ +--- +title: Contact Shadows +pull_requests: [ 22382 ] +--- + +The `shadows_enabled` field on `PointLight`, `DirectionalLight`, and `SpotLight` has changed to `shadow_maps_enabled`. + +This was changed because these lights now support contact shadows, and have a `contact_shadows_enabled` field. The old `shadows_enabled` field only configures shadow maps, making the old name misleading. diff --git a/tests/3d/test_invalid_skinned_mesh.rs b/tests/3d/test_invalid_skinned_mesh.rs index f371a06b7130e..cbd1082fcdb37 100644 --- a/tests/3d/test_invalid_skinned_mesh.rs +++ b/tests/3d/test_invalid_skinned_mesh.rs @@ -72,7 +72,7 @@ fn setup_environment( commands.spawn(( Transform::from_xyz(1.0, 1.0, 3.0).looking_at(Vec3::ZERO, Vec3::Y), DirectionalLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, )); diff --git a/tests/window/minimizing.rs b/tests/window/minimizing.rs index 8ba5d79fdd75c..8099c27a76365 100644 --- a/tests/window/minimizing.rs +++ b/tests/window/minimizing.rs @@ -46,7 +46,7 @@ fn setup_3d( // light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0), diff --git a/tests/window/resizing.rs b/tests/window/resizing.rs index d0a41fc9461c6..d0b0cb8aaf354 100644 --- a/tests/window/resizing.rs +++ b/tests/window/resizing.rs @@ -122,7 +122,7 @@ fn setup_3d( // light commands.spawn(( PointLight { - shadows_enabled: true, + shadow_maps_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0),