mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Use prepass shaders for shadows (#7784)
# Objective - Fixes #4372. ## Solution - Use the prepass shaders for the shadow passes. - Move `DEPTH_CLAMP_ORTHO` from `ShadowPipelineKey` to `MeshPipelineKey` and the associated clamp operation from `depth.wgsl` to `prepass.wgsl`. - Remove `depth.wgsl` . - Replace `ShadowPipeline` with `ShadowSamplers`. Instead of running the custom `ShadowPipeline` we run the `PrepassPipeline` with the `DEPTH_PREPASS` flag and additionally the `DEPTH_CLAMP_ORTHO` flag for directional lights as well as the `ALPHA_MASK` flag for materials that use `AlphaMode::Mask(_)`.
This commit is contained in:
parent
a39c22386b
commit
e54103fd69
8 changed files with 125 additions and 260 deletions
|
@ -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::<Shadow>.in_set(RenderSet::PhaseSort))
|
||||
.init_resource::<ShadowPipeline>()
|
||||
.init_resource::<DrawFunctions<Shadow>>()
|
||||
.init_resource::<ShadowSamplers>()
|
||||
.init_resource::<LightMeta>()
|
||||
.init_resource::<GlobalLightMeta>()
|
||||
.init_resource::<SpecializedMeshPipelines<ShadowPipeline>>();
|
||||
.init_resource::<GlobalLightMeta>();
|
||||
|
||||
let shadow_pass_node = ShadowPassNode::new(&mut render_app.world);
|
||||
render_app.add_render_command::<Shadow, DrawShadowMesh>();
|
||||
let mut graph = render_app.world.resource_mut::<RenderGraph>();
|
||||
let draw_3d_graph = graph
|
||||
.get_sub_graph_mut(bevy_core_pipeline::core_3d::graph::NAME)
|
||||
|
|
|
@ -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::<DrawFunctions<Shadow>>()
|
||||
.add_render_command::<Shadow, DrawPrepass<M>>()
|
||||
.add_render_command::<Transparent3d, DrawMaterial<M>>()
|
||||
.add_render_command::<Opaque3d, DrawMaterial<M>>()
|
||||
.add_render_command::<AlphaMask3d, DrawMaterial<M>>()
|
||||
|
@ -201,6 +204,12 @@ where
|
|||
.in_set(RenderSet::Prepare)
|
||||
.after(PrepareAssetSet::PreAssetPrepare),
|
||||
)
|
||||
.add_system(render::queue_shadows::<M>.in_set(RenderLightSystems::QueueShadows))
|
||||
.add_system(
|
||||
render::queue_shadow_view_bind_group::<M>
|
||||
.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::<M>.in_set(RenderSet::Queue));
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
#import bevy_pbr::mesh_view_types
|
||||
#import bevy_pbr::mesh_types
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> view: View;
|
||||
|
||||
@group(1) @binding(0)
|
||||
var<uniform> mesh: Mesh;
|
||||
|
||||
#ifdef SKINNED
|
||||
@group(1) @binding(1)
|
||||
var<uniform> 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<f32>,
|
||||
#ifdef SKINNED
|
||||
@location(4) joint_indices: vec4<u32>,
|
||||
@location(5) joint_weights: vec4<f32>,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) clip_position: vec4<f32>,
|
||||
};
|
||||
|
||||
@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<f32>(vertex.position, 1.0));
|
||||
#ifdef DEPTH_CLAMP_ORTHO
|
||||
out.clip_position.z = min(out.clip_position.z, 1.0);
|
||||
#endif
|
||||
|
||||
return out;
|
||||
}
|
|
@ -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::<RenderDevice>();
|
||||
|
||||
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::<MeshPipeline>();
|
||||
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<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
||||
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::<Shader>(),
|
||||
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<M: Material>(
|
||||
render_device: Res<RenderDevice>,
|
||||
shadow_pipeline: Res<ShadowPipeline>,
|
||||
prepass_pipeline: Res<PrepassPipeline<M>>,
|
||||
mut light_meta: ResMut<LightMeta>,
|
||||
view_uniforms: Res<ViewUniforms>,
|
||||
) {
|
||||
|
@ -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<M: Material>(
|
||||
shadow_draw_functions: Res<DrawFunctions<Shadow>>,
|
||||
shadow_pipeline: Res<ShadowPipeline>,
|
||||
casting_meshes: Query<&Handle<Mesh>, Without<NotShadowCaster>>,
|
||||
prepass_pipeline: Res<PrepassPipeline<M>>,
|
||||
casting_meshes: Query<(&Handle<Mesh>, &Handle<M>), Without<NotShadowCaster>>,
|
||||
render_meshes: Res<RenderAssets<Mesh>>,
|
||||
mut pipelines: ResMut<SpecializedMeshPipelines<ShadowPipeline>>,
|
||||
render_materials: Res<RenderMaterials<M>>,
|
||||
mut pipelines: ResMut<SpecializedMeshPipelines<PrepassPipeline<M>>>,
|
||||
pipeline_cache: Res<PipelineCache>,
|
||||
view_lights: Query<(Entity, &ViewLightEntities)>,
|
||||
mut view_light_shadow_phases: Query<(&LightEntity, &mut RenderPhase<Shadow>)>,
|
||||
point_light_entities: Query<&CubemapVisibleEntities, With<ExtractedPointLight>>,
|
||||
directional_light_entities: Query<&CascadesVisibleEntities, With<ExtractedDirectionalLight>>,
|
||||
spot_light_entities: Query<&VisibleEntities, With<ExtractedPointLight>>,
|
||||
) {
|
||||
) where
|
||||
M::Data: PartialEq + Eq + Hash + Clone,
|
||||
{
|
||||
for (entity, view_lights) in &view_lights {
|
||||
let draw_shadow_mesh = shadow_draw_functions.read().id::<DrawShadowMesh>();
|
||||
let draw_shadow_mesh = shadow_draw_functions.read().id::<DrawPrepass<M>>();
|
||||
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<const I: usize>;
|
||||
impl<const I: usize> RenderCommand<Shadow> for SetShadowViewBindGroup<I> {
|
||||
type Param = SRes<LightMeta>;
|
||||
|
|
|
@ -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<RenderDevice>,
|
||||
mesh_pipeline: Res<MeshPipeline>,
|
||||
shadow_pipeline: Res<ShadowPipeline>,
|
||||
shadow_samplers: Res<ShadowSamplers>,
|
||||
light_meta: Res<LightMeta>,
|
||||
global_light_meta: Res<GlobalLightMeta>,
|
||||
fog_meta: Res<FogMeta>,
|
||||
|
@ -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,
|
||||
|
|
|
@ -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<f32> = 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
|
||||
|
|
Loading…
Reference in a new issue