From d32d7dacd10f621e68ad74bd4a38849d8404ab78 Mon Sep 17 00:00:00 2001 From: VictorElHajj Date: Mon, 22 Jun 2026 19:31:14 +0200 Subject: [PATCH] Add prepass_reads_material flag in material that sets PREPASS_READS_MATERIAL --- crates/bevy_material/src/lib.rs | 2 ++ crates/bevy_pbr/src/extended_material.rs | 9 +++++++++ crates/bevy_pbr/src/material.rs | 7 +++++++ crates/bevy_pbr/src/prepass/mod.rs | 13 +++++++++++-- crates/bevy_pbr/src/render/light.rs | 4 ++++ 5 files changed, 33 insertions(+), 2 deletions(-) diff --git a/crates/bevy_material/src/lib.rs b/crates/bevy_material/src/lib.rs index 296554085a129..fcb1375ea0a71 100644 --- a/crates/bevy_material/src/lib.rs +++ b/crates/bevy_material/src/lib.rs @@ -94,6 +94,8 @@ pub struct MaterialProperties { pub shadows_enabled: bool, /// Whether prepass is enabled for this material pub prepass_enabled: bool, + /// Whether prepass needs to read the material + pub prepass_reads_material: bool, } impl MaterialProperties { diff --git a/crates/bevy_pbr/src/extended_material.rs b/crates/bevy_pbr/src/extended_material.rs index 088a81f65cb2f..24f7e76372ef9 100644 --- a/crates/bevy_pbr/src/extended_material.rs +++ b/crates/bevy_pbr/src/extended_material.rs @@ -61,6 +61,11 @@ pub trait MaterialExtension: Asset + AsBindGroup + Clone + Sized { true } + #[inline] + fn prepass_reads_material() -> bool { + false + } + /// Returns this material's prepass vertex shader. If [`ShaderRef::Default`] is returned, the base material prepass vertex shader /// will be used. fn prepass_vertex_shader() -> ShaderRef { @@ -355,6 +360,10 @@ impl Material for ExtendedMaterial { E::enable_shadows() } + fn prepass_reads_material() -> bool { + E::prepass_reads_material() + } + fn prepass_vertex_shader() -> ShaderRef { match E::prepass_vertex_shader() { ShaderRef::Default => B::prepass_vertex_shader(), diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 967ccda522ace..fb7fe28342e8a 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -200,6 +200,11 @@ pub trait Material: Asset + AsBindGroup + Clone + Sized { true } + #[inline] + fn prepass_reads_material() -> bool { + false + } + /// Returns this material's prepass vertex shader. If [`ShaderRef::Default`] is returned, the default prepass vertex shader /// will be used. /// @@ -1690,6 +1695,7 @@ where let shadows_enabled = M::enable_shadows(); let prepass_enabled = M::enable_prepass(); + let prepass_reads_material = M::prepass_reads_material(); let draw_opaque_pbr = opaque_draw_functions.read().id::(); let draw_alpha_mask_pbr = alpha_mask_draw_functions.read().id::(); @@ -1790,6 +1796,7 @@ where material_key, shadows_enabled, prepass_enabled, + prepass_reads_material, }), }) } diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 85a7f813ba448..308d6195f50ac 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -386,7 +386,9 @@ impl SpecializedMeshPipeline for PrepassPipelineSpecializer { } fn is_depth_only_opaque_prepass(mesh_key: MeshPipelineKey) -> bool { - mesh_key.intersection(MeshPipelineKey::ALL_PREPASS_BITS) == MeshPipelineKey::DEPTH_PREPASS + !mesh_key.contains(MeshPipelineKey::PREPASS_READS_MATERIAL) + && mesh_key.intersection(MeshPipelineKey::ALL_PREPASS_BITS) + == MeshPipelineKey::DEPTH_PREPASS } impl PrepassPipeline { @@ -421,7 +423,10 @@ impl PrepassPipeline { // or emulated by setting depth in the fragment shader for GPUs that don't support it natively. let emulate_unclipped_depth = mesh_key.contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO) && !self.depth_clip_control_supported; - if is_depth_only_opaque_prepass(mesh_key) && !emulate_unclipped_depth { + if is_depth_only_opaque_prepass(mesh_key) + && !emulate_unclipped_depth + && !material_properties.prepass_reads_material + { bind_group_layouts.push(self.empty_layout.clone()); } else { bind_group_layouts.push( @@ -1114,6 +1119,10 @@ pub(crate) fn specialize_prepass_material_meshes( mesh_key |= MeshPipelineKey::VISIBILITY_RANGE_DITHER; } + if material.properties.prepass_reads_material { + mesh_key |= MeshPipelineKey::PREPASS_READS_MATERIAL; + } + // If the previous frame has skins or morph targets, note that. if motion_vector_prepass.is_some() { if mesh_instance diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 0188614a17623..dd1ccabed4baf 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -2448,6 +2448,10 @@ pub(crate) fn specialize_shadows( _ => MeshPipelineKey::NONE, }; + if material.properties.prepass_reads_material { + mesh_key |= MeshPipelineKey::PREPASS_READS_MATERIAL; + } + work_items.push(ShadowSpecializationWorkItem { visible_entity: *visible_entity, retained_view_entity: extracted_view_light.retained_view_entity,