diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 884b08a423ac7..530a2fa71ee55 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -52,8 +52,8 @@ use bevy_render::{ extract_resource::ExtractResourcePlugin, prelude::Color, render_graph::RenderGraph, - render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions}, - render_resource::{Shader, SpecializedMeshPipelines}, + render_phase::sort_phase_system, + render_resource::Shader, view::{ViewSet, VisibilitySystems}, ExtractSchedule, RenderApp, RenderSet, }; @@ -80,8 +80,6 @@ pub const PBR_FUNCTIONS_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 16550102964439850292); pub const PBR_AMBIENT_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2441520459096337034); -pub const SHADOW_SHADER_HANDLE: HandleUntyped = - HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 1836745567947005696); /// Sets up the entire PBR infrastructure of bevy. pub struct PbrPlugin { @@ -144,12 +142,6 @@ impl Plugin for PbrPlugin { Shader::from_wgsl ); load_internal_asset!(app, PBR_SHADER_HANDLE, "render/pbr.wgsl", Shader::from_wgsl); - load_internal_asset!( - app, - SHADOW_SHADER_HANDLE, - "render/depth.wgsl", - Shader::from_wgsl - ); load_internal_asset!( app, PBR_PREPASS_SHADER_HANDLE, @@ -293,17 +285,12 @@ impl Plugin for PbrPlugin { .after(render::prepare_lights) .in_set(RenderLightSystems::PrepareClusters), ) - .add_system(render::queue_shadows.in_set(RenderLightSystems::QueueShadows)) - .add_system(render::queue_shadow_view_bind_group.in_set(RenderSet::Queue)) .add_system(sort_phase_system::.in_set(RenderSet::PhaseSort)) - .init_resource::() - .init_resource::>() + .init_resource::() .init_resource::() - .init_resource::() - .init_resource::>(); + .init_resource::(); let shadow_pass_node = ShadowPassNode::new(&mut render_app.world); - render_app.add_render_command::(); let mut graph = render_app.world.resource_mut::(); let draw_3d_graph = graph .get_sub_graph_mut(bevy_core_pipeline::core_3d::graph::NAME) diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 7ab25bdd9d553..16213b2bb1ce0 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -1,6 +1,7 @@ use crate::{ - AlphaMode, DrawMesh, EnvironmentMapLight, MeshPipeline, MeshPipelineKey, MeshUniform, - PrepassPlugin, SetMeshBindGroup, SetMeshViewBindGroup, + queue_mesh_view_bind_groups, render, AlphaMode, DrawMesh, DrawPrepass, EnvironmentMapLight, + MeshPipeline, MeshPipelineKey, MeshUniform, PrepassPlugin, RenderLightSystems, + SetMeshBindGroup, SetMeshViewBindGroup, Shadow, }; use bevy_app::{App, IntoSystemAppConfig, Plugin}; use bevy_asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle}; @@ -188,6 +189,8 @@ where if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { render_app + .init_resource::>() + .add_render_command::>() .add_render_command::>() .add_render_command::>() .add_render_command::>() @@ -201,6 +204,12 @@ where .in_set(RenderSet::Prepare) .after(PrepareAssetSet::PreAssetPrepare), ) + .add_system(render::queue_shadows::.in_set(RenderLightSystems::QueueShadows)) + .add_system( + render::queue_shadow_view_bind_group:: + .in_set(RenderSet::Queue) + .ambiguous_with(queue_mesh_view_bind_groups), // queue_mesh_view_bind_groups does not read `shadow_view_bind_group`), + ) .add_system(queue_material_meshes::.in_set(RenderSet::Queue)); } diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 436f39350e63a..acfb160dd35c8 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -202,6 +202,13 @@ where shader_defs.push("ALPHA_MASK".into()); } + let blend_key = key + .mesh_key + .intersection(MeshPipelineKey::BLEND_RESERVED_BITS); + if blend_key == MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA { + shader_defs.push("BLEND_PREMULTIPLIED_ALPHA".into()); + } + if layout.contains(Mesh::ATTRIBUTE_POSITION) { shader_defs.push("VERTEX_POSITIONS".into()); vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0)); @@ -215,6 +222,9 @@ where "MAX_CASCADES_PER_LIGHT".to_string(), MAX_CASCADES_PER_LIGHT as i32, )); + if key.mesh_key.contains(MeshPipelineKey::DEPTH_CLAMP_ORTHO) { + shader_defs.push("DEPTH_CLAMP_ORTHO".into()); + } if layout.contains(Mesh::ATTRIBUTE_UV_0) { shader_defs.push("VERTEX_UVS".into()); @@ -244,9 +254,11 @@ where let vertex_buffer_layout = layout.get_layout(&vertex_attributes)?; - // The fragment shader is only used when the normal prepass is enabled or the material uses an alpha mask - let fragment = if key.mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS) - || key.mesh_key.contains(MeshPipelineKey::ALPHA_MASK) + // The fragment shader is only used when the normal prepass is enabled or the material uses alpha cutoff values + let fragment = if key + .mesh_key + .intersects(MeshPipelineKey::NORMAL_PREPASS | MeshPipelineKey::ALPHA_MASK) + || blend_key == MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA { // Use the fragment shader from the material if present let frag_shader_handle = if let Some(handle) = &self.material_fragment_shader { diff --git a/crates/bevy_pbr/src/prepass/prepass.wgsl b/crates/bevy_pbr/src/prepass/prepass.wgsl index d2050675f891a..6e81017dc090e 100644 --- a/crates/bevy_pbr/src/prepass/prepass.wgsl +++ b/crates/bevy_pbr/src/prepass/prepass.wgsl @@ -49,6 +49,9 @@ fn vertex(vertex: Vertex) -> VertexOutput { #endif // SKINNED out.clip_position = mesh_position_local_to_clip(model, vec4(vertex.position, 1.0)); +#ifdef DEPTH_CLAMP_ORTHO + out.clip_position.z = min(out.clip_position.z, 1.0); +#endif // DEPTH_CLAMP_ORTHO #ifdef VERTEX_UVS out.uv = vertex.uv; diff --git a/crates/bevy_pbr/src/render/depth.wgsl b/crates/bevy_pbr/src/render/depth.wgsl deleted file mode 100644 index e58a74f7230c7..0000000000000 --- a/crates/bevy_pbr/src/render/depth.wgsl +++ /dev/null @@ -1,46 +0,0 @@ -#import bevy_pbr::mesh_view_types -#import bevy_pbr::mesh_types - -@group(0) @binding(0) -var view: View; - -@group(1) @binding(0) -var mesh: Mesh; - -#ifdef SKINNED -@group(1) @binding(1) -var joint_matrices: SkinnedMesh; -#import bevy_pbr::skinning -#endif - -// NOTE: Bindings must come before functions that use them! -#import bevy_pbr::mesh_functions - -struct Vertex { - @location(0) position: vec3, -#ifdef SKINNED - @location(4) joint_indices: vec4, - @location(5) joint_weights: vec4, -#endif -}; - -struct VertexOutput { - @builtin(position) clip_position: vec4, -}; - -@vertex -fn vertex(vertex: Vertex) -> VertexOutput { -#ifdef SKINNED - let model = skin_model(vertex.joint_indices, vertex.joint_weights); -#else - let model = mesh.model; -#endif - - var out: VertexOutput; - out.clip_position = mesh_position_local_to_clip(model, vec4(vertex.position, 1.0)); -#ifdef DEPTH_CLAMP_ORTHO - out.clip_position.z = min(out.clip_position.z, 1.0); -#endif - - return out; -} diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 5131065614423..51cc941d5a84b 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -1,9 +1,10 @@ use crate::{ - directional_light_order, point_light_order, AmbientLight, Cascade, CascadeShadowConfig, - Cascades, CascadesVisibleEntities, Clusters, CubemapVisibleEntities, DirectionalLight, - DirectionalLightShadowMap, DrawMesh, EnvironmentMapLight, GlobalVisiblePointLights, - MeshPipeline, NotShadowCaster, PointLight, PointLightShadowMap, SetMeshBindGroup, SpotLight, - VisiblePointLights, SHADOW_SHADER_HANDLE, + directional_light_order, point_light_order, AlphaMode, AmbientLight, Cascade, + CascadeShadowConfig, Cascades, CascadesVisibleEntities, Clusters, CubemapVisibleEntities, + DirectionalLight, DirectionalLightShadowMap, DrawPrepass, EnvironmentMapLight, + GlobalVisiblePointLights, Material, MaterialPipelineKey, MeshPipeline, MeshPipelineKey, + NotShadowCaster, PointLight, PointLightShadowMap, PrepassPipeline, RenderMaterials, SpotLight, + VisiblePointLights, }; use bevy_asset::Handle; use bevy_core_pipeline::core_3d::Transparent3d; @@ -15,20 +16,17 @@ use bevy_math::{Mat4, UVec3, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles use bevy_render::{ camera::Camera, color::Color, - mesh::{Mesh, MeshVertexBufferLayout}, + mesh::Mesh, render_asset::RenderAssets, render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType}, render_phase::{ CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem, RenderCommand, - RenderCommandResult, RenderPhase, SetItemPipeline, TrackedRenderPass, + RenderCommandResult, RenderPhase, TrackedRenderPass, }, render_resource::*, renderer::{RenderContext, RenderDevice, RenderQueue}, texture::*, - view::{ - ComputedVisibility, ExtractedView, ViewUniform, ViewUniformOffset, ViewUniforms, - VisibleEntities, - }, + view::{ComputedVisibility, ExtractedView, ViewUniformOffset, ViewUniforms, VisibleEntities}, Extract, }; use bevy_transform::{components::GlobalTransform, prelude::Transform}; @@ -37,7 +35,10 @@ use bevy_utils::{ tracing::{error, warn}, HashMap, }; -use std::num::{NonZeroU32, NonZeroU64}; +use std::{ + hash::Hash, + num::{NonZeroU32, NonZeroU64}, +}; #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] pub enum RenderLightSystems { @@ -231,43 +232,17 @@ pub const MAX_CASCADES_PER_LIGHT: usize = 1; pub const SHADOW_FORMAT: TextureFormat = TextureFormat::Depth32Float; #[derive(Resource, Clone)] -pub struct ShadowPipeline { - pub view_layout: BindGroupLayout, - pub mesh_layout: BindGroupLayout, - pub skinned_mesh_layout: BindGroupLayout, +pub struct ShadowSamplers { pub point_light_sampler: Sampler, pub directional_light_sampler: Sampler, } // TODO: this pattern for initializing the shaders / pipeline isn't ideal. this should be handled by the asset system -impl FromWorld for ShadowPipeline { +impl FromWorld for ShadowSamplers { fn from_world(world: &mut World) -> Self { let render_device = world.resource::(); - let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { - entries: &[ - // View - BindGroupLayoutEntry { - binding: 0, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Buffer { - ty: BufferBindingType::Uniform, - has_dynamic_offset: true, - min_binding_size: Some(ViewUniform::min_size()), - }, - count: None, - }, - ], - label: Some("shadow_view_layout"), - }); - - let mesh_pipeline = world.resource::(); - let skinned_mesh_layout = mesh_pipeline.skinned_mesh_layout.clone(); - - ShadowPipeline { - view_layout, - mesh_layout: mesh_pipeline.mesh_layout.clone(), - skinned_mesh_layout, + ShadowSamplers { point_light_sampler: render_device.create_sampler(&SamplerDescriptor { address_mode_u: AddressMode::ClampToEdge, address_mode_v: AddressMode::ClampToEdge, @@ -292,120 +267,6 @@ impl FromWorld for ShadowPipeline { } } -bitflags::bitflags! { - #[repr(transparent)] - pub struct ShadowPipelineKey: u32 { - const NONE = 0; - const DEPTH_CLAMP_ORTHO = 1; - const PRIMITIVE_TOPOLOGY_RESERVED_BITS = ShadowPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS << ShadowPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS; - } -} - -impl ShadowPipelineKey { - const PRIMITIVE_TOPOLOGY_MASK_BITS: u32 = 0b111; - const PRIMITIVE_TOPOLOGY_SHIFT_BITS: u32 = 32 - 3; - - pub fn from_primitive_topology(primitive_topology: PrimitiveTopology) -> Self { - let primitive_topology_bits = ((primitive_topology as u32) - & Self::PRIMITIVE_TOPOLOGY_MASK_BITS) - << Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS; - Self::from_bits(primitive_topology_bits).unwrap() - } - - pub fn primitive_topology(&self) -> PrimitiveTopology { - let primitive_topology_bits = - (self.bits >> Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS) & Self::PRIMITIVE_TOPOLOGY_MASK_BITS; - match primitive_topology_bits { - x if x == PrimitiveTopology::PointList as u32 => PrimitiveTopology::PointList, - x if x == PrimitiveTopology::LineList as u32 => PrimitiveTopology::LineList, - x if x == PrimitiveTopology::LineStrip as u32 => PrimitiveTopology::LineStrip, - x if x == PrimitiveTopology::TriangleList as u32 => PrimitiveTopology::TriangleList, - x if x == PrimitiveTopology::TriangleStrip as u32 => PrimitiveTopology::TriangleStrip, - _ => PrimitiveTopology::default(), - } - } -} - -impl SpecializedMeshPipeline for ShadowPipeline { - type Key = ShadowPipelineKey; - - fn specialize( - &self, - key: Self::Key, - layout: &MeshVertexBufferLayout, - ) -> Result { - let mut vertex_attributes = vec![Mesh::ATTRIBUTE_POSITION.at_shader_location(0)]; - - let mut bind_group_layout = vec![self.view_layout.clone()]; - let mut shader_defs = Vec::new(); - shader_defs.push(ShaderDefVal::UInt( - "MAX_DIRECTIONAL_LIGHTS".to_string(), - MAX_DIRECTIONAL_LIGHTS as u32, - )); - shader_defs.push(ShaderDefVal::UInt( - "MAX_CASCADES_PER_LIGHT".to_string(), - MAX_CASCADES_PER_LIGHT as u32, - )); - - if key.contains(ShadowPipelineKey::DEPTH_CLAMP_ORTHO) { - // Avoid clipping shadow casters that are behind the near plane. - shader_defs.push("DEPTH_CLAMP_ORTHO".into()); - } - - if layout.contains(Mesh::ATTRIBUTE_JOINT_INDEX) - && layout.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT) - { - shader_defs.push("SKINNED".into()); - vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_INDEX.at_shader_location(4)); - vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_WEIGHT.at_shader_location(5)); - bind_group_layout.push(self.skinned_mesh_layout.clone()); - } else { - bind_group_layout.push(self.mesh_layout.clone()); - } - - let vertex_buffer_layout = layout.get_layout(&vertex_attributes)?; - - Ok(RenderPipelineDescriptor { - vertex: VertexState { - shader: SHADOW_SHADER_HANDLE.typed::(), - entry_point: "vertex".into(), - shader_defs, - buffers: vec![vertex_buffer_layout], - }, - fragment: None, - layout: bind_group_layout, - primitive: PrimitiveState { - topology: key.primitive_topology(), - strip_index_format: None, - front_face: FrontFace::Ccw, - cull_mode: None, - unclipped_depth: false, - polygon_mode: PolygonMode::Fill, - conservative: false, - }, - depth_stencil: Some(DepthStencilState { - format: SHADOW_FORMAT, - depth_write_enabled: true, - depth_compare: CompareFunction::GreaterEqual, - stencil: StencilState { - front: StencilFaceState::IGNORE, - back: StencilFaceState::IGNORE, - read_mask: 0, - write_mask: 0, - }, - bias: DepthBiasState { - constant: 0, - slope_scale: 0.0, - clamp: 0.0, - }, - }), - multisample: MultisampleState::default(), - label: Some("shadow_pipeline".into()), - push_constant_ranges: Vec::new(), - }) - } -} - #[derive(Component)] pub struct ExtractedClusterConfig { /// Special near value for cluster calculations @@ -1688,9 +1549,9 @@ pub fn prepare_clusters( } } -pub fn queue_shadow_view_bind_group( +pub fn queue_shadow_view_bind_group( render_device: Res, - shadow_pipeline: Res, + prepass_pipeline: Res>, mut light_meta: ResMut, view_uniforms: Res, ) { @@ -1702,27 +1563,30 @@ pub fn queue_shadow_view_bind_group( resource: view_binding, }], label: Some("shadow_view_bind_group"), - layout: &shadow_pipeline.view_layout, + layout: &prepass_pipeline.view_layout, })); } } #[allow(clippy::too_many_arguments)] -pub fn queue_shadows( +pub fn queue_shadows( shadow_draw_functions: Res>, - shadow_pipeline: Res, - casting_meshes: Query<&Handle, Without>, + prepass_pipeline: Res>, + casting_meshes: Query<(&Handle, &Handle), Without>, render_meshes: Res>, - mut pipelines: ResMut>, + render_materials: Res>, + mut pipelines: ResMut>>, pipeline_cache: Res, view_lights: Query<(Entity, &ViewLightEntities)>, mut view_light_shadow_phases: Query<(&LightEntity, &mut RenderPhase)>, point_light_entities: Query<&CubemapVisibleEntities, With>, directional_light_entities: Query<&CascadesVisibleEntities, With>, spot_light_entities: Query<&VisibleEntities, With>, -) { +) where + M::Data: PartialEq + Eq + Hash + Clone, +{ for (entity, view_lights) in &view_lights { - let draw_shadow_mesh = shadow_draw_functions.read().id::(); + let draw_shadow_mesh = shadow_draw_functions.read().id::>(); for view_light_entity in view_lights.lights.iter().copied() { let (light_entity, mut shadow_phase) = view_light_shadow_phases.get_mut(view_light_entity).unwrap(); @@ -1753,17 +1617,34 @@ pub fn queue_shadows( // NOTE: Lights with shadow mapping disabled will have no visible entities // so no meshes will be queued for entity in visible_entities.iter().copied() { - if let Ok(mesh_handle) = casting_meshes.get(entity) { - if let Some(mesh) = render_meshes.get(mesh_handle) { - let mut key = - ShadowPipelineKey::from_primitive_topology(mesh.primitive_topology); + if let Ok((mesh_handle, material_handle)) = casting_meshes.get(entity) { + if let (Some(mesh), Some(material)) = ( + render_meshes.get(mesh_handle), + render_materials.get(material_handle), + ) { + let mut mesh_key = + MeshPipelineKey::from_primitive_topology(mesh.primitive_topology) + | MeshPipelineKey::DEPTH_PREPASS; if is_directional_light { - key |= ShadowPipelineKey::DEPTH_CLAMP_ORTHO; + mesh_key |= MeshPipelineKey::DEPTH_CLAMP_ORTHO; + } + let alpha_mode = material.properties.alpha_mode; + match alpha_mode { + AlphaMode::Mask(_) => { + mesh_key |= MeshPipelineKey::ALPHA_MASK; + } + AlphaMode::Blend | AlphaMode::Premultiplied | AlphaMode::Add => { + mesh_key |= MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA; + } + _ => {} } let pipeline_id = pipelines.specialize( &pipeline_cache, - &shadow_pipeline, - key, + &prepass_pipeline, + MaterialPipelineKey { + mesh_key, + bind_group_data: material.key.clone(), + }, &mesh.layout, ); @@ -1892,13 +1773,6 @@ impl Node for ShadowPassNode { } } -pub type DrawShadowMesh = ( - SetItemPipeline, - SetShadowViewBindGroup<0>, - SetMeshBindGroup<1>, - DrawMesh, -); - pub struct SetShadowViewBindGroup; impl RenderCommand for SetShadowViewBindGroup { type Param = SRes; diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 119836ae582e6..60dba3f9a2ffe 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -1,9 +1,8 @@ use crate::{ - environment_map, prepass, queue_shadow_view_bind_group, EnvironmentMapLight, FogMeta, - GlobalLightMeta, GpuFog, GpuLights, GpuPointLights, LightMeta, NotShadowCaster, - NotShadowReceiver, ShadowPipeline, ViewClusterBindings, ViewFogUniformOffset, - ViewLightsUniformOffset, ViewShadowBindings, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, - MAX_CASCADES_PER_LIGHT, MAX_DIRECTIONAL_LIGHTS, + environment_map, prepass, EnvironmentMapLight, FogMeta, GlobalLightMeta, GpuFog, GpuLights, + GpuPointLights, LightMeta, NotShadowCaster, NotShadowReceiver, ShadowSamplers, + ViewClusterBindings, ViewFogUniformOffset, ViewLightsUniformOffset, ViewShadowBindings, + CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, MAX_CASCADES_PER_LIGHT, MAX_DIRECTIONAL_LIGHTS, }; use bevy_app::{IntoSystemAppConfigs, Plugin}; use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped}; @@ -111,11 +110,7 @@ impl Plugin for MeshRenderPlugin { .add_systems((extract_meshes, extract_skinned_meshes).in_schedule(ExtractSchedule)) .add_system(prepare_skinned_meshes.in_set(RenderSet::Prepare)) .add_system(queue_mesh_bind_group.in_set(RenderSet::Queue)) - .add_system( - queue_mesh_view_bind_groups - .in_set(RenderSet::Queue) - .ambiguous_with(queue_shadow_view_bind_group), // queue_mesh_view_bind_groups does not read `shadow_view_bind_group` - ); + .add_system(queue_mesh_view_bind_groups.in_set(RenderSet::Queue)); } } } @@ -579,6 +574,7 @@ bitflags::bitflags! { const NORMAL_PREPASS = (1 << 4); const ALPHA_MASK = (1 << 5); const ENVIRONMENT_MAP = (1 << 6); + const DEPTH_CLAMP_ORTHO = (1 << 7); const BLEND_RESERVED_BITS = Self::BLEND_MASK_BITS << Self::BLEND_SHIFT_BITS; // ← Bitmask reserving bits for the blend state const BLEND_OPAQUE = (0 << Self::BLEND_SHIFT_BITS); // ← Values are just sequential within the mask, and can range from 0 to 3 const BLEND_PREMULTIPLIED_ALPHA = (1 << Self::BLEND_SHIFT_BITS); // @@ -937,7 +933,7 @@ pub fn queue_mesh_view_bind_groups( mut commands: Commands, render_device: Res, mesh_pipeline: Res, - shadow_pipeline: Res, + shadow_samplers: Res, light_meta: Res, global_light_meta: Res, fog_meta: Res, @@ -1003,7 +999,7 @@ pub fn queue_mesh_view_bind_groups( }, BindGroupEntry { binding: 3, - resource: BindingResource::Sampler(&shadow_pipeline.point_light_sampler), + resource: BindingResource::Sampler(&shadow_samplers.point_light_sampler), }, BindGroupEntry { binding: 4, @@ -1013,7 +1009,7 @@ pub fn queue_mesh_view_bind_groups( }, BindGroupEntry { binding: 5, - resource: BindingResource::Sampler(&shadow_pipeline.directional_light_sampler), + resource: BindingResource::Sampler(&shadow_samplers.directional_light_sampler), }, BindGroupEntry { binding: 6, diff --git a/crates/bevy_pbr/src/render/pbr_prepass.wgsl b/crates/bevy_pbr/src/render/pbr_prepass.wgsl index 54584ef682666..e6efe84c34681 100644 --- a/crates/bevy_pbr/src/render/pbr_prepass.wgsl +++ b/crates/bevy_pbr/src/render/pbr_prepass.wgsl @@ -1,6 +1,8 @@ #import bevy_pbr::prepass_bindings #import bevy_pbr::pbr_bindings +#ifdef NORMAL_PREPASS #import bevy_pbr::pbr_functions +#endif // NORMAL_PREPASS struct FragmentInput { @builtin(front_facing) is_front: bool, @@ -16,9 +18,23 @@ struct FragmentInput { #endif // NORMAL_PREPASS }; +// Cutoff used for the premultiplied alpha modes BLEND and ADD. +const PREMULTIPLIED_ALPHA_CUTOFF = 0.05; + // We can use a simplified version of alpha_discard() here since we only need to handle the alpha_cutoff fn prepass_alpha_discard(in: FragmentInput) { -#ifdef ALPHA_MASK + +// This is a workaround since the preprocessor does not support +// #if defined(ALPHA_MASK) || defined(BLEND_PREMULTIPLIED_ALPHA) +#ifndef ALPHA_MASK +#ifndef BLEND_PREMULTIPLIED_ALPHA + +#define EMPTY_PREPASS_ALPHA_DISCARD + +#endif // BLEND_PREMULTIPLIED_ALPHA not defined +#endif // ALPHA_MASK not defined + +#ifndef EMPTY_PREPASS_ALPHA_DISCARD var output_color: vec4 = material.base_color; #ifdef VERTEX_UVS @@ -27,10 +43,24 @@ fn prepass_alpha_discard(in: FragmentInput) { } #endif // VERTEX_UVS +#ifdef ALPHA_MASK if ((material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK) != 0u) && output_color.a < material.alpha_cutoff { discard; } #endif // ALPHA_MASK + +#ifdef BLEND_PREMULTIPLIED_ALPHA + let alpha_mode = material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS; + if (alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND || alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD) + && output_color.a < PREMULTIPLIED_ALPHA_CUTOFF { + discard; + } else if alpha_mode == STANDARD_MATERIAL_FLAGS_ALPHA_MODE_PREMULTIPLIED + && all(output_color < vec4(PREMULTIPLIED_ALPHA_CUTOFF)) { + discard; + } +#endif // BLEND_PREMULTIPLIED_ALPHA + +#endif // EMPTY_PREPASS_ALPHA_DISCARD not defined } #ifdef NORMAL_PREPASS