Make PCSS experimental (#16382)

# Objective

PCSS still has some fundamental issues (#16155). We should resolve them
before "releasing" the feature.

## Solution

1. Rename the already-optional `pbr_pcss` cargo feature to
`experimental_pbr_pcss` to better communicate its state to developers.
2. Adjust the description of the `experimental_pbr_pcss` cargo feature
to better communicate its state to developers.
3. Gate PCSS-related light component fields behind that cargo feature,
to prevent surfacing them to developers by default.
This commit is contained in:
Carter Anderson 2024-11-13 23:39:26 -08:00 committed by François Mockers
parent 988770ad99
commit e6fbcb786b
10 changed files with 41 additions and 26 deletions

View file

@ -403,7 +403,7 @@ pbr_multi_layer_material_textures = [
pbr_anisotropy_texture = ["bevy_internal/pbr_anisotropy_texture"] pbr_anisotropy_texture = ["bevy_internal/pbr_anisotropy_texture"]
# Enable support for PCSS, at the risk of blowing past the global, per-shader sampler limit on older/lower-end GPUs # Enable support for PCSS, at the risk of blowing past the global, per-shader sampler limit on older/lower-end GPUs
pbr_pcss = ["bevy_internal/pbr_pcss"] experimental_pbr_pcss = ["bevy_internal/experimental_pbr_pcss"]
# Enable some limitations to be able to use WebGL2. Please refer to the [WebGL2 and WebGPU](https://github.com/bevyengine/bevy/tree/latest/examples#webgl2-and-webgpu) section of the examples README for more information on how to run Wasm builds with WebGPU. # Enable some limitations to be able to use WebGL2. Please refer to the [WebGL2 and WebGPU](https://github.com/bevyengine/bevy/tree/latest/examples#webgl2-and-webgpu) section of the examples README for more information on how to run Wasm builds with WebGPU.
webgl2 = ["bevy_internal/webgl"] webgl2 = ["bevy_internal/webgl"]
@ -3790,7 +3790,7 @@ wasm = true
name = "pcss" name = "pcss"
path = "examples/3d/pcss.rs" path = "examples/3d/pcss.rs"
doc-scrape-examples = true doc-scrape-examples = true
required-features = ["pbr_pcss"] required-features = ["experimental_pbr_pcss"]
[package.metadata.example.pcss] [package.metadata.example.pcss]
name = "Percentage-closer soft shadows" name = "Percentage-closer soft shadows"

View file

@ -135,7 +135,7 @@ pbr_anisotropy_texture = [
] ]
# Percentage-closer soft shadows # Percentage-closer soft shadows
pbr_pcss = ["bevy_pbr?/pbr_pcss"] experimental_pbr_pcss = ["bevy_pbr?/experimental_pbr_pcss"]
# Optimise for WebGL2 # Optimise for WebGL2
webgl = [ webgl = [

View file

@ -14,7 +14,7 @@ webgpu = []
pbr_transmission_textures = [] pbr_transmission_textures = []
pbr_multi_layer_material_textures = [] pbr_multi_layer_material_textures = []
pbr_anisotropy_texture = [] pbr_anisotropy_texture = []
pbr_pcss = [] experimental_pbr_pcss = []
shader_format_glsl = ["bevy_render/shader_format_glsl"] shader_format_glsl = ["bevy_render/shader_format_glsl"]
trace = ["bevy_render/trace"] trace = ["bevy_render/trace"]
ios_simulator = ["bevy_render/ios_simulator"] ios_simulator = ["bevy_render/ios_simulator"]

View file

@ -95,6 +95,7 @@ pub struct DirectionalLight {
/// ///
/// Note that soft shadows are significantly more expensive to render than /// Note that soft shadows are significantly more expensive to render than
/// hard shadows. /// hard shadows.
#[cfg(feature = "experimental_pbr_pcss")]
pub soft_shadow_size: Option<f32>, pub soft_shadow_size: Option<f32>,
/// A value that adjusts the tradeoff between self-shadowing artifacts and /// A value that adjusts the tradeoff between self-shadowing artifacts and
@ -120,9 +121,10 @@ impl Default for DirectionalLight {
color: Color::WHITE, color: Color::WHITE,
illuminance: light_consts::lux::AMBIENT_DAYLIGHT, illuminance: light_consts::lux::AMBIENT_DAYLIGHT,
shadows_enabled: false, shadows_enabled: false,
soft_shadow_size: None,
shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS, shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS,
shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS, shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS,
#[cfg(feature = "experimental_pbr_pcss")]
soft_shadow_size: None,
} }
} }
} }

View file

@ -61,6 +61,7 @@ pub struct PointLight {
/// ///
/// Note that soft shadows are significantly more expensive to render than /// Note that soft shadows are significantly more expensive to render than
/// hard shadows. /// hard shadows.
#[cfg(feature = "experimental_pbr_pcss")]
pub soft_shadows_enabled: bool, pub soft_shadows_enabled: bool,
/// A bias used when sampling shadow maps to avoid "shadow-acne", or false shadow occlusions /// A bias used when sampling shadow maps to avoid "shadow-acne", or false shadow occlusions
@ -95,10 +96,11 @@ impl Default for PointLight {
range: 20.0, range: 20.0,
radius: 0.0, radius: 0.0,
shadows_enabled: false, shadows_enabled: false,
soft_shadows_enabled: false,
shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS, shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS,
shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS, shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS,
shadow_map_near_z: Self::DEFAULT_SHADOW_MAP_NEAR_Z, shadow_map_near_z: Self::DEFAULT_SHADOW_MAP_NEAR_Z,
#[cfg(feature = "experimental_pbr_pcss")]
soft_shadows_enabled: false,
} }
} }
} }

View file

@ -57,6 +57,7 @@ pub struct SpotLight {
/// ///
/// Note that soft shadows are significantly more expensive to render than /// Note that soft shadows are significantly more expensive to render than
/// hard shadows. /// hard shadows.
#[cfg(feature = "experimental_pbr_pcss")]
pub soft_shadows_enabled: bool, pub soft_shadows_enabled: bool,
/// A value that adjusts the tradeoff between self-shadowing artifacts and /// A value that adjusts the tradeoff between self-shadowing artifacts and
@ -115,12 +116,13 @@ impl Default for SpotLight {
range: 20.0, range: 20.0,
radius: 0.0, radius: 0.0,
shadows_enabled: false, shadows_enabled: false,
soft_shadows_enabled: false,
shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS, shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS,
shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS, shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS,
shadow_map_near_z: Self::DEFAULT_SHADOW_MAP_NEAR_Z, shadow_map_near_z: Self::DEFAULT_SHADOW_MAP_NEAR_Z,
inner_angle: 0.0, inner_angle: 0.0,
outer_angle: core::f32::consts::FRAC_PI_4, outer_angle: core::f32::consts::FRAC_PI_4,
#[cfg(feature = "experimental_pbr_pcss")]
soft_shadows_enabled: false,
} }
} }
} }

View file

@ -41,12 +41,12 @@ pub struct ExtractedPointLight {
pub radius: f32, pub radius: f32,
pub transform: GlobalTransform, pub transform: GlobalTransform,
pub shadows_enabled: bool, pub shadows_enabled: bool,
pub soft_shadows_enabled: bool,
pub shadow_depth_bias: f32, pub shadow_depth_bias: f32,
pub shadow_normal_bias: f32, pub shadow_normal_bias: f32,
pub shadow_map_near_z: f32, pub shadow_map_near_z: f32,
pub spot_light_angles: Option<(f32, f32)>, pub spot_light_angles: Option<(f32, f32)>,
pub volumetric: bool, pub volumetric: bool,
pub soft_shadows_enabled: bool,
} }
#[derive(Component, Debug)] #[derive(Component, Debug)]
@ -56,13 +56,13 @@ pub struct ExtractedDirectionalLight {
pub transform: GlobalTransform, pub transform: GlobalTransform,
pub shadows_enabled: bool, pub shadows_enabled: bool,
pub volumetric: bool, pub volumetric: bool,
pub soft_shadow_size: Option<f32>,
pub shadow_depth_bias: f32, pub shadow_depth_bias: f32,
pub shadow_normal_bias: f32, pub shadow_normal_bias: f32,
pub cascade_shadow_config: CascadeShadowConfig, pub cascade_shadow_config: CascadeShadowConfig,
pub cascades: EntityHashMap<Vec<Cascade>>, pub cascades: EntityHashMap<Vec<Cascade>>,
pub frusta: EntityHashMap<Vec<Frustum>>, pub frusta: EntityHashMap<Vec<Frustum>>,
pub render_layers: RenderLayers, pub render_layers: RenderLayers,
pub soft_shadow_size: Option<f32>,
} }
// NOTE: These must match the bit flags in bevy_pbr/src/render/mesh_view_types.wgsl! // NOTE: These must match the bit flags in bevy_pbr/src/render/mesh_view_types.wgsl!
@ -147,10 +147,10 @@ pub const MAX_CASCADES_PER_LIGHT: usize = 1;
#[derive(Resource, Clone)] #[derive(Resource, Clone)]
pub struct ShadowSamplers { pub struct ShadowSamplers {
pub point_light_comparison_sampler: Sampler, pub point_light_comparison_sampler: Sampler,
#[cfg(feature = "pbr_pcss")] #[cfg(feature = "experimental_pbr_pcss")]
pub point_light_linear_sampler: Sampler, pub point_light_linear_sampler: Sampler,
pub directional_light_comparison_sampler: Sampler, pub directional_light_comparison_sampler: Sampler,
#[cfg(feature = "pbr_pcss")] #[cfg(feature = "experimental_pbr_pcss")]
pub directional_light_linear_sampler: Sampler, pub directional_light_linear_sampler: Sampler,
} }
@ -174,7 +174,7 @@ impl FromWorld for ShadowSamplers {
compare: Some(CompareFunction::GreaterEqual), compare: Some(CompareFunction::GreaterEqual),
..base_sampler_descriptor ..base_sampler_descriptor
}), }),
#[cfg(feature = "pbr_pcss")] #[cfg(feature = "experimental_pbr_pcss")]
point_light_linear_sampler: render_device.create_sampler(&base_sampler_descriptor), point_light_linear_sampler: render_device.create_sampler(&base_sampler_descriptor),
directional_light_comparison_sampler: render_device.create_sampler( directional_light_comparison_sampler: render_device.create_sampler(
&SamplerDescriptor { &SamplerDescriptor {
@ -182,7 +182,7 @@ impl FromWorld for ShadowSamplers {
..base_sampler_descriptor ..base_sampler_descriptor
}, },
), ),
#[cfg(feature = "pbr_pcss")] #[cfg(feature = "experimental_pbr_pcss")]
directional_light_linear_sampler: render_device directional_light_linear_sampler: render_device
.create_sampler(&base_sampler_descriptor), .create_sampler(&base_sampler_descriptor),
} }
@ -291,7 +291,6 @@ pub fn extract_lights(
radius: point_light.radius, radius: point_light.radius,
transform: *transform, transform: *transform,
shadows_enabled: point_light.shadows_enabled, shadows_enabled: point_light.shadows_enabled,
soft_shadows_enabled: point_light.soft_shadows_enabled,
shadow_depth_bias: point_light.shadow_depth_bias, shadow_depth_bias: point_light.shadow_depth_bias,
// The factor of SQRT_2 is for the worst-case diagonal offset // The factor of SQRT_2 is for the worst-case diagonal offset
shadow_normal_bias: point_light.shadow_normal_bias shadow_normal_bias: point_light.shadow_normal_bias
@ -300,6 +299,10 @@ pub fn extract_lights(
shadow_map_near_z: point_light.shadow_map_near_z, shadow_map_near_z: point_light.shadow_map_near_z,
spot_light_angles: None, spot_light_angles: None,
volumetric: volumetric_light.is_some(), volumetric: volumetric_light.is_some(),
#[cfg(feature = "experimental_pbr_pcss")]
soft_shadows_enabled: point_light.soft_shadows_enabled,
#[cfg(not(feature = "experimental_pbr_pcss"))]
soft_shadows_enabled: false,
}; };
point_lights_values.push(( point_lights_values.push((
render_entity, render_entity,
@ -350,7 +353,6 @@ pub fn extract_lights(
radius: spot_light.radius, radius: spot_light.radius,
transform: *transform, transform: *transform,
shadows_enabled: spot_light.shadows_enabled, shadows_enabled: spot_light.shadows_enabled,
soft_shadows_enabled: spot_light.soft_shadows_enabled,
shadow_depth_bias: spot_light.shadow_depth_bias, shadow_depth_bias: spot_light.shadow_depth_bias,
// The factor of SQRT_2 is for the worst-case diagonal offset // The factor of SQRT_2 is for the worst-case diagonal offset
shadow_normal_bias: spot_light.shadow_normal_bias shadow_normal_bias: spot_light.shadow_normal_bias
@ -359,6 +361,10 @@ pub fn extract_lights(
shadow_map_near_z: spot_light.shadow_map_near_z, shadow_map_near_z: spot_light.shadow_map_near_z,
spot_light_angles: Some((spot_light.inner_angle, spot_light.outer_angle)), spot_light_angles: Some((spot_light.inner_angle, spot_light.outer_angle)),
volumetric: volumetric_light.is_some(), volumetric: volumetric_light.is_some(),
#[cfg(feature = "experimental_pbr_pcss")]
soft_shadows_enabled: spot_light.soft_shadows_enabled,
#[cfg(not(feature = "experimental_pbr_pcss"))]
soft_shadows_enabled: false,
}, },
render_visible_entities, render_visible_entities,
*frustum, *frustum,
@ -430,7 +436,10 @@ pub fn extract_lights(
illuminance: directional_light.illuminance, illuminance: directional_light.illuminance,
transform: *transform, transform: *transform,
volumetric: volumetric_light.is_some(), volumetric: volumetric_light.is_some(),
#[cfg(feature = "experimental_pbr_pcss")]
soft_shadow_size: directional_light.soft_shadow_size, soft_shadow_size: directional_light.soft_shadow_size,
#[cfg(not(feature = "experimental_pbr_pcss"))]
soft_shadow_size: None,
shadows_enabled: directional_light.shadows_enabled, shadows_enabled: directional_light.shadows_enabled,
shadow_depth_bias: directional_light.shadow_depth_bias, shadow_depth_bias: directional_light.shadow_depth_bias,
// The factor of SQRT_2 is for the worst-case diagonal offset // The factor of SQRT_2 is for the worst-case diagonal offset
@ -911,17 +920,17 @@ pub fn prepare_lights(
.extend(1.0 / (light.range * light.range)), .extend(1.0 / (light.range * light.range)),
position_radius: light.transform.translation().extend(light.radius), position_radius: light.transform.translation().extend(light.radius),
flags: flags.bits(), flags: flags.bits(),
soft_shadow_size: if light.soft_shadows_enabled {
light.radius
} else {
0.0
},
shadow_depth_bias: light.shadow_depth_bias, shadow_depth_bias: light.shadow_depth_bias,
shadow_normal_bias: light.shadow_normal_bias, shadow_normal_bias: light.shadow_normal_bias,
shadow_map_near_z: light.shadow_map_near_z, shadow_map_near_z: light.shadow_map_near_z,
spot_light_tan_angle, spot_light_tan_angle,
pad_a: 0.0, pad_a: 0.0,
pad_b: 0.0, pad_b: 0.0,
soft_shadow_size: if light.soft_shadows_enabled {
light.radius
} else {
0.0
},
}); });
global_light_meta.entity_to_index.insert(entity, index); global_light_meta.entity_to_index.insert(entity, index);
} }

View file

@ -1859,7 +1859,7 @@ impl SpecializedMeshPipeline for MeshPipeline {
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
shader_defs.push("WEBGL2".into()); shader_defs.push("WEBGL2".into());
#[cfg(feature = "pbr_pcss")] #[cfg(feature = "experimental_pbr_pcss")]
shader_defs.push("PCSS_SAMPLERS_AVAILABLE".into()); shader_defs.push("PCSS_SAMPLERS_AVAILABLE".into());
if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) { if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) {

View file

@ -228,7 +228,7 @@ fn layout_entries(
// Point Shadow Texture Array Comparison Sampler // Point Shadow Texture Array Comparison Sampler
(3, sampler(SamplerBindingType::Comparison)), (3, sampler(SamplerBindingType::Comparison)),
// Point Shadow Texture Array Linear Sampler // Point Shadow Texture Array Linear Sampler
#[cfg(feature = "pbr_pcss")] #[cfg(feature = "experimental_pbr_pcss")]
(4, sampler(SamplerBindingType::Filtering)), (4, sampler(SamplerBindingType::Filtering)),
// Directional Shadow Texture Array // Directional Shadow Texture Array
( (
@ -245,7 +245,7 @@ fn layout_entries(
// Directional Shadow Texture Array Comparison Sampler // Directional Shadow Texture Array Comparison Sampler
(6, sampler(SamplerBindingType::Comparison)), (6, sampler(SamplerBindingType::Comparison)),
// Directional Shadow Texture Array Linear Sampler // Directional Shadow Texture Array Linear Sampler
#[cfg(feature = "pbr_pcss")] #[cfg(feature = "experimental_pbr_pcss")]
(7, sampler(SamplerBindingType::Filtering)), (7, sampler(SamplerBindingType::Filtering)),
// PointLights // PointLights
( (
@ -580,11 +580,11 @@ pub fn prepare_mesh_view_bind_groups(
(1, light_binding.clone()), (1, light_binding.clone()),
(2, &shadow_bindings.point_light_depth_texture_view), (2, &shadow_bindings.point_light_depth_texture_view),
(3, &shadow_samplers.point_light_comparison_sampler), (3, &shadow_samplers.point_light_comparison_sampler),
#[cfg(feature = "pbr_pcss")] #[cfg(feature = "experimental_pbr_pcss")]
(4, &shadow_samplers.point_light_linear_sampler), (4, &shadow_samplers.point_light_linear_sampler),
(5, &shadow_bindings.directional_light_depth_texture_view), (5, &shadow_bindings.directional_light_depth_texture_view),
(6, &shadow_samplers.directional_light_comparison_sampler), (6, &shadow_samplers.directional_light_comparison_sampler),
#[cfg(feature = "pbr_pcss")] #[cfg(feature = "experimental_pbr_pcss")]
(7, &shadow_samplers.directional_light_linear_sampler), (7, &shadow_samplers.directional_light_linear_sampler),
(8, clusterable_objects_binding.clone()), (8, clusterable_objects_binding.clone()),
( (

View file

@ -67,6 +67,7 @@ The default feature set enables most of the expected features of a game engine,
|detailed_trace|Enable detailed trace event logging. These trace events are expensive even when off, thus they require compile time opt-in| |detailed_trace|Enable detailed trace event logging. These trace events are expensive even when off, thus they require compile time opt-in|
|dynamic_linking|Force dynamic linking, which improves iterative compile times| |dynamic_linking|Force dynamic linking, which improves iterative compile times|
|embedded_watcher|Enables watching in memory asset providers for Bevy Asset hot-reloading| |embedded_watcher|Enables watching in memory asset providers for Bevy Asset hot-reloading|
|experimental_pbr_pcss|Enable support for PCSS, at the risk of blowing past the global, per-shader sampler limit on older/lower-end GPUs|
|exr|EXR image format support| |exr|EXR image format support|
|ff|Farbfeld image format support| |ff|Farbfeld image format support|
|file_watcher|Enables watching the filesystem for Bevy Asset hot-reloading| |file_watcher|Enables watching the filesystem for Bevy Asset hot-reloading|
@ -83,7 +84,6 @@ The default feature set enables most of the expected features of a game engine,
|mp3|MP3 audio format support| |mp3|MP3 audio format support|
|pbr_anisotropy_texture|Enable support for anisotropy texture in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs| |pbr_anisotropy_texture|Enable support for anisotropy texture in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs|
|pbr_multi_layer_material_textures|Enable support for multi-layer material textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs| |pbr_multi_layer_material_textures|Enable support for multi-layer material textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs|
|pbr_pcss|Enable support for PCSS, at the risk of blowing past the global, per-shader sampler limit on older/lower-end GPUs|
|pbr_transmission_textures|Enable support for transmission-related textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs| |pbr_transmission_textures|Enable support for transmission-related textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs|
|pnm|PNM image format support, includes pam, pbm, pgm and ppm| |pnm|PNM image format support, includes pam, pbm, pgm and ppm|
|qoi|QOI image format support| |qoi|QOI image format support|