diff --git a/Cargo.toml b/Cargo.toml index 6612b1630986e..90c9c6864f490 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1176,6 +1176,17 @@ description = "A shader and a material that uses it" category = "Shaders" wasm = true +[[example]] +name = "shader_prepass" +path = "examples/shader/shader_prepass.rs" + +[package.metadata.example.shader_prepass] +name = "Material Prepass" +description = "A shader that uses the depth texture generated in a prepass" +category = "Shaders" +wasm = true + + [[example]] name = "shader_material_screenspace_texture" path = "examples/shader/shader_material_screenspace_texture.rs" diff --git a/assets/shaders/custom_material.wgsl b/assets/shaders/custom_material.wgsl index 65dd816b0e29e..95b1b7d26a196 100644 --- a/assets/shaders/custom_material.wgsl +++ b/assets/shaders/custom_material.wgsl @@ -1,5 +1,5 @@ struct CustomMaterial { - color: vec3, + color: vec4, }; @group(1) @binding(0) @@ -13,5 +13,5 @@ var base_color_sampler: sampler; fn fragment( #import bevy_pbr::mesh_vertex_output ) -> @location(0) vec4 { - return vec4(material.color, 1.0) * textureSample(base_color_texture, base_color_sampler, uv); + return material.color * textureSample(base_color_texture, base_color_sampler, uv); } diff --git a/assets/shaders/red.wgsl b/assets/shaders/red.wgsl deleted file mode 100644 index 6c4a206d69820..0000000000000 --- a/assets/shaders/red.wgsl +++ /dev/null @@ -1,99 +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 OUTPUT_NORMALS - @location(1) normal: vec3, -#ifdef VERTEX_UVS - @location(2) uv: vec2, -#endif // VERTEX_UVS -#ifdef VERTEX_TANGENTS - @location(3) tangent: vec4, -#endif // VERTEX_TANGENTS -#endif // OUTPUT_NORMALS - -#ifdef SKINNED - @location(4) joint_indices: vec4, - @location(5) joint_weights: vec4, -#endif // SKINNED -} - -struct VertexOutput { - @builtin(position) clip_position: vec4, - -#ifdef OUTPUT_NORMALS - @location(0) world_normal: vec3, -#ifdef VERTEX_UVS - @location(1) uv: vec2, -#endif // VERTEX_UVS -#ifdef VERTEX_TANGENTS - @location(2) world_tangent: vec4, -#endif // VERTEX_TANGENTS -#endif // OUTPUT_NORMALS -} - -@vertex -fn vertex(vertex: Vertex) -> VertexOutput { - var out: VertexOutput; - -#ifdef SKINNED - var model = skin_model(vertex.joint_indices, vertex.joint_weights); -#else // SKINNED - var model = mesh.model; -#endif // SKINNED - - out.clip_position = mesh_position_local_to_clip(model, vec4(vertex.position, 1.0)); - -#ifdef OUTPUT_NORMALS -#ifdef SKINNED - out.world_normal = skin_normals(model, vertex.normal); -#else // SKINNED - out.world_normal = mesh_normal_local_to_world(vertex.normal); -#endif // SKINNED - -#ifdef VERTEX_UVS - out.uv = vertex.uv; -#endif // VERTEX_UVS - -#ifdef VERTEX_TANGENTS - out.world_tangent = mesh_tangent_local_to_world(model, vertex.tangent); -#endif // VERTEX_TANGENTS -#endif // OUTPUT_NORMALS - - return out; -} - -#ifdef OUTPUT_NORMALS -struct FragmentInput { - @builtin(front_facing) is_front: bool, - @location(0) world_normal: vec3, -#ifdef VERTEX_UVS - @location(1) uv: vec2, -#endif -#ifdef VERTEX_TANGENTS - @location(2) world_tangent: vec4, -#endif -} - -@fragment -fn fragment(in: FragmentInput) -> @location(0) vec4 { - return vec4(1.0, 0.0, 0.0, 1.0); -} -#endif // OUTPUT_NORMALS diff --git a/assets/shaders/show_depth.wgsl b/assets/shaders/show_depth.wgsl index 95a7cecf54941..e7f4190e3dc7a 100644 --- a/assets/shaders/show_depth.wgsl +++ b/assets/shaders/show_depth.wgsl @@ -8,11 +8,6 @@ fn fragment( @builtin(sample_index) sample_index: u32, #import bevy_pbr::mesh_vertex_output ) -> @location(0) vec4 { - if frag_coord.y < 300.0 { - let depth = prepass_normal(frag_coord, sample_index); - return vec4(depth, 1.0); - } else { - let depth = prepass_depth(frag_coord, sample_index); - return vec4(depth, depth, depth, 1.0); - } + let depth = prepass_depth(frag_coord, sample_index); + return vec4(depth, depth, depth, 1.0); } diff --git a/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs index 04d7c8ec9eb5c..f2508f16e1b2d 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs @@ -20,7 +20,7 @@ use bevy_utils::tracing::info_span; pub struct PrepassSettings { /// If true then depth values will be copied to a separate texture available to the main pass. pub output_depth: bool, - /// If true then vertex world normals will be copied to a separate texture available to the main pass + /// If true then vertex world normals will be copied to a separate texture available to the main pass. pub output_normals: bool, } diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 4a2fa66ec4714..a32922df1c8c9 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -67,6 +67,8 @@ pub const SHADOWS_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 11350275143789590502); pub const PBR_SHADER_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 4805239651767701046); +pub const PBR_PREPASS_SHADER_HANDLE: HandleUntyped = + HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 9407115064344201137); pub const PBR_FUNCTIONS_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 16550102964439850292); pub const SHADOW_SHADER_HANDLE: HandleUntyped = @@ -122,6 +124,12 @@ impl Plugin for PbrPlugin { "render/depth.wgsl", Shader::from_wgsl ); + load_internal_asset!( + app, + PBR_PREPASS_SHADER_HANDLE, + "render/pbr_prepass.wgsl", + Shader::from_wgsl + ); app.register_type::() .register_type::() diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 8f2862f8d6fb5..af231d43c99ef 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -148,6 +148,12 @@ pub trait Material: AsBindGroup + Send + Sync + Clone + TypeUuid + Sized + 'stat ShaderRef::Default } + /// Controls if this Material is used in the prepass + /// You also need to enable the prepass globally using `GlobalMaterialOptions` for this to work + fn prepass_enabled() -> bool { + true + } + /// Customizes the default [`RenderPipelineDescriptor`] for a specific entity using the entity's /// [`MaterialPipelineKey`] and [`MeshVertexBufferLayout`] as input. #[allow(unused_variables)] @@ -162,6 +168,14 @@ pub trait Material: AsBindGroup + Send + Sync + Clone + TypeUuid + Sized + 'stat } } +/// Options that affects all materials +#[derive(Resource, Default)] +pub struct GlobalMaterialOptions { + // Controls if the prepass will be used when rendering + // Disabled by default + pub prepass_enabled: bool, +} + /// Adds the necessary ECS resources and render logic to enable rendering entities using the given [`Material`] /// asset type. pub struct MaterialPlugin(PhantomData); @@ -179,7 +193,8 @@ where fn build(&self, app: &mut App) { app.add_asset::() .add_plugin(ExtractComponentPlugin::>::extract_visible()) - .add_plugin(PrepassPlugin::::default()); + .init_resource::(); + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { render_app .add_render_command::>() @@ -196,6 +211,11 @@ where ) .add_system_to_stage(RenderStage::Queue, queue_material_meshes::); } + + let options = app.world.resource::(); + if options.prepass_enabled && M::prepass_enabled() { + app.add_plugin(PrepassPlugin::::default()); + } } } @@ -239,7 +259,7 @@ where } /// Render pipeline data for a given [`Material`]. -#[derive(Resource)] +#[derive(Resource, Clone)] pub struct MaterialPipeline { pub mesh_pipeline: MeshPipeline, pub material_layout: BindGroupLayout, diff --git a/crates/bevy_pbr/src/pbr_material.rs b/crates/bevy_pbr/src/pbr_material.rs index db61a1fe70e38..8e388b544ba12 100644 --- a/crates/bevy_pbr/src/pbr_material.rs +++ b/crates/bevy_pbr/src/pbr_material.rs @@ -1,4 +1,7 @@ -use crate::{AlphaMode, Material, MaterialPipeline, MaterialPipelineKey, PBR_SHADER_HANDLE}; +use crate::{ + AlphaMode, Material, MaterialPipeline, MaterialPipelineKey, PBR_PREPASS_SHADER_HANDLE, + PBR_SHADER_HANDLE, +}; use bevy_asset::Handle; use bevy_math::Vec4; use bevy_reflect::TypeUuid; @@ -425,6 +428,10 @@ impl Material for StandardMaterial { Ok(()) } + fn prepass_fragment_shader() -> ShaderRef { + PBR_PREPASS_SHADER_HANDLE.typed().into() + } + fn fragment_shader() -> ShaderRef { PBR_SHADER_HANDLE.typed().into() } @@ -438,4 +445,8 @@ impl Material for StandardMaterial { fn depth_bias(&self) -> f32 { self.depth_bias } + + fn prepass_enabled() -> bool { + true + } } diff --git a/crates/bevy_pbr/src/render/pbr.wgsl b/crates/bevy_pbr/src/render/pbr.wgsl index f7729970c6d52..cf71d1829a7a6 100644 --- a/crates/bevy_pbr/src/render/pbr.wgsl +++ b/crates/bevy_pbr/src/render/pbr.wgsl @@ -11,7 +11,6 @@ struct FragmentInput { @builtin(front_facing) is_front: bool, @builtin(position) frag_coord: vec4, - @builtin(sample_index) sample_index: u32, #import bevy_pbr::mesh_vertex_output }; @@ -77,7 +76,6 @@ fn fragment(in: FragmentInput) -> @location(0) vec4 { pbr_input.N = prepare_normal( material.flags, in.world_normal, - // prepass_normal(in.frag_coord, in.sample_index), #ifdef VERTEX_TANGENTS #ifdef STANDARDMATERIAL_NORMAL_MAP in.world_tangent, diff --git a/crates/bevy_pbr/src/render/pbr_prepass.wgsl b/crates/bevy_pbr/src/render/pbr_prepass.wgsl new file mode 100644 index 0000000000000..2e7a4a5ebabdd --- /dev/null +++ b/crates/bevy_pbr/src/render/pbr_prepass.wgsl @@ -0,0 +1,60 @@ +// FIXME: These imports are wrong, but they make it possible to import pbr functions without copying any code + +#import bevy_pbr::mesh_view_bindings +#import bevy_pbr::mesh_bindings +// #import bevy_pbr::prepass_bindings + +#import bevy_pbr::pbr_bindings +#import bevy_pbr::utils +#import bevy_pbr::shadows +#import bevy_pbr::clustered_forward +#import bevy_pbr::lighting +#import bevy_pbr::pbr_functions + +struct FragmentInput { + @builtin(front_facing) is_front: bool, + @builtin(position) frag_coord: vec4, +#ifdef OUTPUT_NORMALS + @location(0) world_normal: vec3, +#ifdef VERTEX_UVS + @location(1) uv: vec2, +#endif // VERTEX_UVS +#ifdef VERTEX_TANGENTS + @location(2) world_tangent: vec4, +#endif // VERTEX_TANGENTS +#endif // OUTPUT_NORMALS +}; + +@fragment +fn fragment(in: FragmentInput) -> @location(0) vec4 { + var output_color: vec4 = material.base_color; + +#ifdef VERTEX_UVS + if ((material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) { + output_color = output_color * textureSample(base_color_texture, base_color_sampler, in.uv); + } +#endif + + // NOTE: Unlit bit not set means == 0 is true, so the true case is if lit + if ((material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) { + let normal = prepare_normal( + material.flags, + in.world_normal, +#ifdef VERTEX_TANGENTS +#ifdef STANDARDMATERIAL_NORMAL_MAP + in.world_tangent, +#endif +#endif +#ifdef VERTEX_UVS + in.uv, +#endif + in.is_front, + ); + + alpha_discard(material, output_color); + return vec4(normal * 0.5 + vec3(0.5), 1.0); + } else { + alpha_discard(material, output_color); + return vec4(in.world_normal * 0.5 + vec3(0.5), 1.0); + } +} diff --git a/crates/bevy_pbr/src/render/prepass.rs b/crates/bevy_pbr/src/render/prepass.rs index 1e5f4bf4de0a9..d58268638df06 100644 --- a/crates/bevy_pbr/src/render/prepass.rs +++ b/crates/bevy_pbr/src/render/prepass.rs @@ -41,14 +41,11 @@ use bevy_render::{ }, Extract, RenderApp, RenderStage, }; -use bevy_utils::{ - tracing::{error, warn}, - FloatOrd, HashMap, -}; +use bevy_utils::{tracing::error, FloatOrd, HashMap}; use crate::{ - AlphaMode, DrawMesh, Material, MeshPipeline, MeshPipelineKey, MeshUniform, RenderMaterials, - SetMeshBindGroup, + AlphaMode, DrawMesh, Material, MaterialPipeline, MaterialPipelineKey, MeshPipeline, + MeshPipelineKey, MeshUniform, RenderMaterials, SetMaterialBindGroup, SetMeshBindGroup, }; use std::{hash::Hash, marker::PhantomData}; @@ -62,7 +59,10 @@ pub mod draw_3d_graph { pub const PREPASS_FORMAT: TextureFormat = TextureFormat::Depth32Float; pub const PREPASS_SHADER_HANDLE: HandleUntyped = - HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 17179930919397780179); + HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 921124473254008983); + +pub const PREPASS_BINDINGS_SHADER_HANDLE: HandleUntyped = + HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 5533152893177403494); pub struct PrepassPlugin(PhantomData); @@ -84,6 +84,13 @@ where Shader::from_wgsl ); + load_internal_asset!( + app, + PREPASS_BINDINGS_SHADER_HANDLE, + "prepass_bindings.wgsl", + Shader::from_wgsl + ); + let render_app = match app.get_sub_app_mut(RenderApp) { Ok(render_app) => render_app, Err(_) => return, @@ -107,8 +114,8 @@ where let prepass_node = PrepassNode::new(&mut render_app.world); render_app - .add_render_command::() - .add_render_command::(); + .add_render_command::>() + .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) @@ -139,6 +146,7 @@ pub struct PrepassPipeline { pub material_layout: BindGroupLayout, pub material_vertex_shader: Option>, pub material_fragment_shader: Option>, + pub material_pipeline: MaterialPipeline, _marker: PhantomData, } @@ -167,7 +175,7 @@ impl FromWorld for PrepassPipeline { let mesh_pipeline = world.resource::(); let skinned_mesh_layout = mesh_pipeline.skinned_mesh_layout.clone(); - Self { + PrepassPipeline { view_layout, mesh_layout: mesh_pipeline.mesh_layout.clone(), skinned_mesh_layout, @@ -182,13 +190,17 @@ impl FromWorld for PrepassPipeline { ShaderRef::Path(path) => Some(asset_server.load(path)), }, material_layout: M::bind_group_layout(render_device), + material_pipeline: world.resource::>().clone(), _marker: PhantomData, } } } -impl SpecializedMeshPipeline for PrepassPipeline { - type Key = MeshPipelineKey; +impl SpecializedMeshPipeline for PrepassPipeline +where + M::Data: PartialEq + Eq + Hash + Clone, +{ + type Key = MaterialPipelineKey; fn specialize( &self, @@ -197,18 +209,25 @@ impl SpecializedMeshPipeline for PrepassPipeline { ) -> Result { let mut bind_group_layout = vec![self.view_layout.clone()]; let mut shader_defs = Vec::new(); + shader_defs.push(String::from("PREPASS_DEPTH")); + + // FIXME figure out a way to only add it when necessary + // right now the issue is that the group is hardcoded to 1 in the pbr_bindings + // but when it's not present then the mesh bind group is now group 1 and everything breaks + // if self.material_fragment_shader.is_some() || self.material_vertex_shader.is_some() { + bind_group_layout.insert(1, self.material_layout.clone()); + // } - if key.contains(MeshPipelineKey::ALPHA_MASK) { + if key.mesh_key.contains(MeshPipelineKey::ALPHA_MASK) { shader_defs.push(String::from("ALPHA_MASK")); - // FIXME: This needs to be implemented per-material! - bind_group_layout.push(self.material_layout.clone()); } let mut vertex_attributes = vec![Mesh::ATTRIBUTE_POSITION.at_shader_location(0)]; - if key.contains(MeshPipelineKey::PREPASS_NORMALS) { + if key.mesh_key.contains(MeshPipelineKey::PREPASS_NORMALS) { vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(1)); shader_defs.push(String::from("OUTPUT_NORMALS")); + shader_defs.push(String::from("PREPASS_NORMALS")); } if layout.contains(Mesh::ATTRIBUTE_UV_0) { @@ -227,20 +246,19 @@ impl SpecializedMeshPipeline for PrepassPipeline { shader_defs.push(String::from("SKINNED")); 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()); + bind_group_layout.insert(2, self.skinned_mesh_layout.clone()); } else { - bind_group_layout.push(self.mesh_layout.clone()); + bind_group_layout.insert(2, self.mesh_layout.clone()); } let vertex_buffer_layout = layout.get_layout(&vertex_attributes)?; - let fragment = if key.contains(MeshPipelineKey::PREPASS_NORMALS) - || key.contains(MeshPipelineKey::ALPHA_MASK) + let fragment = if key.mesh_key.contains(MeshPipelineKey::PREPASS_NORMALS) + || key.mesh_key.contains(MeshPipelineKey::ALPHA_MASK) { let frag_shader_handle = if let Some(handle) = &self.material_fragment_shader { handle.clone() } else { - warn!("Missing Material::prepass_fragment_shader() for material with UUID {}. Rendering may be incorrect.", M::TYPE_UUID); PREPASS_SHADER_HANDLE.typed::() }; @@ -261,11 +279,10 @@ impl SpecializedMeshPipeline for PrepassPipeline { let vert_shader_handle = if let Some(handle) = &self.material_vertex_shader { handle.clone() } else { - warn!("Missing Material::prepass_vertex_shader() for material with UUID {}. Rendering may be incorrect.", M::TYPE_UUID); PREPASS_SHADER_HANDLE.typed::() }; - Ok(RenderPipelineDescriptor { + let mut descriptor = RenderPipelineDescriptor { vertex: VertexState { shader: vert_shader_handle, entry_point: "vertex".into(), @@ -275,17 +292,15 @@ impl SpecializedMeshPipeline for PrepassPipeline { fragment, layout: Some(bind_group_layout), primitive: PrimitiveState { - topology: key.primitive_topology(), + topology: key.mesh_key.primitive_topology(), strip_index_format: None, front_face: FrontFace::Ccw, - // FIXME: Should use from material... but that would need specialization cull_mode: None, unclipped_depth: false, polygon_mode: PolygonMode::Fill, conservative: false, }, depth_stencil: Some(DepthStencilState { - // FIXME: Same as main pass format: PREPASS_FORMAT, depth_write_enabled: true, depth_compare: CompareFunction::GreaterEqual, @@ -302,12 +317,16 @@ impl SpecializedMeshPipeline for PrepassPipeline { }, }), multisample: MultisampleState { - count: key.msaa_samples(), + count: key.mesh_key.msaa_samples(), mask: !0, alpha_to_coverage_enabled: false, }, label: Some("prepass_pipeline".into()), - }) + }; + + M::specialize(&self.material_pipeline, &mut descriptor, layout, key)?; + + Ok(descriptor) } } @@ -459,11 +478,11 @@ pub fn queue_prepass_material_meshes( { let opaque_draw_prepass = opaque_draw_functions .read() - .get_id::() + .get_id::>() .unwrap(); let alpha_mask_draw_prepass = alpha_mask_draw_functions .read() - .get_id::() + .get_id::>() .unwrap(); for (view, visible_entities, prepass_settings, mut opaque_phase, mut alpha_mask_phase) in &mut views @@ -495,7 +514,10 @@ pub fn queue_prepass_material_meshes( let pipeline_id = pipelines.specialize( &mut pipeline_cache, &prepass_pipeline, - key, + MaterialPipelineKey { + mesh_key: key, + bind_group_data: material.key.clone(), + }, &mesh.layout, ); let pipeline_id = match pipeline_id { @@ -725,11 +747,11 @@ impl Node for PrepassNode { } } - if let Some(view_depth_texture_resource) = &view_prepass_textures.depth { + if let Some(prepass_depth_texture) = &view_prepass_textures.depth { // copy depth buffer to texture render_context.command_encoder.copy_texture_to_texture( view_depth_texture.texture.as_image_copy(), - view_depth_texture_resource.texture.as_image_copy(), + prepass_depth_texture.texture.as_image_copy(), view_prepass_textures.size, ); } @@ -739,10 +761,11 @@ impl Node for PrepassNode { } } -pub type DrawPrepass = ( +pub type DrawPrepass = ( SetItemPipeline, SetDepthViewBindGroup<0>, - SetMeshBindGroup<1>, + SetMaterialBindGroup, + SetMeshBindGroup<2>, DrawMesh, ); diff --git a/crates/bevy_pbr/src/render/prepass.wgsl b/crates/bevy_pbr/src/render/prepass.wgsl index 40ff86ff27fe2..a0074643a69aa 100644 --- a/crates/bevy_pbr/src/render/prepass.wgsl +++ b/crates/bevy_pbr/src/render/prepass.wgsl @@ -1,19 +1,4 @@ -#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::prepass_bindings #import bevy_pbr::mesh_functions struct Vertex { @@ -37,7 +22,6 @@ struct Vertex { struct VertexOutput { @builtin(position) clip_position: vec4, - #ifdef OUTPUT_NORMALS @location(0) world_normal: vec3, #ifdef VERTEX_UVS diff --git a/crates/bevy_pbr/src/render/prepass_bindings.wgsl b/crates/bevy_pbr/src/render/prepass_bindings.wgsl new file mode 100644 index 0000000000000..cd338af0ed7da --- /dev/null +++ b/crates/bevy_pbr/src/render/prepass_bindings.wgsl @@ -0,0 +1,18 @@ +#define_import_path bevy_pbr::prepass_bindings + +#import bevy_pbr::mesh_view_types +#import bevy_pbr::mesh_types + +@group(0) @binding(0) +var view: View; + +// Material bindings will be in @group(1) + +@group(2) @binding(0) +var mesh: Mesh; + +#ifdef SKINNED +@group(2) @binding(1) +var joint_matrices: SkinnedMesh; +#import bevy_pbr::skinning +#endif diff --git a/crates/bevy_pbr/src/render/utils.wgsl b/crates/bevy_pbr/src/render/utils.wgsl index 1cdb9926c3722..1065e78ce10e7 100644 --- a/crates/bevy_pbr/src/render/utils.wgsl +++ b/crates/bevy_pbr/src/render/utils.wgsl @@ -15,7 +15,7 @@ fn hsv2rgb(hue: f32, saturation: f32, value: f32) -> vec3 { vec3(1.0) ); - return value * mix( vec3(1.0), rgb, vec3(saturation)); + return value * mix(vec3(1.0), rgb, vec3(saturation)); } fn random1D(s: f32) -> f32 { @@ -30,6 +30,7 @@ fn coords_to_viewport_uv(position: vec2, viewport: vec4) -> vec2 return (position - viewport.xy) / viewport.zw; } +#ifndef PREPASS_NORMALS fn prepass_normal(frag_coord: vec4, sample_index: u32) -> vec3 { #ifdef MULTISAMPLED let normal_sample: vec4 = textureLoad(normal_prepass_texture, vec2(frag_coord.xy), i32(sample_index)); @@ -38,7 +39,9 @@ fn prepass_normal(frag_coord: vec4, sample_index: u32) -> vec3 { #endif return normal_sample.xyz * 2.0 - vec3(1.0); } +#endif +#ifndef PREPASS_DEPTH fn prepass_depth(frag_coord: vec4, sample_index: u32) -> f32 { #ifdef MULTISAMPLED let depth_sample: f32 = textureLoad(depth_prepass_texture, vec2(frag_coord.xy), i32(sample_index)); @@ -47,3 +50,4 @@ fn prepass_depth(frag_coord: vec4, sample_index: u32) -> f32 { #endif return depth_sample; } +#endif \ No newline at end of file diff --git a/crates/bevy_render/macros/src/as_bind_group.rs b/crates/bevy_render/macros/src/as_bind_group.rs index da0ce28c8f8d6..30888a5227055 100644 --- a/crates/bevy_render/macros/src/as_bind_group.rs +++ b/crates/bevy_render/macros/src/as_bind_group.rs @@ -275,53 +275,53 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { resource: bindings[#binding_vec_index].get_binding(), } }); - // // single field uniform bindings for a given index can use a straightforward binding - // if uniform_fields.len() == 1 { - // let field = &uniform_fields[0]; - // let field_name = field.ident.as_ref().unwrap(); - // let field_ty = &field.ty; - // binding_impls.push(quote! {{ - // let mut buffer = #render_path::render_resource::encase::UniformBuffer::new(Vec::new()); - // buffer.write(&self.#field_name).unwrap(); - // #render_path::render_resource::OwnedBindingResource::Buffer(render_device.create_buffer_with_data( - // &#render_path::render_resource::BufferInitDescriptor { - // label: None, - // usage: #render_path::render_resource::BufferUsages::COPY_DST | #render_path::render_resource::BufferUsages::UNIFORM, - // contents: buffer.as_ref(), - // }, - // )) - // }}); - - // binding_layouts.push(quote!{ - // #render_path::render_resource::BindGroupLayoutEntry { - // binding: #binding_index, - // visibility: #render_path::render_resource::ShaderStages::all(), - // ty: #render_path::render_resource::BindingType::Buffer { - // ty: #render_path::render_resource::BufferBindingType::Uniform, - // has_dynamic_offset: false, - // min_binding_size: Some(<#field_ty as #render_path::render_resource::ShaderType>::min_size()), - // }, - // count: None, - // } - // }); - // // multi-field uniform bindings for a given index require an intermediate struct to derive ShaderType - // } else { - let uniform_struct_name = Ident::new( - &format!("_{struct_name}AsBindGroupUniformStructBindGroup{binding_index}"), - Span::call_site(), - ); - - let field_name = uniform_fields.iter().map(|f| f.ident.as_ref().unwrap()); - let field_type = uniform_fields.iter().map(|f| &f.ty); - field_struct_impls.push(quote! { - #[derive(#render_path::render_resource::ShaderType)] - struct #uniform_struct_name<'a> { - #(#field_name: &'a #field_type,)* - } - }); + // single field uniform bindings for a given index can use a straightforward binding + if uniform_fields.len() == 1 { + let field = &uniform_fields[0]; + let field_name = field.ident.as_ref().unwrap(); + let field_ty = &field.ty; + binding_impls.push(quote! {{ + let mut buffer = #render_path::render_resource::encase::UniformBuffer::new(Vec::new()); + buffer.write(&self.#field_name).unwrap(); + #render_path::render_resource::OwnedBindingResource::Buffer(render_device.create_buffer_with_data( + &#render_path::render_resource::BufferInitDescriptor { + label: None, + usage: #render_path::render_resource::BufferUsages::COPY_DST | #render_path::render_resource::BufferUsages::UNIFORM, + contents: buffer.as_ref(), + }, + )) + }}); + + binding_layouts.push(quote!{ + #render_path::render_resource::BindGroupLayoutEntry { + binding: #binding_index, + visibility: #render_path::render_resource::ShaderStages::all(), + ty: #render_path::render_resource::BindingType::Buffer { + ty: #render_path::render_resource::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: Some(<#field_ty as #render_path::render_resource::ShaderType>::min_size()), + }, + count: None, + } + }); + // multi-field uniform bindings for a given index require an intermediate struct to derive ShaderType + } else { + let uniform_struct_name = Ident::new( + &format!("_{struct_name}AsBindGroupUniformStructBindGroup{binding_index}"), + Span::call_site(), + ); + + let field_name = uniform_fields.iter().map(|f| f.ident.as_ref().unwrap()); + let field_type = uniform_fields.iter().map(|f| &f.ty); + field_struct_impls.push(quote! { + #[derive(#render_path::render_resource::ShaderType)] + struct #uniform_struct_name<'a> { + #(#field_name: &'a #field_type,)* + } + }); - let field_name = uniform_fields.iter().map(|f| f.ident.as_ref().unwrap()); - binding_impls.push(quote! {{ + let field_name = uniform_fields.iter().map(|f| f.ident.as_ref().unwrap()); + binding_impls.push(quote! {{ let mut buffer = #render_path::render_resource::encase::UniformBuffer::new(Vec::new()); buffer.write(&#uniform_struct_name { #(#field_name: &self.#field_name,)* @@ -335,7 +335,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { )) }}); - binding_layouts.push(quote!{ + binding_layouts.push(quote!{ #render_path::render_resource::BindGroupLayoutEntry { binding: #binding_index, visibility: #render_path::render_resource::ShaderStages::all(), @@ -347,7 +347,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { count: None, } }); - // } + } } } diff --git a/examples/3d/3d_scene.rs b/examples/3d/3d_scene.rs index f107c8d8c765c..b2db6e3a43c30 100644 --- a/examples/3d/3d_scene.rs +++ b/examples/3d/3d_scene.rs @@ -1,90 +1,30 @@ //! A simple 3D scene with light shining over a cube sitting on a plane. -use bevy::{ - prelude::*, - reflect::TypeUuid, - render::render_resource::{AsBindGroup, ShaderRef}, -}; +use bevy::prelude::*; fn main() { App::new() - .insert_resource(Msaa { samples: 1 }) .add_plugins(DefaultPlugins) - .add_plugin(MaterialPlugin::::default()) .add_startup_system(setup) - .add_system(rotate) .run(); } -#[derive(AsBindGroup, TypeUuid, Debug, Clone)] -#[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] -pub struct CustomMaterial { - #[uniform(0)] - color: Vec3, - #[texture(1)] - #[sampler(2)] - color_texture: Option>, - alpha_mode: AlphaMode, -} - -impl Material for CustomMaterial { - fn fragment_shader() -> ShaderRef { - "shaders/custom_material.wgsl".into() - } - - fn alpha_mode(&self) -> AlphaMode { - self.alpha_mode - } -} - /// set up a simple 3D scene fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, - mut cmaterials: ResMut>, ) { // plane commands.spawn(PbrBundle { mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), - material: materials.add(StandardMaterial { - unlit: true, - ..Color::rgb(0.3, 0.5, 0.3).into() - }), - ..default() - }); - // cube - commands.spawn(( - PbrBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), - material: materials.add(StandardMaterial { - unlit: true, - ..Color::rgb(0.8, 0.7, 0.6).into() - }), - transform: Transform::from_xyz(-1.0, 0.5, 0.0), - ..default() - }, - Rotates, - )); - // cube - commands.spawn(MaterialMeshBundle { - mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), - material: cmaterials.add(CustomMaterial { - color: Vec3::ONE, - color_texture: None, - alpha_mode: AlphaMode::Opaque, - }), - transform: Transform::from_xyz(1.0, 0.5, 0.0), + material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..default() }); // cube - commands.spawn(MaterialMeshBundle { + commands.spawn(PbrBundle { mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), - material: cmaterials.add(CustomMaterial { - color: Vec3::ONE, - color_texture: None, - alpha_mode: AlphaMode::Opaque, - }), + material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), transform: Transform::from_xyz(0.0, 0.5, 0.0), ..default() }); @@ -104,14 +44,3 @@ fn setup( ..default() }); } - -#[derive(Component)] -struct Rotates; - -fn rotate(mut q: Query<&mut Transform, With>, time: Res