From dcf8017089e7a54634246a10546be9e4c68198e5 Mon Sep 17 00:00:00 2001 From: DGriffin91 <33357138+DGriffin91@users.noreply.github.com> Date: Tue, 28 Jun 2022 16:08:53 -0700 Subject: [PATCH 001/138] Move texture sample out of branch --- crates/bevy_pbr/src/render/pbr_functions.wgsl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index b389801b61b46..f35d2a3d6b375 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -41,13 +41,13 @@ fn prepare_normal( #ifdef VERTEX_TANGENTS #ifdef STANDARDMATERIAL_NORMAL_MAP // Nt is the tangent-space normal. - var Nt: vec3; + var Nt = textureSample(normal_map_texture, normal_map_sampler, uv).rgb; if ((standard_material_flags & STANDARD_MATERIAL_FLAGS_TWO_COMPONENT_NORMAL_MAP) != 0u) { // Only use the xy components and derive z for 2-component normal maps. - Nt = vec3(textureSample(normal_map_texture, normal_map_sampler, uv).rg * 2.0 - 1.0, 0.0); + Nt = vec3(Nt.rg * 2.0 - 1.0, 0.0); Nt.z = sqrt(1.0 - Nt.x * Nt.x - Nt.y * Nt.y); } else { - Nt = textureSample(normal_map_texture, normal_map_sampler, uv).rgb * 2.0 - 1.0; + Nt = Nt * 2.0 - 1.0; } // Normal maps authored for DirectX require flipping the y component if ((standard_material_flags & STANDARD_MATERIAL_FLAGS_FLIP_NORMAL_MAP_Y) != 0u) { From 9e406c75dd5d1f2752d98dfb2263d5fa806e7e22 Mon Sep 17 00:00:00 2001 From: DGriffin91 Date: Tue, 28 Jun 2022 17:08:35 -0700 Subject: [PATCH 002/138] use tonemapping in array_texture example --- assets/shaders/array_texture.wgsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/shaders/array_texture.wgsl b/assets/shaders/array_texture.wgsl index 65466b8d70799..a3b0c0fb58852 100644 --- a/assets/shaders/array_texture.wgsl +++ b/assets/shaders/array_texture.wgsl @@ -59,5 +59,5 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4 { ); pbr_input.V = calculate_view(in.world_position, pbr_input.is_orthographic); - return pbr(pbr_input); + return tone_mapping(pbr(pbr_input)); } From c2209a6338f3186c906ff2f0b9535a488ea182c6 Mon Sep 17 00:00:00 2001 From: DGriffin91 Date: Tue, 28 Jun 2022 17:10:09 -0700 Subject: [PATCH 003/138] Revert "use tonemapping in array_texture example" This reverts commit 9e406c75dd5d1f2752d98dfb2263d5fa806e7e22. --- assets/shaders/array_texture.wgsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/shaders/array_texture.wgsl b/assets/shaders/array_texture.wgsl index a3b0c0fb58852..65466b8d70799 100644 --- a/assets/shaders/array_texture.wgsl +++ b/assets/shaders/array_texture.wgsl @@ -59,5 +59,5 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4 { ); pbr_input.V = calculate_view(in.world_position, pbr_input.is_orthographic); - return tone_mapping(pbr(pbr_input)); + return pbr(pbr_input); } From f2dbfdcdbd3b926d8dcf5abd543eb0bfce6434cb Mon Sep 17 00:00:00 2001 From: DGriffin91 Date: Wed, 31 May 2023 15:37:04 -0700 Subject: [PATCH 004/138] init deferred --- .../src/core_3d/main_opaque_pass_3d_node.rs | 8 +- crates/bevy_core_pipeline/src/prepass/mod.rs | 5 + crates/bevy_core_pipeline/src/prepass/node.rs | 13 + .../deferred_lighting/deferred_lighting.wgsl | 147 +++++ crates/bevy_pbr/src/deferred_lighting/mod.rs | 546 ++++++++++++++++++ crates/bevy_pbr/src/deferred_lighting/temp.rs | 364 ++++++++++++ crates/bevy_pbr/src/lib.rs | 1 + crates/bevy_pbr/src/material.rs | 8 +- crates/bevy_pbr/src/prepass/mod.rs | 87 ++- crates/bevy_pbr/src/prepass/prepass.wgsl | 8 + crates/bevy_pbr/src/render/mesh.rs | 19 +- .../src/render/mesh_view_bindings.wgsl | 2 + crates/bevy_pbr/src/render/pbr.wgsl | 11 +- crates/bevy_pbr/src/render/pbr_prepass.wgsl | 52 ++ examples/3d/load_gltf.rs | 18 + 15 files changed, 1268 insertions(+), 21 deletions(-) create mode 100644 crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl create mode 100644 crates/bevy_pbr/src/deferred_lighting/mod.rs create mode 100644 crates/bevy_pbr/src/deferred_lighting/temp.rs diff --git a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs index 34d8c299c94c9..c54a2e8b02845 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs @@ -1,7 +1,7 @@ use crate::{ clear_color::{ClearColor, ClearColorConfig}, core_3d::{Camera3d, Opaque3d}, - prepass::{DepthPrepass, MotionVectorPrepass, NormalPrepass}, + prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass}, skybox::{SkyboxBindGroup, SkyboxPipelineId}, }; use bevy_ecs::{prelude::*, query::QueryItem}; @@ -34,6 +34,7 @@ impl ViewNode for MainOpaquePass3dNode { Option<&'static DepthPrepass>, Option<&'static NormalPrepass>, Option<&'static MotionVectorPrepass>, + Option<&'static DeferredPrepass>, Option<&'static SkyboxPipelineId>, Option<&'static SkyboxBindGroup>, &'static ViewUniformOffset, @@ -53,12 +54,16 @@ impl ViewNode for MainOpaquePass3dNode { depth_prepass, normal_prepass, motion_vector_prepass, + deferred_prepass, skybox_pipeline, skybox_bind_group, view_uniform_offset, ): QueryItem, world: &World, ) -> Result<(), NodeRunError> { + if deferred_prepass.is_some() { + return Ok(()); // TODO allow forward materials + } // Run the opaque pass, sorted front-to-back // NOTE: Scoped to drop the mutable borrow of render_context #[cfg(feature = "trace")] @@ -86,6 +91,7 @@ impl ViewNode for MainOpaquePass3dNode { load: if depth_prepass.is_some() || normal_prepass.is_some() || motion_vector_prepass.is_some() + || deferred_prepass.is_some() { // if any prepass runs, it will generate a depth buffer so we should use it, // even if only the normal_prepass is used. diff --git a/crates/bevy_core_pipeline/src/prepass/mod.rs b/crates/bevy_core_pipeline/src/prepass/mod.rs index 93bcebe0318c3..d4721118e4f7f 100644 --- a/crates/bevy_core_pipeline/src/prepass/mod.rs +++ b/crates/bevy_core_pipeline/src/prepass/mod.rs @@ -41,6 +41,7 @@ use bevy_utils::FloatOrd; pub const DEPTH_PREPASS_FORMAT: TextureFormat = TextureFormat::Depth32Float; pub const NORMAL_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgb10a2Unorm; pub const MOTION_VECTOR_PREPASS_FORMAT: TextureFormat = TextureFormat::Rg16Float; +pub const DEFERRED_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgba32Uint; /// If added to a [`crate::prelude::Camera3d`] then depth values will be copied to a separate texture available to the main pass. #[derive(Component, Default, Reflect)] @@ -55,6 +56,9 @@ pub struct NormalPrepass; #[derive(Component, Default, Reflect)] pub struct MotionVectorPrepass; +#[derive(Component, Default, Reflect)] +pub struct DeferredPrepass; + /// Textures that are written to by the prepass. /// /// This component will only be present if any of the relevant prepass components are also present. @@ -69,6 +73,7 @@ pub struct ViewPrepassTextures { /// The motion vectors texture generated by the prepass. /// Exists only if [`MotionVectorPrepass`] is added to the `ViewTarget` pub motion_vectors: Option, + pub deferred: Option, /// The size of the textures. pub size: Extent3d, } diff --git a/crates/bevy_core_pipeline/src/prepass/node.rs b/crates/bevy_core_pipeline/src/prepass/node.rs index 6d3502212d990..f7f8c750ba40e 100644 --- a/crates/bevy_core_pipeline/src/prepass/node.rs +++ b/crates/bevy_core_pipeline/src/prepass/node.rs @@ -75,6 +75,19 @@ impl ViewNode for PrepassNode { }, }, )); + color_attachments.push( + view_prepass_textures + .deferred + .as_ref() + .map(|deferred_texture| RenderPassColorAttachment { + view: &deferred_texture.default_view, + resolve_target: None, + ops: Operations { + load: LoadOp::Clear(Color::rgba_linear(0.0, 0.0, 0.0, 0.0).into()), + store: true, + }, + }), + ); if color_attachments.iter().all(Option::is_none) { // all attachments are none: clear the attachment list so that no fragment shader is required color_attachments.clear(); diff --git a/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl b/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl new file mode 100644 index 0000000000000..8e0b81b0614a6 --- /dev/null +++ b/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl @@ -0,0 +1,147 @@ + +#import bevy_core_pipeline::fullscreen_vertex_shader +#import bevy_pbr::mesh_types +#import bevy_pbr::mesh_view_bindings + +#import bevy_pbr::pbr_types +#import bevy_pbr::utils +#import bevy_pbr::clustered_forward +#import bevy_pbr::lighting +#import bevy_pbr::shadows +#import bevy_pbr::fog +#import bevy_pbr::pbr_functions +#import bevy_pbr::pbr_ambient + +#import bevy_pbr::prepass_utils + +//struct StandardMaterial { +// base_color: vec4, +// emissive: vec4, +// perceptual_roughness: f32, +// metallic: f32, +// reflectance: f32, +// // 'flags' is a bit field indicating various options. u32 is 32 bits so we have up to 32 options. +// flags: u32, +// alpha_cutoff: f32, +// parallax_depth_scale: f32, +// max_parallax_layer_count: f32, +// max_relief_mapping_search_steps: u32, +//}; + +//struct PbrInput { +// material: StandardMaterial, +// occlusion: f32, +// frag_coord: vec4, +// world_position: vec4, +// // Normalized world normal used for shadow mapping as normal-mapping is not used for shadow +// // mapping +// world_normal: vec3, +// // Normalized normal-mapped world normal used for lighting +// N: vec3, +// // Normalized view vector in world space, pointing from the fragment world position toward the +// // view world position +// V: vec3, +// is_orthographic: bool, +// flags: u32, +//}; + +// --------------------------- +// from https://github.com/DGriffin91/bevy_coordinate_systems/blob/main/src/transformations.wgsl +// --------------------------- + +/// Convert a ndc space position to world space +fn position_ndc_to_world(ndc_pos: vec3) -> vec3 { + let world_pos = view.inverse_view_proj * vec4(ndc_pos, 1.0); + return world_pos.xyz / world_pos.w; +} + +/// Convert ndc space xy coordinate [-1.0 .. 1.0] to uv [0.0 .. 1.0] +fn ndc_to_uv(ndc: vec2) -> vec2 { + return ndc * vec2(0.5, -0.5) + vec2(0.5); +} + +/// Convert uv [0.0 .. 1.0] coordinate to ndc space xy [-1.0 .. 1.0] +fn uv_to_ndc(uv: vec2) -> vec2 { + return (uv - vec2(0.5)) * vec2(2.0, -2.0); +} + +/// returns the (0.0, 0.0) .. (1.0, 1.0) position within the viewport for the current render target +/// [0 .. render target viewport size] eg. [(0.0, 0.0) .. (1280.0, 720.0)] to [(0.0, 0.0) .. (1.0, 1.0)] +fn frag_coord_to_uv(frag_coord: vec2) -> vec2 { + return (frag_coord - view.viewport.xy) / view.viewport.zw; +} + +/// Convert frag coord to ndc +fn frag_coord_to_ndc(frag_coord: vec4) -> vec3 { + return vec3(uv_to_ndc(frag_coord_to_uv(frag_coord.xy)), frag_coord.z); +} + +// --------------------------- +// --------------------------- +// --------------------------- + +@fragment +fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { + let depth = prepass_depth(in.position, 0u); + let frag_coord = vec4(in.position.xy, depth, 0.0); + + let world_position = position_ndc_to_world(frag_coord_to_ndc(frag_coord)); + + let is_orthographic = view.projection[3].w == 1.0; + + let V = calculate_view(vec4(world_position, 0.0), is_orthographic); + + let deferred_data = textureLoad(deferred_prepass_texture, vec2(frag_coord.xy), 0); + + let base_color = unpack4x8unorm(deferred_data.r); + let emissive = unpack4x8unorm(deferred_data.g); + let misc = unpack4x8unorm(deferred_data.b); + let metallic = misc.r; + let perceptual_roughness = misc.g; + let occlusion = misc.b; + let reflectance = misc.a; + + + var pbr_input = pbr_input_new(); + + pbr_input.material.base_color = base_color; + pbr_input.material.emissive = emissive; + pbr_input.material.perceptual_roughness = perceptual_roughness; + pbr_input.material.metallic = metallic; + pbr_input.material.reflectance = reflectance; + //pbr_input.material.flags = + pbr_input.material.alpha_cutoff = 0.5; + pbr_input.material.parallax_depth_scale = 0.1; //default + pbr_input.material.max_parallax_layer_count = 16.0; //default + pbr_input.material.max_relief_mapping_search_steps = 5u; //default + + pbr_input.frag_coord = frag_coord; + pbr_input.world_normal = prepass_normal(frag_coord, 0u); + pbr_input.world_position = vec4(world_position, 0.0); + pbr_input.N = pbr_input.world_normal; + pbr_input.V = V; + pbr_input.is_orthographic = is_orthographic; + + + var output_color = pbr(pbr_input); + +#ifdef TONEMAP_IN_SHADER + output_color = tone_mapping(output_color); +#ifdef DEBAND_DITHER + var output_rgb = output_color.rgb; + output_rgb = powsafe(output_rgb, 1.0 / 2.2); + output_rgb = output_rgb + screen_space_dither(in.frag_coord.xy); + // This conversion back to linear space is required because our output texture format is + // SRGB; the GPU will assume our output is linear and will apply an SRGB conversion. + output_rgb = powsafe(output_rgb, 2.2); + output_color = vec4(output_rgb, output_color.a); +#endif +#endif +#ifdef PREMULTIPLY_ALPHA + output_color = premultiply_alpha(material.flags, output_color); +#endif + + + return output_color;//textureSample(screen_texture, texture_sampler, in.uv); +} + diff --git a/crates/bevy_pbr/src/deferred_lighting/mod.rs b/crates/bevy_pbr/src/deferred_lighting/mod.rs new file mode 100644 index 0000000000000..43d4cbb3630e3 --- /dev/null +++ b/crates/bevy_pbr/src/deferred_lighting/mod.rs @@ -0,0 +1,546 @@ +use bevy_asset::{load_internal_asset, HandleUntyped}; +use bevy_core_pipeline::{ + clear_color::ClearColorConfig, + core_3d, + fullscreen_vertex_shader::fullscreen_shader_vertex_state, + prelude::{Camera3d, ClearColor}, + prepass::{DeferredPrepass, ViewPrepassTextures}, + tonemapping::{ + get_lut_bind_group_layout_entries, get_lut_bindings, Tonemapping, TonemappingLuts, + }, +}; +use bevy_ecs::{prelude::*, query::QueryItem}; +use bevy_render::{ + globals::{GlobalsBuffer, GlobalsUniform}, + render_asset::RenderAssets, + render_graph::{NodeRunError, RenderGraphContext, ViewNode, ViewNodeRunner}, + render_resource::{BindGroupDescriptor, Operations, PipelineCache, RenderPassDescriptor}, + renderer::RenderContext, + texture::{FallbackImageCubemap, FallbackImagesDepth, FallbackImagesMsaa, Image}, + view::{Msaa, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms}, + Render, RenderSet, +}; + +use bevy_app::prelude::*; + +use bevy_reflect::TypeUuid; +use bevy_render::{ + render_graph::RenderGraphApp, render_resource::*, renderer::RenderDevice, texture::BevyDefault, + view::ExtractedView, RenderApp, +}; + +use crate::{ + environment_map, prepass, EnvironmentMapLight, FogMeta, GlobalLightMeta, GpuFog, GpuLights, + GpuPointLights, LightMeta, MeshPipeline, MeshPipelineKey, MeshViewBindGroup, ShadowSamplers, + ViewClusterBindings, ViewFogUniformOffset, ViewLightsUniformOffset, ViewShadowBindings, + CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, MAX_CASCADES_PER_LIGHT, MAX_DIRECTIONAL_LIGHTS, +}; + +pub struct DeferredLightingPlugin; + +pub const DEFERRED_LIGHTING_SHADER_HANDLE: HandleUntyped = + HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2708011359337029741); + +impl Plugin for DeferredLightingPlugin { + fn build(&self, app: &mut App) { + load_internal_asset!( + app, + DEFERRED_LIGHTING_SHADER_HANDLE, + "deferred_lighting.wgsl", + Shader::from_wgsl + ); + + let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + return; + }; + + render_app + .init_resource::>() + .add_systems( + Render, + ( + queue_deferred_lighting_bind_groups.in_set(RenderSet::Queue), + prepare_deferred_lighting_pipelines.in_set(RenderSet::Prepare), + ), + ) + .add_render_graph_node::>( + core_3d::graph::NAME, + DeferredLightingNode::NAME, + ) + .add_render_graph_edges( + core_3d::graph::NAME, + &[ + core_3d::graph::node::PREPASS, + DeferredLightingNode::NAME, + core_3d::graph::node::START_MAIN_PASS, + ], + ); + } + + fn finish(&self, app: &mut App) { + let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { + return; + }; + + render_app.init_resource::(); + } +} + +#[derive(Default)] +struct DeferredLightingNode; + +impl DeferredLightingNode { + pub const NAME: &str = "deferred_lighting"; +} + +impl ViewNode for DeferredLightingNode { + type ViewQuery = ( + &'static ViewUniformOffset, + &'static ViewLightsUniformOffset, + &'static ViewFogUniformOffset, + &'static MeshViewBindGroup, + &'static ViewTarget, + &'static Camera3d, + &'static DeferredLightingPipeline, + ); + + fn run( + &self, + _graph_context: &mut RenderGraphContext, + render_context: &mut RenderContext, + ( + view_uniform_offset, + view_lights_offset, + view_fog_offset, + mesh_bind_group, + target, + camera_3d, + deferred_lighting_pipeline, + ): QueryItem, + world: &World, + ) -> Result<(), NodeRunError> { + let pipeline_cache = world.resource::(); + + let Some(pipeline) = pipeline_cache.get_render_pipeline(deferred_lighting_pipeline.pipeline_id) else { + return Ok(()); + }; + + let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor { + label: Some("deferred_lighting_pass"), + color_attachments: &[Some(target.get_color_attachment(Operations { + load: match camera_3d.clear_color { + ClearColorConfig::Default => { + LoadOp::Clear(world.resource::().0.into()) + } + ClearColorConfig::Custom(color) => LoadOp::Clear(color.into()), + ClearColorConfig::None => LoadOp::Load, + }, + store: true, + }))], + depth_stencil_attachment: None, + }); + + render_pass.set_render_pipeline(pipeline); + render_pass.set_bind_group( + 0, + &mesh_bind_group.value, + &[ + view_uniform_offset.offset, + view_lights_offset.offset, + view_fog_offset.offset, + ], + ); + render_pass.draw(0..3, 0..1); + + Ok(()) + } +} + +#[derive(Resource)] +pub struct DeferredLightingLayout { + bind_group_layout: BindGroupLayout, +} + +#[derive(Component)] +pub struct DeferredLightingPipeline { + pub pipeline_id: CachedRenderPipelineId, +} + +impl SpecializedRenderPipeline for DeferredLightingLayout { + type Key = MeshPipelineKey; + + fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { + let mut shader_defs = Vec::new(); + + // TODO insert tonemapping etc... + + 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, + )); + + RenderPipelineDescriptor { + label: Some("deferred_lighting_pipeline".into()), + layout: vec![self.bind_group_layout.clone()], + vertex: fullscreen_shader_vertex_state(), + fragment: Some(FragmentState { + shader: DEFERRED_LIGHTING_SHADER_HANDLE.typed(), + shader_defs, + entry_point: "fragment".into(), + targets: vec![Some(ColorTargetState { + format: if key.contains(MeshPipelineKey::HDR) { + ViewTarget::TEXTURE_FORMAT_HDR + } else { + TextureFormat::bevy_default() + }, + blend: None, + write_mask: ColorWrites::ALL, + })], + }), + primitive: PrimitiveState::default(), + depth_stencil: None, + multisample: MultisampleState::default(), + push_constant_ranges: vec![], + } + } +} + +impl FromWorld for DeferredLightingLayout { + fn from_world(world: &mut World) -> Self { + let render_device = world.resource::(); + + let clustered_forward_buffer_binding_type = render_device + .get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT); + + let bind_group_layout = + render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { + label: Some("deferred_lighting_bind_group_layout"), + entries: &layout_entries(clustered_forward_buffer_binding_type, false), + }); + + Self { bind_group_layout } + } +} + +pub fn prepare_deferred_lighting_pipelines( + mut commands: Commands, + pipeline_cache: Res, + mut pipelines: ResMut>, + differed_lighting_pipeline: Res, + views: Query<(Entity, &ExtractedView), With>, +) { + for (entity, view) in &views { + let pipeline_id = pipelines.specialize( + &pipeline_cache, + &differed_lighting_pipeline, + MeshPipelineKey::from_hdr(view.hdr), + ); + + commands + .entity(entity) + .insert(DeferredLightingPipeline { pipeline_id }); + } +} + +/// Returns the appropriate bind group layout vec based on the parameters +fn layout_entries( + clustered_forward_buffer_binding_type: BufferBindingType, + multisampled: bool, +) -> Vec { + let mut entries = vec![ + // 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, + }, + // Lights + BindGroupLayoutEntry { + binding: 1, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: Some(GpuLights::min_size()), + }, + count: None, + }, + // Point Shadow Texture Cube Array + BindGroupLayoutEntry { + binding: 2, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled: false, + sample_type: TextureSampleType::Depth, + #[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] + view_dimension: TextureViewDimension::CubeArray, + #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + view_dimension: TextureViewDimension::Cube, + }, + count: None, + }, + // Point Shadow Texture Array Sampler + BindGroupLayoutEntry { + binding: 3, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Sampler(SamplerBindingType::Comparison), + count: None, + }, + // Directional Shadow Texture Array + BindGroupLayoutEntry { + binding: 4, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled: false, + sample_type: TextureSampleType::Depth, + #[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] + view_dimension: TextureViewDimension::D2Array, + #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + view_dimension: TextureViewDimension::D2, + }, + count: None, + }, + // Directional Shadow Texture Array Sampler + BindGroupLayoutEntry { + binding: 5, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Sampler(SamplerBindingType::Comparison), + count: None, + }, + // PointLights + BindGroupLayoutEntry { + binding: 6, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Buffer { + ty: clustered_forward_buffer_binding_type, + has_dynamic_offset: false, + min_binding_size: Some(GpuPointLights::min_size( + clustered_forward_buffer_binding_type, + )), + }, + count: None, + }, + // ClusteredLightIndexLists + BindGroupLayoutEntry { + binding: 7, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Buffer { + ty: clustered_forward_buffer_binding_type, + has_dynamic_offset: false, + min_binding_size: Some(ViewClusterBindings::min_size_cluster_light_index_lists( + clustered_forward_buffer_binding_type, + )), + }, + count: None, + }, + // ClusterOffsetsAndCounts + BindGroupLayoutEntry { + binding: 8, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Buffer { + ty: clustered_forward_buffer_binding_type, + has_dynamic_offset: false, + min_binding_size: Some(ViewClusterBindings::min_size_cluster_offsets_and_counts( + clustered_forward_buffer_binding_type, + )), + }, + count: None, + }, + // Globals + BindGroupLayoutEntry { + binding: 9, + visibility: ShaderStages::VERTEX_FRAGMENT, + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: Some(GlobalsUniform::min_size()), + }, + count: None, + }, + // Fog + BindGroupLayoutEntry { + binding: 10, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: Some(GpuFog::min_size()), + }, + count: None, + }, + ]; + + // EnvironmentMapLight + let environment_map_entries = environment_map::get_bind_group_layout_entries([11, 12, 13]); + entries.extend_from_slice(&environment_map_entries); + + // Tonemapping + let tonemapping_lut_entries = get_lut_bind_group_layout_entries([14, 15]); + entries.extend_from_slice(&tonemapping_lut_entries); + + if cfg!(any(not(feature = "webgl"), not(target_arch = "wasm32"))) + || (cfg!(all(feature = "webgl", target_arch = "wasm32")) && !multisampled) + { + entries.extend_from_slice(&prepass::get_bind_group_layout_entries( + [16, 17, 18, 19], + multisampled, + )); + } + + entries +} + +#[allow(clippy::too_many_arguments)] +pub fn queue_deferred_lighting_bind_groups( + mut commands: Commands, + render_device: Res, + mesh_pipeline: Res, + shadow_samplers: Res, + light_meta: Res, + global_light_meta: Res, + fog_meta: Res, + view_uniforms: Res, + views: Query< + ( + Entity, + &ViewShadowBindings, + &ViewClusterBindings, + Option<&ViewPrepassTextures>, + Option<&EnvironmentMapLight>, + &Tonemapping, + ), + With, + >, + images: Res>, + mut fallback_images: FallbackImagesMsaa, + mut fallback_depths: FallbackImagesDepth, + fallback_cubemap: Res, + msaa: Res, + globals_buffer: Res, + tonemapping_luts: Res, +) { + if let ( + Some(view_binding), + Some(light_binding), + Some(point_light_binding), + Some(globals), + Some(fog_binding), + ) = ( + view_uniforms.uniforms.binding(), + light_meta.view_gpu_lights.binding(), + global_light_meta.gpu_point_lights.binding(), + globals_buffer.buffer.binding(), + fog_meta.gpu_fogs.binding(), + ) { + for ( + entity, + view_shadow_bindings, + view_cluster_bindings, + prepass_textures, + environment_map, + tonemapping, + ) in &views + { + let layout = if msaa.samples() > 1 { + &mesh_pipeline.view_layout_multisampled + } else { + &mesh_pipeline.view_layout + }; + + let mut entries = vec![ + BindGroupEntry { + binding: 0, + resource: view_binding.clone(), + }, + BindGroupEntry { + binding: 1, + resource: light_binding.clone(), + }, + BindGroupEntry { + binding: 2, + resource: BindingResource::TextureView( + &view_shadow_bindings.point_light_depth_texture_view, + ), + }, + BindGroupEntry { + binding: 3, + resource: BindingResource::Sampler(&shadow_samplers.point_light_sampler), + }, + BindGroupEntry { + binding: 4, + resource: BindingResource::TextureView( + &view_shadow_bindings.directional_light_depth_texture_view, + ), + }, + BindGroupEntry { + binding: 5, + resource: BindingResource::Sampler(&shadow_samplers.directional_light_sampler), + }, + BindGroupEntry { + binding: 6, + resource: point_light_binding.clone(), + }, + BindGroupEntry { + binding: 7, + resource: view_cluster_bindings.light_index_lists_binding().unwrap(), + }, + BindGroupEntry { + binding: 8, + resource: view_cluster_bindings.offsets_and_counts_binding().unwrap(), + }, + BindGroupEntry { + binding: 9, + resource: globals.clone(), + }, + BindGroupEntry { + binding: 10, + resource: fog_binding.clone(), + }, + ]; + + let env_map = environment_map::get_bindings( + environment_map, + &images, + &fallback_cubemap, + [11, 12, 13], + ); + entries.extend_from_slice(&env_map); + + let tonemapping_luts = + get_lut_bindings(&images, &tonemapping_luts, tonemapping, [14, 15]); + entries.extend_from_slice(&tonemapping_luts); + + // When using WebGL, we can't have a depth texture with multisampling + if cfg!(any(not(feature = "webgl"), not(target_arch = "wasm32"))) + || (cfg!(all(feature = "webgl", target_arch = "wasm32")) && msaa.samples() == 1) + { + entries.extend_from_slice(&prepass::get_bindings( + prepass_textures, + &mut fallback_images, + &mut fallback_depths, + &msaa, + [16, 17, 18, 19], + )); + } + + let view_bind_group = render_device.create_bind_group(&BindGroupDescriptor { + entries: &entries, + label: Some("mesh_view_bind_group"), + layout, + }); + + commands.entity(entity).insert(MeshViewBindGroup { + value: view_bind_group, + }); + } + } +} diff --git a/crates/bevy_pbr/src/deferred_lighting/temp.rs b/crates/bevy_pbr/src/deferred_lighting/temp.rs new file mode 100644 index 0000000000000..d9d4f9c5c3424 --- /dev/null +++ b/crates/bevy_pbr/src/deferred_lighting/temp.rs @@ -0,0 +1,364 @@ +// Recursive expansion of AsBindGroup macro +// ========================================= + +impl AsBindGroup for StandardMaterial { + type Data = StandardMaterialKey; + fn as_bind_group( + &self, + layout: &BindGroupLayout, + render_device: &bevy_render::renderer::RenderDevice, + images: &bevy_render::render_asset::RenderAssets, + fallback_image: &bevy_render::texture::FallbackImage, + ) -> Result, AsBindGroupError> { + let bindings = vec![ + { + use AsBindGroupShaderType; + let mut buffer = encase::UniformBuffer::new(Vec::new()); + let converted: StandardMaterialUniform = self.as_bind_group_shader_type(images); + buffer.write(&converted).unwrap(); + OwnedBindingResource::Buffer(render_device.create_buffer_with_data( + &BufferInitDescriptor { + label: None, + usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM, + contents: buffer.as_ref(), + }, + )) + }, + OwnedBindingResource::TextureView({ + let handle: = + (&self.base_color_texture).into(); + if let Some(handle) = handle { + images + .get(handle) + .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? + .texture_view + .clone() + } else { + fallback_image.texture_view.clone() + } + }), + OwnedBindingResource::Sampler({ + let handle: = + (&self.base_color_texture).into(); + if let Some(handle) = handle { + images + .get(handle) + .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? + .sampler + .clone() + } else { + fallback_image.sampler.clone() + } + }), + OwnedBindingResource::TextureView({ + let handle: = + (&self.emissive_texture).into(); + if let Some(handle) = handle { + images + .get(handle) + .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? + .texture_view + .clone() + } else { + fallback_image.texture_view.clone() + } + }), + OwnedBindingResource::Sampler({ + let handle: = + (&self.emissive_texture).into(); + if let Some(handle) = handle { + images + .get(handle) + .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? + .sampler + .clone() + } else { + fallback_image.sampler.clone() + } + }), + OwnedBindingResource::TextureView({ + let handle: = + (&self.metallic_roughness_texture).into(); + if let Some(handle) = handle { + images + .get(handle) + .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? + .texture_view + .clone() + } else { + fallback_image.texture_view.clone() + } + }), + OwnedBindingResource::Sampler({ + let handle: = + (&self.metallic_roughness_texture).into(); + if let Some(handle) = handle { + images + .get(handle) + .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? + .sampler + .clone() + } else { + fallback_image.sampler.clone() + } + }), + OwnedBindingResource::TextureView({ + let handle: = + (&self.normal_map_texture).into(); + if let Some(handle) = handle { + images + .get(handle) + .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? + .texture_view + .clone() + } else { + fallback_image.texture_view.clone() + } + }), + OwnedBindingResource::Sampler({ + let handle: = + (&self.normal_map_texture).into(); + if let Some(handle) = handle { + images + .get(handle) + .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? + .sampler + .clone() + } else { + fallback_image.sampler.clone() + } + }), + OwnedBindingResource::TextureView({ + let handle: = + (&self.occlusion_texture).into(); + if let Some(handle) = handle { + images + .get(handle) + .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? + .texture_view + .clone() + } else { + fallback_image.texture_view.clone() + } + }), + OwnedBindingResource::Sampler({ + let handle: = + (&self.occlusion_texture).into(); + if let Some(handle) = handle { + images + .get(handle) + .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? + .sampler + .clone() + } else { + fallback_image.sampler.clone() + } + }), + OwnedBindingResource::TextureView({ + let handle: = + (&self.depth_map).into(); + if let Some(handle) = handle { + images + .get(handle) + .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? + .texture_view + .clone() + } else { + fallback_image.texture_view.clone() + } + }), + OwnedBindingResource::Sampler({ + let handle: = + (&self.depth_map).into(); + if let Some(handle) = handle { + images + .get(handle) + .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? + .sampler + .clone() + } else { + fallback_image.sampler.clone() + } + }), + ]; + let bind_group = { + let descriptor = BindGroupDescriptor { + entries: &[ + BindGroupEntry { + binding: 0u32, + resource: bindings[0usize].get_binding(), + }, + BindGroupEntry { + binding: 1u32, + resource: bindings[1usize].get_binding(), + }, + BindGroupEntry { + binding: 2u32, + resource: bindings[2usize].get_binding(), + }, + BindGroupEntry { + binding: 3u32, + resource: bindings[3usize].get_binding(), + }, + BindGroupEntry { + binding: 4u32, + resource: bindings[4usize].get_binding(), + }, + BindGroupEntry { + binding: 5u32, + resource: bindings[5usize].get_binding(), + }, + BindGroupEntry { + binding: 6u32, + resource: bindings[6usize].get_binding(), + }, + BindGroupEntry { + binding: 9u32, + resource: bindings[7usize].get_binding(), + }, + BindGroupEntry { + binding: 10u32, + resource: bindings[8usize].get_binding(), + }, + BindGroupEntry { + binding: 7u32, + resource: bindings[9usize].get_binding(), + }, + BindGroupEntry { + binding: 8u32, + resource: bindings[10usize].get_binding(), + }, + BindGroupEntry { + binding: 11u32, + resource: bindings[11usize].get_binding(), + }, + BindGroupEntry { + binding: 12u32, + resource: bindings[12usize].get_binding(), + }, + ], + label: None, + layout: &layout, + }; + render_device.create_bind_group(&descriptor) + }; + Ok(PreparedBindGroup { + bindings, + bind_group, + data: self.into(), + }) + } + fn bind_group_layout(render_device: &bevy_render::renderer::RenderDevice) -> BindGroupLayout { + render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { + entries: &[ + BindGroupLayoutEntry { + binding: 0u32, + visibility: ShaderStages::all(), + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: Some(::min_size()), + }, + count: None, + }, + BindGroupLayoutEntry { + binding: 1u32, + visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled: false, + sample_type: TextureSampleType::Float { filterable: true }, + view_dimension: TextureViewDimension::D2, + }, + count: None, + }, + BindGroupLayoutEntry { + binding: 2u32, + visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, + ty: BindingType::Sampler(SamplerBindingType::Filtering), + count: None, + }, + BindGroupLayoutEntry { + binding: 3u32, + visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled: false, + sample_type: TextureSampleType::Float { filterable: true }, + view_dimension: TextureViewDimension::D2, + }, + count: None, + }, + BindGroupLayoutEntry { + binding: 4u32, + visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, + ty: BindingType::Sampler(SamplerBindingType::Filtering), + count: None, + }, + BindGroupLayoutEntry { + binding: 5u32, + visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled: false, + sample_type: TextureSampleType::Float { filterable: true }, + view_dimension: TextureViewDimension::D2, + }, + count: None, + }, + BindGroupLayoutEntry { + binding: 6u32, + visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, + ty: BindingType::Sampler(SamplerBindingType::Filtering), + count: None, + }, + BindGroupLayoutEntry { + binding: 9u32, + visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled: false, + sample_type: TextureSampleType::Float { filterable: true }, + view_dimension: TextureViewDimension::D2, + }, + count: None, + }, + BindGroupLayoutEntry { + binding: 10u32, + visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, + ty: BindingType::Sampler(SamplerBindingType::Filtering), + count: None, + }, + BindGroupLayoutEntry { + binding: 7u32, + visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled: false, + sample_type: TextureSampleType::Float { filterable: true }, + view_dimension: TextureViewDimension::D2, + }, + count: None, + }, + BindGroupLayoutEntry { + binding: 8u32, + visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, + ty: BindingType::Sampler(SamplerBindingType::Filtering), + count: None, + }, + BindGroupLayoutEntry { + binding: 11u32, + visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled: false, + sample_type: TextureSampleType::Float { filterable: true }, + view_dimension: TextureViewDimension::D2, + }, + count: None, + }, + BindGroupLayoutEntry { + binding: 12u32, + visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, + ty: BindingType::Sampler(SamplerBindingType::Filtering), + count: None, + }, + ], + label: None, + }) + } +} diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 140364787bd9a..0f710f69942eb 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -4,6 +4,7 @@ pub mod wireframe; mod alpha; mod bundle; +pub mod deferred_lighting; mod environment_map; mod fog; mod light; diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index e5db9bee18739..29815775d10d3 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -8,7 +8,7 @@ use bevy_asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle}; use bevy_core_pipeline::{ core_3d::{AlphaMask3d, Opaque3d, Transparent3d}, experimental::taa::TemporalAntiAliasSettings, - prepass::NormalPrepass, + prepass::{DeferredPrepass, NormalPrepass}, tonemapping::{DebandDither, Tonemapping}, }; use bevy_derive::{Deref, DerefMut}; @@ -388,6 +388,7 @@ pub fn queue_material_meshes( Option<&DebandDither>, Option<&EnvironmentMapLight>, Option<&NormalPrepass>, + Option<&DeferredPrepass>, Option<&TemporalAntiAliasSettings>, &mut RenderPhase, &mut RenderPhase, @@ -403,6 +404,7 @@ pub fn queue_material_meshes( dither, environment_map, normal_prepass, + deferred_prepass, taa_settings, mut opaque_phase, mut alpha_mask_phase, @@ -420,6 +422,10 @@ pub fn queue_material_meshes( view_key |= MeshPipelineKey::NORMAL_PREPASS; } + if deferred_prepass.is_some() { + view_key |= MeshPipelineKey::DEFERRED_PREPASS; + } + if taa_settings.is_some() { view_key |= MeshPipelineKey::TAA; } diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 9307bc0d75aa9..7a2966e8f0f85 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -3,9 +3,9 @@ use bevy_asset::{load_internal_asset, AssetServer, Handle, HandleUntyped}; use bevy_core_pipeline::{ prelude::Camera3d, prepass::{ - AlphaMask3dPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass, Opaque3dPrepass, - ViewPrepassTextures, DEPTH_PREPASS_FORMAT, MOTION_VECTOR_PREPASS_FORMAT, - NORMAL_PREPASS_FORMAT, + AlphaMask3dPrepass, DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass, + Opaque3dPrepass, ViewPrepassTextures, DEFERRED_PREPASS_FORMAT, DEPTH_PREPASS_FORMAT, + MOTION_VECTOR_PREPASS_FORMAT, NORMAL_PREPASS_FORMAT, }, }; use bevy_ecs::{ @@ -410,6 +410,10 @@ where } } + if key.mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) { + shader_defs.push("DEFERRED_PREPASS".into()); + } + if key .mesh_key .contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) @@ -457,6 +461,15 @@ where write_mask: ColorWrites::ALL, }), ); + targets.push( + key.mesh_key + .contains(MeshPipelineKey::DEFERRED_PREPASS) + .then_some(ColorTargetState { + format: DEFERRED_PREPASS_FORMAT, + blend: None, + write_mask: ColorWrites::ALL, + }), + ); if targets.iter().all(Option::is_none) { // if no targets are required then clear the list, so that no fragment shader is required // (though one may still be used for discarding depth buffer writes) @@ -545,9 +558,9 @@ where } pub fn get_bind_group_layout_entries( - bindings: [u32; 3], + bindings: [u32; 4], multisampled: bool, -) -> [BindGroupLayoutEntry; 3] { +) -> [BindGroupLayoutEntry; 4] { [ // Depth texture BindGroupLayoutEntry { @@ -582,6 +595,17 @@ pub fn get_bind_group_layout_entries( }, count: None, }, + // Deferred texture + BindGroupLayoutEntry { + binding: bindings[3], + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled: false, + sample_type: TextureSampleType::Uint, + view_dimension: TextureViewDimension::D2, + }, + count: None, + }, ] } @@ -590,8 +614,8 @@ pub fn get_bindings<'a>( fallback_images: &'a mut FallbackImagesMsaa, fallback_depths: &'a mut FallbackImagesDepth, msaa: &'a Msaa, - bindings: [u32; 3], -) -> [BindGroupEntry<'a>; 3] { + bindings: [u32; 4], +) -> [BindGroupEntry<'a>; 4] { let depth_view = match prepass_textures.and_then(|x| x.depth.as_ref()) { Some(texture) => &texture.default_view, None => { @@ -615,6 +639,11 @@ pub fn get_bindings<'a>( None => normal_motion_vectors_fallback, }; + let deferred_view = match prepass_textures.and_then(|x| x.deferred.as_ref()) { + Some(texture) => &texture.default_view, + None => normal_motion_vectors_fallback, + }; + [ BindGroupEntry { binding: bindings[0], @@ -628,6 +657,10 @@ pub fn get_bindings<'a>( binding: bindings[2], resource: BindingResource::TextureView(motion_vectors_view), }, + BindGroupEntry { + binding: bindings[3], + resource: BindingResource::TextureView(deferred_view), + }, ] } @@ -642,6 +675,7 @@ pub fn extract_camera_prepass_phase( Option<&DepthPrepass>, Option<&NormalPrepass>, Option<&MotionVectorPrepass>, + Option<&DeferredPrepass>, Option<&PreviousViewProjection>, ), With, @@ -654,6 +688,7 @@ pub fn extract_camera_prepass_phase( depth_prepass, normal_prepass, motion_vector_prepass, + deferred_prepass, maybe_previous_view_proj, ) in cameras_3d.iter() { @@ -663,6 +698,7 @@ pub fn extract_camera_prepass_phase( if depth_prepass.is_some() || normal_prepass.is_some() || motion_vector_prepass.is_some() + || deferred_prepass.is_some() { entity.insert(( RenderPhase::::default(), @@ -679,6 +715,9 @@ pub fn extract_camera_prepass_phase( if motion_vector_prepass.is_some() { entity.insert(MotionVectorPrepass); } + if deferred_prepass.is_some() { + entity.insert(DeferredPrepass); + } if let Some(previous_view) = maybe_previous_view_proj { entity.insert(previous_view.clone()); @@ -741,6 +780,7 @@ pub fn prepare_prepass_textures( Option<&DepthPrepass>, Option<&NormalPrepass>, Option<&MotionVectorPrepass>, + Option<&DeferredPrepass>, ), ( With>, @@ -750,8 +790,11 @@ pub fn prepare_prepass_textures( ) { let mut depth_textures = HashMap::default(); let mut normal_textures = HashMap::default(); + let mut deferred_textures = HashMap::default(); let mut motion_vectors_textures = HashMap::default(); - for (entity, camera, depth_prepass, normal_prepass, motion_vector_prepass) in &views_3d { + for (entity, camera, depth_prepass, normal_prepass, motion_vector_prepass, deferred_prepass) in + &views_3d + { let Some(physical_target_size) = camera.physical_target_size else { continue; }; @@ -827,10 +870,33 @@ pub fn prepare_prepass_textures( .clone() }); + let cached_deferred_texture = deferred_prepass.is_some().then(|| { + deferred_textures + .entry(camera.target.clone()) + .or_insert_with(|| { + texture_cache.get( + &render_device, + TextureDescriptor { + label: Some("prepass_deferred_texture"), + size, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: DEFERRED_PREPASS_FORMAT, + usage: TextureUsages::RENDER_ATTACHMENT + | TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }, + ) + }) + .clone() + }); + commands.entity(entity).insert(ViewPrepassTextures { depth: cached_depth_texture, normal: cached_normals_texture, motion_vectors: cached_motion_vectors_texture, + deferred: cached_deferred_texture, size, }); } @@ -913,6 +979,7 @@ pub fn queue_prepass_material_meshes( Option<&DepthPrepass>, Option<&NormalPrepass>, Option<&MotionVectorPrepass>, + Option<&DeferredPrepass>, )>, ) where M::Data: PartialEq + Eq + Hash + Clone, @@ -933,6 +1000,7 @@ pub fn queue_prepass_material_meshes( depth_prepass, normal_prepass, motion_vector_prepass, + deferred_prepass, ) in &mut views { let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples()); @@ -945,6 +1013,9 @@ pub fn queue_prepass_material_meshes( if motion_vector_prepass.is_some() { view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS; } + if deferred_prepass.is_some() { + view_key |= MeshPipelineKey::DEFERRED_PREPASS; + } let rangefinder = view.rangefinder3d(); diff --git a/crates/bevy_pbr/src/prepass/prepass.wgsl b/crates/bevy_pbr/src/prepass/prepass.wgsl index 9aba5afb6ceb6..97366603952f3 100644 --- a/crates/bevy_pbr/src/prepass/prepass.wgsl +++ b/crates/bevy_pbr/src/prepass/prepass.wgsl @@ -106,6 +106,10 @@ struct FragmentOutput { #ifdef MOTION_VECTOR_PREPASS @location(1) motion_vector: vec2, #endif // MOTION_VECTOR_PREPASS + +#ifdef DEFERRED_PREPASS + @location(2) deferred: vec4, +#endif // DEFERRED_PREPASS } @fragment @@ -130,6 +134,10 @@ fn fragment(in: FragmentInput) -> FragmentOutput { out.motion_vector = (clip_position - previous_clip_position) * vec2(0.5, -0.5); #endif // MOTION_VECTOR_PREPASS +#ifdef DEFERRED_PREPASS + out.deferred = vec4(0u); +#endif + return out; } #endif // PREPASS_FRAGMENT diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index f94c75b370c3e..494857b169e5f 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -448,7 +448,7 @@ impl FromWorld for MeshPipeline { || (cfg!(all(feature = "webgl", target_arch = "wasm32")) && !multisampled) { entries.extend_from_slice(&prepass::get_bind_group_layout_entries( - [16, 17, 18], + [16, 17, 18, 19], multisampled, )); } @@ -581,12 +581,13 @@ bitflags::bitflags! { const DEBAND_DITHER = (1 << 2); const DEPTH_PREPASS = (1 << 3); const NORMAL_PREPASS = (1 << 4); - const MOTION_VECTOR_PREPASS = (1 << 5); - const MAY_DISCARD = (1 << 6); // Guards shader codepaths that may discard, allowing early depth tests in most cases + const DEFERRED_PREPASS = (1 << 5); + const MOTION_VECTOR_PREPASS = (1 << 6); + const MAY_DISCARD = (1 << 7); // Guards shader codepaths that may discard, allowing early depth tests in most cases // See: https://www.khronos.org/opengl/wiki/Early_Fragment_Test - const ENVIRONMENT_MAP = (1 << 7); - const DEPTH_CLAMP_ORTHO = (1 << 8); - const TAA = (1 << 9); + const ENVIRONMENT_MAP = (1 << 8); + const DEPTH_CLAMP_ORTHO = (1 << 9); + const TAA = (1 << 10); 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); // @@ -673,6 +674,10 @@ impl SpecializedMeshPipeline for MeshPipeline { shader_defs.push("LOAD_PREPASS_NORMALS".into()); } + if key.contains(MeshPipelineKey::DEFERRED_PREPASS) { + shader_defs.push("LOAD_PREPASS_DEFERRED".into()); + } + if layout.contains(Mesh::ATTRIBUTE_POSITION) { shader_defs.push("VERTEX_POSITIONS".into()); vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0)); @@ -1085,7 +1090,7 @@ pub fn queue_mesh_view_bind_groups( &mut fallback_images, &mut fallback_depths, &msaa, - [16, 17, 18], + [16, 17, 18, 19], )); } diff --git a/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl b/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl index 378cf8e290935..3190d9c73cea0 100644 --- a/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl +++ b/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl @@ -72,4 +72,6 @@ var depth_prepass_texture: texture_depth_2d; var normal_prepass_texture: texture_2d; @group(0) @binding(18) var motion_vector_prepass_texture: texture_2d; +@group(0) @binding(19) +var deferred_prepass_texture: texture_2d; #endif diff --git a/crates/bevy_pbr/src/render/pbr.wgsl b/crates/bevy_pbr/src/render/pbr.wgsl index 7043c4e92abe7..3bf8c6de0f73e 100644 --- a/crates/bevy_pbr/src/render/pbr.wgsl +++ b/crates/bevy_pbr/src/render/pbr.wgsl @@ -21,10 +21,12 @@ struct FragmentInput { @fragment fn fragment(in: FragmentInput) -> @location(0) vec4 { + let is_orthographic = view.projection[3].w == 1.0; let V = calculate_view(in.world_position, is_orthographic); #ifdef VERTEX_UVS var uv = in.uv; + let deferred_data = textureLoad(deferred_prepass_texture, vec2(in.frag_coord.xy), 0); #ifdef VERTEX_TANGENTS if ((material.flags & STANDARD_MATERIAL_FLAGS_DEPTH_MAP_BIT) != 0u) { let N = in.world_normal; @@ -50,10 +52,11 @@ fn fragment(in: FragmentInput) -> @location(0) vec4 { output_color = output_color * in.color; #endif #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, uv); - } -#endif + output_color = output_color * unpack4x8unorm(deferred_data.r); + //if ((material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) { + // output_color = output_color * textureSample(base_color_texture, base_color_sampler, uv); + //} +#endif // VERTEX_UVS // 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) { diff --git a/crates/bevy_pbr/src/render/pbr_prepass.wgsl b/crates/bevy_pbr/src/render/pbr_prepass.wgsl index 1a90c4570934f..e7abef2b2fc02 100644 --- a/crates/bevy_pbr/src/render/pbr_prepass.wgsl +++ b/crates/bevy_pbr/src/render/pbr_prepass.wgsl @@ -22,6 +22,7 @@ struct FragmentInput { @location(3) world_position: vec4, @location(4) previous_world_position: vec4, #endif // MOTION_VECTOR_PREPASS + }; // Cutoff used for the premultiplied alpha modes BLEND and ADD. @@ -66,6 +67,10 @@ struct FragmentOutput { #ifdef MOTION_VECTOR_PREPASS @location(1) motion_vector: vec2, #endif // MOTION_VECTOR_PREPASS + +#ifdef DEFERRED_PREPASS + @location(2) deferred: vec4, +#endif // DEFERRED_PREPASS } @fragment @@ -116,6 +121,53 @@ fn fragment(in: FragmentInput) -> FragmentOutput { out.motion_vector = (clip_position - previous_clip_position) * vec2(0.5, -0.5); #endif // MOTION_VECTOR_PREPASS +#ifdef DEFERRED_PREPASS + let uv = in.uv; + var output_color = material.base_color; + var reflectance = 0.5; + var emissive = vec3(0.0); + var metallic = 0.0; + var perceptual_roughness = 0.5; + var occlusion = 1.0; + +#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, 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) { + reflectance = material.reflectance; + emissive = material.emissive.rgb; +#ifdef VERTEX_UVS + if ((material.flags & STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) { + emissive = emissive.rgb * textureSample(emissive_texture, emissive_sampler, uv).rgb; + } +#endif + metallic = material.metallic; + perceptual_roughness = material.perceptual_roughness; +#ifdef VERTEX_UVS + if ((material.flags & STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) { + let metallic_roughness = textureSample(metallic_roughness_texture, metallic_roughness_sampler, uv); + // Sampling from GLTF standard channels for now + metallic = metallic * metallic_roughness.b; + perceptual_roughness = perceptual_roughness * metallic_roughness.g; + } +#endif +#ifdef VERTEX_UVS + if ((material.flags & STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) { + occlusion = textureSample(occlusion_texture, occlusion_sampler, uv).r; + } +#endif + } + out.deferred = vec4( + pack4x8unorm(output_color), + pack4x8unorm(vec4(emissive, 0.0)), + pack4x8unorm(vec4(metallic, perceptual_roughness, occlusion, reflectance)), + pack4x8unorm(vec4(0.0)), + ); +#endif // DEFERRED_PREPASS + return out; } #else diff --git a/examples/3d/load_gltf.rs b/examples/3d/load_gltf.rs index 33b1559907f0f..f53b798f97f60 100644 --- a/examples/3d/load_gltf.rs +++ b/examples/3d/load_gltf.rs @@ -6,15 +6,24 @@ use bevy::{ pbr::{CascadeShadowConfigBuilder, DirectionalLightShadowMap}, prelude::*, }; +use bevy_internal::{ + core_pipeline::{ + fxaa::Fxaa, + prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass}, + }, + pbr::deferred_lighting::DeferredLightingPlugin, +}; fn main() { App::new() + .insert_resource(Msaa::Off) .insert_resource(AmbientLight { color: Color::WHITE, brightness: 1.0 / 5.0f32, }) .insert_resource(DirectionalLightShadowMap { size: 4096 }) .add_plugins(DefaultPlugins) + .add_plugin(DeferredLightingPlugin) .add_systems(Startup, setup) .add_systems(Update, animate_light_direction) .run(); @@ -23,6 +32,10 @@ fn main() { fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(( Camera3dBundle { + camera: Camera { + hdr: true, + ..default() + }, transform: Transform::from_xyz(0.7, 0.7, 1.0) .looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y), ..default() @@ -31,6 +44,11 @@ fn setup(mut commands: Commands, asset_server: Res) { diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"), specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), }, + NormalPrepass, + DepthPrepass, + MotionVectorPrepass, + DeferredPrepass, + Fxaa::default(), )); commands.spawn(DirectionalLightBundle { From 52a38ebd5a938844a732df3013e0a0165f079aca Mon Sep 17 00:00:00 2001 From: DGriffin91 Date: Wed, 31 May 2023 15:41:15 -0700 Subject: [PATCH 005/138] remove temp file --- crates/bevy_pbr/src/deferred_lighting/temp.rs | 364 ------------------ 1 file changed, 364 deletions(-) delete mode 100644 crates/bevy_pbr/src/deferred_lighting/temp.rs diff --git a/crates/bevy_pbr/src/deferred_lighting/temp.rs b/crates/bevy_pbr/src/deferred_lighting/temp.rs deleted file mode 100644 index d9d4f9c5c3424..0000000000000 --- a/crates/bevy_pbr/src/deferred_lighting/temp.rs +++ /dev/null @@ -1,364 +0,0 @@ -// Recursive expansion of AsBindGroup macro -// ========================================= - -impl AsBindGroup for StandardMaterial { - type Data = StandardMaterialKey; - fn as_bind_group( - &self, - layout: &BindGroupLayout, - render_device: &bevy_render::renderer::RenderDevice, - images: &bevy_render::render_asset::RenderAssets, - fallback_image: &bevy_render::texture::FallbackImage, - ) -> Result, AsBindGroupError> { - let bindings = vec![ - { - use AsBindGroupShaderType; - let mut buffer = encase::UniformBuffer::new(Vec::new()); - let converted: StandardMaterialUniform = self.as_bind_group_shader_type(images); - buffer.write(&converted).unwrap(); - OwnedBindingResource::Buffer(render_device.create_buffer_with_data( - &BufferInitDescriptor { - label: None, - usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM, - contents: buffer.as_ref(), - }, - )) - }, - OwnedBindingResource::TextureView({ - let handle: = - (&self.base_color_texture).into(); - if let Some(handle) = handle { - images - .get(handle) - .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? - .texture_view - .clone() - } else { - fallback_image.texture_view.clone() - } - }), - OwnedBindingResource::Sampler({ - let handle: = - (&self.base_color_texture).into(); - if let Some(handle) = handle { - images - .get(handle) - .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? - .sampler - .clone() - } else { - fallback_image.sampler.clone() - } - }), - OwnedBindingResource::TextureView({ - let handle: = - (&self.emissive_texture).into(); - if let Some(handle) = handle { - images - .get(handle) - .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? - .texture_view - .clone() - } else { - fallback_image.texture_view.clone() - } - }), - OwnedBindingResource::Sampler({ - let handle: = - (&self.emissive_texture).into(); - if let Some(handle) = handle { - images - .get(handle) - .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? - .sampler - .clone() - } else { - fallback_image.sampler.clone() - } - }), - OwnedBindingResource::TextureView({ - let handle: = - (&self.metallic_roughness_texture).into(); - if let Some(handle) = handle { - images - .get(handle) - .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? - .texture_view - .clone() - } else { - fallback_image.texture_view.clone() - } - }), - OwnedBindingResource::Sampler({ - let handle: = - (&self.metallic_roughness_texture).into(); - if let Some(handle) = handle { - images - .get(handle) - .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? - .sampler - .clone() - } else { - fallback_image.sampler.clone() - } - }), - OwnedBindingResource::TextureView({ - let handle: = - (&self.normal_map_texture).into(); - if let Some(handle) = handle { - images - .get(handle) - .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? - .texture_view - .clone() - } else { - fallback_image.texture_view.clone() - } - }), - OwnedBindingResource::Sampler({ - let handle: = - (&self.normal_map_texture).into(); - if let Some(handle) = handle { - images - .get(handle) - .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? - .sampler - .clone() - } else { - fallback_image.sampler.clone() - } - }), - OwnedBindingResource::TextureView({ - let handle: = - (&self.occlusion_texture).into(); - if let Some(handle) = handle { - images - .get(handle) - .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? - .texture_view - .clone() - } else { - fallback_image.texture_view.clone() - } - }), - OwnedBindingResource::Sampler({ - let handle: = - (&self.occlusion_texture).into(); - if let Some(handle) = handle { - images - .get(handle) - .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? - .sampler - .clone() - } else { - fallback_image.sampler.clone() - } - }), - OwnedBindingResource::TextureView({ - let handle: = - (&self.depth_map).into(); - if let Some(handle) = handle { - images - .get(handle) - .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? - .texture_view - .clone() - } else { - fallback_image.texture_view.clone() - } - }), - OwnedBindingResource::Sampler({ - let handle: = - (&self.depth_map).into(); - if let Some(handle) = handle { - images - .get(handle) - .ok_or_else(|| AsBindGroupError::RetryNextUpdate)? - .sampler - .clone() - } else { - fallback_image.sampler.clone() - } - }), - ]; - let bind_group = { - let descriptor = BindGroupDescriptor { - entries: &[ - BindGroupEntry { - binding: 0u32, - resource: bindings[0usize].get_binding(), - }, - BindGroupEntry { - binding: 1u32, - resource: bindings[1usize].get_binding(), - }, - BindGroupEntry { - binding: 2u32, - resource: bindings[2usize].get_binding(), - }, - BindGroupEntry { - binding: 3u32, - resource: bindings[3usize].get_binding(), - }, - BindGroupEntry { - binding: 4u32, - resource: bindings[4usize].get_binding(), - }, - BindGroupEntry { - binding: 5u32, - resource: bindings[5usize].get_binding(), - }, - BindGroupEntry { - binding: 6u32, - resource: bindings[6usize].get_binding(), - }, - BindGroupEntry { - binding: 9u32, - resource: bindings[7usize].get_binding(), - }, - BindGroupEntry { - binding: 10u32, - resource: bindings[8usize].get_binding(), - }, - BindGroupEntry { - binding: 7u32, - resource: bindings[9usize].get_binding(), - }, - BindGroupEntry { - binding: 8u32, - resource: bindings[10usize].get_binding(), - }, - BindGroupEntry { - binding: 11u32, - resource: bindings[11usize].get_binding(), - }, - BindGroupEntry { - binding: 12u32, - resource: bindings[12usize].get_binding(), - }, - ], - label: None, - layout: &layout, - }; - render_device.create_bind_group(&descriptor) - }; - Ok(PreparedBindGroup { - bindings, - bind_group, - data: self.into(), - }) - } - fn bind_group_layout(render_device: &bevy_render::renderer::RenderDevice) -> BindGroupLayout { - render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { - entries: &[ - BindGroupLayoutEntry { - binding: 0u32, - visibility: ShaderStages::all(), - ty: BindingType::Buffer { - ty: BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: Some(::min_size()), - }, - count: None, - }, - BindGroupLayoutEntry { - binding: 1u32, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled: false, - sample_type: TextureSampleType::Float { filterable: true }, - view_dimension: TextureViewDimension::D2, - }, - count: None, - }, - BindGroupLayoutEntry { - binding: 2u32, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Sampler(SamplerBindingType::Filtering), - count: None, - }, - BindGroupLayoutEntry { - binding: 3u32, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled: false, - sample_type: TextureSampleType::Float { filterable: true }, - view_dimension: TextureViewDimension::D2, - }, - count: None, - }, - BindGroupLayoutEntry { - binding: 4u32, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Sampler(SamplerBindingType::Filtering), - count: None, - }, - BindGroupLayoutEntry { - binding: 5u32, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled: false, - sample_type: TextureSampleType::Float { filterable: true }, - view_dimension: TextureViewDimension::D2, - }, - count: None, - }, - BindGroupLayoutEntry { - binding: 6u32, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Sampler(SamplerBindingType::Filtering), - count: None, - }, - BindGroupLayoutEntry { - binding: 9u32, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled: false, - sample_type: TextureSampleType::Float { filterable: true }, - view_dimension: TextureViewDimension::D2, - }, - count: None, - }, - BindGroupLayoutEntry { - binding: 10u32, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Sampler(SamplerBindingType::Filtering), - count: None, - }, - BindGroupLayoutEntry { - binding: 7u32, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled: false, - sample_type: TextureSampleType::Float { filterable: true }, - view_dimension: TextureViewDimension::D2, - }, - count: None, - }, - BindGroupLayoutEntry { - binding: 8u32, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Sampler(SamplerBindingType::Filtering), - count: None, - }, - BindGroupLayoutEntry { - binding: 11u32, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled: false, - sample_type: TextureSampleType::Float { filterable: true }, - view_dimension: TextureViewDimension::D2, - }, - count: None, - }, - BindGroupLayoutEntry { - binding: 12u32, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Sampler(SamplerBindingType::Filtering), - count: None, - }, - ], - label: None, - }) - } -} From ea6ce6b09015e0183a6f534675754c23977cfd61 Mon Sep 17 00:00:00 2001 From: DGriffin91 Date: Wed, 31 May 2023 16:54:39 -0700 Subject: [PATCH 006/138] add dither and tonemap in shader --- .../deferred_lighting/deferred_lighting.wgsl | 2 +- crates/bevy_pbr/src/deferred_lighting/mod.rs | 80 ++++++++++++++++--- examples/3d/load_gltf.rs | 2 +- 3 files changed, 71 insertions(+), 13 deletions(-) diff --git a/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl b/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl index 8e0b81b0614a6..f46010f02f370 100644 --- a/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl +++ b/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl @@ -130,7 +130,7 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { #ifdef DEBAND_DITHER var output_rgb = output_color.rgb; output_rgb = powsafe(output_rgb, 1.0 / 2.2); - output_rgb = output_rgb + screen_space_dither(in.frag_coord.xy); + output_rgb = output_rgb + screen_space_dither(frag_coord.xy); // This conversion back to linear space is required because our output texture format is // SRGB; the GPU will assume our output is linear and will apply an SRGB conversion. output_rgb = powsafe(output_rgb, 2.2); diff --git a/crates/bevy_pbr/src/deferred_lighting/mod.rs b/crates/bevy_pbr/src/deferred_lighting/mod.rs index 43d4cbb3630e3..21ff9bd737748 100644 --- a/crates/bevy_pbr/src/deferred_lighting/mod.rs +++ b/crates/bevy_pbr/src/deferred_lighting/mod.rs @@ -6,7 +6,8 @@ use bevy_core_pipeline::{ prelude::{Camera3d, ClearColor}, prepass::{DeferredPrepass, ViewPrepassTextures}, tonemapping::{ - get_lut_bind_group_layout_entries, get_lut_bindings, Tonemapping, TonemappingLuts, + get_lut_bind_group_layout_entries, get_lut_bindings, DebandDither, Tonemapping, + TonemappingLuts, }, }; use bevy_ecs::{prelude::*, query::QueryItem}; @@ -112,7 +113,7 @@ impl ViewNode for DeferredLightingNode { view_uniform_offset, view_lights_offset, view_fog_offset, - mesh_bind_group, + mesh_view_bind_group, target, camera_3d, deferred_lighting_pipeline, @@ -143,7 +144,7 @@ impl ViewNode for DeferredLightingNode { render_pass.set_render_pipeline(pipeline); render_pass.set_bind_group( 0, - &mesh_bind_group.value, + &mesh_view_bind_group.value, &[ view_uniform_offset.offset, view_lights_offset.offset, @@ -172,7 +173,34 @@ impl SpecializedRenderPipeline for DeferredLightingLayout { fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { let mut shader_defs = Vec::new(); - // TODO insert tonemapping etc... + if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) { + shader_defs.push("TONEMAP_IN_SHADER".into()); + + let method = key.intersection(MeshPipelineKey::TONEMAP_METHOD_RESERVED_BITS); + + if method == MeshPipelineKey::TONEMAP_METHOD_NONE { + shader_defs.push("TONEMAP_METHOD_NONE".into()); + } else if method == MeshPipelineKey::TONEMAP_METHOD_REINHARD { + shader_defs.push("TONEMAP_METHOD_REINHARD".into()); + } else if method == MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE { + shader_defs.push("TONEMAP_METHOD_REINHARD_LUMINANCE".into()); + } else if method == MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED { + shader_defs.push("TONEMAP_METHOD_ACES_FITTED ".into()); + } else if method == MeshPipelineKey::TONEMAP_METHOD_AGX { + shader_defs.push("TONEMAP_METHOD_AGX".into()); + } else if method == MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM { + shader_defs.push("TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM".into()); + } else if method == MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC { + shader_defs.push("TONEMAP_METHOD_BLENDER_FILMIC".into()); + } else if method == MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE { + shader_defs.push("TONEMAP_METHOD_TONY_MC_MAPFACE".into()); + } + + // Debanding is tied to tonemapping in the shader, cannot run without it. + if key.contains(MeshPipelineKey::DEBAND_DITHER) { + shader_defs.push("DEBAND_DITHER".into()); + } + } shader_defs.push(ShaderDefVal::UInt( "MAX_DIRECTIONAL_LIGHTS".to_string(), @@ -231,14 +259,44 @@ pub fn prepare_deferred_lighting_pipelines( pipeline_cache: Res, mut pipelines: ResMut>, differed_lighting_pipeline: Res, - views: Query<(Entity, &ExtractedView), With>, + views: Query< + ( + Entity, + &ExtractedView, + Option<&Tonemapping>, + Option<&DebandDither>, + ), + With, + >, ) { - for (entity, view) in &views { - let pipeline_id = pipelines.specialize( - &pipeline_cache, - &differed_lighting_pipeline, - MeshPipelineKey::from_hdr(view.hdr), - ); + for (entity, view, tonemapping, dither) in &views { + let mut view_key = MeshPipelineKey::from_hdr(view.hdr); + + if !view.hdr { + if let Some(tonemapping) = tonemapping { + view_key |= MeshPipelineKey::TONEMAP_IN_SHADER; + view_key |= match tonemapping { + Tonemapping::None => MeshPipelineKey::TONEMAP_METHOD_NONE, + Tonemapping::Reinhard => MeshPipelineKey::TONEMAP_METHOD_REINHARD, + Tonemapping::ReinhardLuminance => { + MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE + } + Tonemapping::AcesFitted => MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED, + Tonemapping::AgX => MeshPipelineKey::TONEMAP_METHOD_AGX, + Tonemapping::SomewhatBoringDisplayTransform => { + MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM + } + Tonemapping::TonyMcMapface => MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE, + Tonemapping::BlenderFilmic => MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC, + }; + } + if let Some(DebandDither::Enabled) = dither { + view_key |= MeshPipelineKey::DEBAND_DITHER; + } + } + + let pipeline_id = + pipelines.specialize(&pipeline_cache, &differed_lighting_pipeline, view_key); commands .entity(entity) diff --git a/examples/3d/load_gltf.rs b/examples/3d/load_gltf.rs index f53b798f97f60..b6cc308d3f777 100644 --- a/examples/3d/load_gltf.rs +++ b/examples/3d/load_gltf.rs @@ -33,7 +33,7 @@ fn setup(mut commands: Commands, asset_server: Res) { commands.spawn(( Camera3dBundle { camera: Camera { - hdr: true, + //hdr: true, ..default() }, transform: Transform::from_xyz(0.7, 0.7, 1.0) From b6f06b0726e997b997d3282b4b6c31babd61b16c Mon Sep 17 00:00:00 2001 From: DGriffin91 Date: Wed, 31 May 2023 17:38:04 -0700 Subject: [PATCH 007/138] get forward and prepass without deferred working again --- crates/bevy_pbr/src/deferred_lighting/mod.rs | 14 ++++++-- crates/bevy_pbr/src/prepass/mod.rs | 16 ++++++--- crates/bevy_pbr/src/render/mesh.rs | 14 +++++--- crates/bevy_pbr/src/render/pbr.wgsl | 8 ++--- .../bevy_render/src/texture/fallback_image.rs | 33 +++++++++++++++++++ crates/bevy_render/src/texture/mod.rs | 1 + examples/3d/load_gltf.rs | 2 +- 7 files changed, 70 insertions(+), 18 deletions(-) diff --git a/crates/bevy_pbr/src/deferred_lighting/mod.rs b/crates/bevy_pbr/src/deferred_lighting/mod.rs index 21ff9bd737748..5aa6136dab419 100644 --- a/crates/bevy_pbr/src/deferred_lighting/mod.rs +++ b/crates/bevy_pbr/src/deferred_lighting/mod.rs @@ -17,7 +17,10 @@ use bevy_render::{ render_graph::{NodeRunError, RenderGraphContext, ViewNode, ViewNodeRunner}, render_resource::{BindGroupDescriptor, Operations, PipelineCache, RenderPassDescriptor}, renderer::RenderContext, - texture::{FallbackImageCubemap, FallbackImagesDepth, FallbackImagesMsaa, Image}, + texture::{ + FallbackImageCubemap, FallbackImageFormatMsaa, FallbackImagesDepth, FallbackImagesMsaa, + Image, + }, view::{Msaa, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms}, Render, RenderSet, }; @@ -479,13 +482,17 @@ pub fn queue_deferred_lighting_bind_groups( With, >, images: Res>, - mut fallback_images: FallbackImagesMsaa, - mut fallback_depths: FallbackImagesDepth, + fallback_images: ( + FallbackImagesMsaa, + FallbackImagesDepth, + FallbackImageFormatMsaa, + ), fallback_cubemap: Res, msaa: Res, globals_buffer: Res, tonemapping_luts: Res, ) { + let (mut fallback_images, mut fallback_depths, mut fallback_format_images) = fallback_images; if let ( Some(view_binding), Some(light_binding), @@ -585,6 +592,7 @@ pub fn queue_deferred_lighting_bind_groups( prepass_textures, &mut fallback_images, &mut fallback_depths, + &mut fallback_format_images, &msaa, [16, 17, 18, 19], )); diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 7a2966e8f0f85..1a7ef6fa40894 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -35,10 +35,11 @@ use bevy_render::{ PolygonMode, PrimitiveState, RenderPipelineDescriptor, Shader, ShaderDefVal, ShaderRef, ShaderStages, ShaderType, SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines, StencilFaceState, StencilState, TextureDescriptor, - TextureDimension, TextureSampleType, TextureUsages, TextureViewDimension, VertexState, + TextureDimension, TextureFormat, TextureSampleType, TextureUsages, TextureViewDimension, + VertexState, }, renderer::{RenderDevice, RenderQueue}, - texture::{FallbackImagesDepth, FallbackImagesMsaa, TextureCache}, + texture::{FallbackImageFormatMsaa, FallbackImagesDepth, FallbackImagesMsaa, TextureCache}, view::{ExtractedView, Msaa, ViewUniform, ViewUniformOffset, ViewUniforms, VisibleEntities}, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; @@ -613,6 +614,7 @@ pub fn get_bindings<'a>( prepass_textures: Option<&'a ViewPrepassTextures>, fallback_images: &'a mut FallbackImagesMsaa, fallback_depths: &'a mut FallbackImagesDepth, + fallback_format_images: &'a mut FallbackImageFormatMsaa, msaa: &'a Msaa, bindings: [u32; 4], ) -> [BindGroupEntry<'a>; 4] { @@ -631,17 +633,21 @@ pub fn get_bindings<'a>( let normal_view = match prepass_textures.and_then(|x| x.normal.as_ref()) { Some(texture) => &texture.default_view, - None => normal_motion_vectors_fallback, + None => &normal_motion_vectors_fallback, }; let motion_vectors_view = match prepass_textures.and_then(|x| x.motion_vectors.as_ref()) { Some(texture) => &texture.default_view, - None => normal_motion_vectors_fallback, + None => &normal_motion_vectors_fallback, }; + let deferred_fallback = &fallback_format_images + .image_for_samplecount(msaa.samples(), TextureFormat::Rgba32Uint) + .texture_view; + let deferred_view = match prepass_textures.and_then(|x| x.deferred.as_ref()) { Some(texture) => &texture.default_view, - None => normal_motion_vectors_fallback, + None => &deferred_fallback, }; [ diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 494857b169e5f..5e209b0d35904 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -33,8 +33,9 @@ use bevy_render::{ render_resource::*, renderer::{RenderDevice, RenderQueue}, texture::{ - BevyDefault, DefaultImageSampler, FallbackImageCubemap, FallbackImagesDepth, - FallbackImagesMsaa, GpuImage, Image, ImageSampler, TextureFormatPixelInfo, + BevyDefault, DefaultImageSampler, FallbackImageCubemap, FallbackImageFormatMsaa, + FallbackImagesDepth, FallbackImagesMsaa, GpuImage, Image, ImageSampler, + TextureFormatPixelInfo, }, view::{ComputedVisibility, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms}, Extract, ExtractSchedule, Render, RenderApp, RenderSet, @@ -983,13 +984,17 @@ pub fn queue_mesh_view_bind_groups( &Tonemapping, )>, images: Res>, - mut fallback_images: FallbackImagesMsaa, - mut fallback_depths: FallbackImagesDepth, + fallback_images: ( + FallbackImagesMsaa, + FallbackImagesDepth, + FallbackImageFormatMsaa, + ), fallback_cubemap: Res, msaa: Res, globals_buffer: Res, tonemapping_luts: Res, ) { + let (mut fallback_images, mut fallback_depths, mut fallback_format_images) = fallback_images; if let ( Some(view_binding), Some(light_binding), @@ -1089,6 +1094,7 @@ pub fn queue_mesh_view_bind_groups( prepass_textures, &mut fallback_images, &mut fallback_depths, + &mut fallback_format_images, &msaa, [16, 17, 18, 19], )); diff --git a/crates/bevy_pbr/src/render/pbr.wgsl b/crates/bevy_pbr/src/render/pbr.wgsl index 3bf8c6de0f73e..90c7508334e54 100644 --- a/crates/bevy_pbr/src/render/pbr.wgsl +++ b/crates/bevy_pbr/src/render/pbr.wgsl @@ -26,7 +26,6 @@ fn fragment(in: FragmentInput) -> @location(0) vec4 { let V = calculate_view(in.world_position, is_orthographic); #ifdef VERTEX_UVS var uv = in.uv; - let deferred_data = textureLoad(deferred_prepass_texture, vec2(in.frag_coord.xy), 0); #ifdef VERTEX_TANGENTS if ((material.flags & STANDARD_MATERIAL_FLAGS_DEPTH_MAP_BIT) != 0u) { let N = in.world_normal; @@ -52,10 +51,9 @@ fn fragment(in: FragmentInput) -> @location(0) vec4 { output_color = output_color * in.color; #endif #ifdef VERTEX_UVS - output_color = output_color * unpack4x8unorm(deferred_data.r); - //if ((material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) { - // output_color = output_color * textureSample(base_color_texture, base_color_sampler, uv); - //} + if ((material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) { + output_color = output_color * textureSample(base_color_texture, base_color_sampler, uv); + } #endif // VERTEX_UVS // NOTE: Unlit bit not set means == 0 is true, so the true case is if lit diff --git a/crates/bevy_render/src/texture/fallback_image.rs b/crates/bevy_render/src/texture/fallback_image.rs index dedb266343570..4b06a2bfaae96 100644 --- a/crates/bevy_render/src/texture/fallback_image.rs +++ b/crates/bevy_render/src/texture/fallback_image.rs @@ -161,6 +161,15 @@ pub struct FallbackImageMsaaCache(HashMap); #[derive(Resource, Deref, DerefMut, Default)] pub struct FallbackImageDepthCache(HashMap); +// TODO, tried combining caches but ran into lifetime issues when multiple fallback images are needed at once +/// A Cache of fallback textures that uses the sample count and TextureFormat as a key +/// +/// # WARNING +/// Images using MSAA with sample count > 1 are not initialized with data, therefore, +/// you shouldn't sample them before writing data to them first. +#[derive(Resource, Deref, DerefMut, Default)] +pub struct FallbackImageFormatMsaaCache(HashMap<(u32, TextureFormat), GpuImage>); + #[derive(SystemParam)] pub struct FallbackImagesMsaa<'w> { cache: ResMut<'w, FallbackImageMsaaCache>, @@ -208,3 +217,27 @@ impl<'w> FallbackImagesDepth<'w> { }) } } + +#[derive(SystemParam)] +pub struct FallbackImageFormatMsaa<'w> { + cache: ResMut<'w, FallbackImageFormatMsaaCache>, + render_device: Res<'w, RenderDevice>, + render_queue: Res<'w, RenderQueue>, + default_sampler: Res<'w, DefaultImageSampler>, +} + +impl<'w> FallbackImageFormatMsaa<'w> { + pub fn image_for_samplecount(&mut self, sample_count: u32, format: TextureFormat) -> &GpuImage { + self.cache.entry((sample_count, format)).or_insert_with(|| { + fallback_image_new( + &self.render_device, + &self.render_queue, + &self.default_sampler, + format, + TextureViewDimension::D2, + sample_count, + 255, + ) + }) + } +} diff --git a/crates/bevy_render/src/texture/mod.rs b/crates/bevy_render/src/texture/mod.rs index bba263beaa499..fac5aea0b6a03 100644 --- a/crates/bevy_render/src/texture/mod.rs +++ b/crates/bevy_render/src/texture/mod.rs @@ -123,6 +123,7 @@ impl Plugin for ImagePlugin { .init_resource::() .init_resource::() .init_resource::() + .init_resource::() .init_resource::(); } } diff --git a/examples/3d/load_gltf.rs b/examples/3d/load_gltf.rs index b6cc308d3f777..e2f7474843bc9 100644 --- a/examples/3d/load_gltf.rs +++ b/examples/3d/load_gltf.rs @@ -47,7 +47,7 @@ fn setup(mut commands: Commands, asset_server: Res) { NormalPrepass, DepthPrepass, MotionVectorPrepass, - DeferredPrepass, + //DeferredPrepass, Fxaa::default(), )); From 5f1d8fc9b7cc7bef0962f44480227baa0e57e153 Mon Sep 17 00:00:00 2001 From: DGriffin91 Date: Wed, 31 May 2023 18:12:16 -0700 Subject: [PATCH 008/138] get shadows working --- .../src/deferred_lighting/deferred_lighting.wgsl | 12 ++++++------ crates/bevy_pbr/src/deferred_lighting/mod.rs | 16 +++++++++++++++- crates/bevy_pbr/src/render/pbr_prepass.wgsl | 2 +- examples/3d/load_gltf.rs | 2 +- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl b/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl index f46010f02f370..eced22faa8b7b 100644 --- a/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl +++ b/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl @@ -85,17 +85,18 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { let depth = prepass_depth(in.position, 0u); let frag_coord = vec4(in.position.xy, depth, 0.0); - let world_position = position_ndc_to_world(frag_coord_to_ndc(frag_coord)); + let world_position = vec4(position_ndc_to_world(frag_coord_to_ndc(frag_coord)), 1.0); let is_orthographic = view.projection[3].w == 1.0; - let V = calculate_view(vec4(world_position, 0.0), is_orthographic); + let V = calculate_view(world_position, is_orthographic); let deferred_data = textureLoad(deferred_prepass_texture, vec2(frag_coord.xy), 0); let base_color = unpack4x8unorm(deferred_data.r); let emissive = unpack4x8unorm(deferred_data.g); let misc = unpack4x8unorm(deferred_data.b); + let flags = deferred_data.a; let metallic = misc.r; let perceptual_roughness = misc.g; let occlusion = misc.b; @@ -103,6 +104,7 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { var pbr_input = pbr_input_new(); + pbr_input.flags = flags; pbr_input.material.base_color = base_color; pbr_input.material.emissive = emissive; @@ -117,11 +119,10 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { pbr_input.frag_coord = frag_coord; pbr_input.world_normal = prepass_normal(frag_coord, 0u); - pbr_input.world_position = vec4(world_position, 0.0); + pbr_input.world_position = world_position; pbr_input.N = pbr_input.world_normal; pbr_input.V = V; pbr_input.is_orthographic = is_orthographic; - var output_color = pbr(pbr_input); @@ -141,7 +142,6 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { output_color = premultiply_alpha(material.flags, output_color); #endif - - return output_color;//textureSample(screen_texture, texture_sampler, in.uv); + return output_color; } diff --git a/crates/bevy_pbr/src/deferred_lighting/mod.rs b/crates/bevy_pbr/src/deferred_lighting/mod.rs index 5aa6136dab419..6fc3414c657d6 100644 --- a/crates/bevy_pbr/src/deferred_lighting/mod.rs +++ b/crates/bevy_pbr/src/deferred_lighting/mod.rs @@ -205,6 +205,10 @@ impl SpecializedRenderPipeline for DeferredLightingLayout { } } + if key.contains(MeshPipelineKey::ENVIRONMENT_MAP) { + shader_defs.push("ENVIRONMENT_MAP".into()); + } + shader_defs.push(ShaderDefVal::UInt( "MAX_DIRECTIONAL_LIGHTS".to_string(), MAX_DIRECTIONAL_LIGHTS as u32, @@ -268,11 +272,13 @@ pub fn prepare_deferred_lighting_pipelines( &ExtractedView, Option<&Tonemapping>, Option<&DebandDither>, + Option<&EnvironmentMapLight>, ), With, >, + images: Res>, ) { - for (entity, view, tonemapping, dither) in &views { + for (entity, view, tonemapping, dither, environment_map) in &views { let mut view_key = MeshPipelineKey::from_hdr(view.hdr); if !view.hdr { @@ -298,6 +304,14 @@ pub fn prepare_deferred_lighting_pipelines( } } + let environment_map_loaded = match environment_map { + Some(environment_map) => environment_map.is_loaded(&images), + None => false, + }; + if environment_map_loaded { + view_key |= MeshPipelineKey::ENVIRONMENT_MAP; + } + let pipeline_id = pipelines.specialize(&pipeline_cache, &differed_lighting_pipeline, view_key); diff --git a/crates/bevy_pbr/src/render/pbr_prepass.wgsl b/crates/bevy_pbr/src/render/pbr_prepass.wgsl index e7abef2b2fc02..bffc76f003925 100644 --- a/crates/bevy_pbr/src/render/pbr_prepass.wgsl +++ b/crates/bevy_pbr/src/render/pbr_prepass.wgsl @@ -164,7 +164,7 @@ fn fragment(in: FragmentInput) -> FragmentOutput { pack4x8unorm(output_color), pack4x8unorm(vec4(emissive, 0.0)), pack4x8unorm(vec4(metallic, perceptual_roughness, occlusion, reflectance)), - pack4x8unorm(vec4(0.0)), + material.flags, ); #endif // DEFERRED_PREPASS diff --git a/examples/3d/load_gltf.rs b/examples/3d/load_gltf.rs index e2f7474843bc9..b6cc308d3f777 100644 --- a/examples/3d/load_gltf.rs +++ b/examples/3d/load_gltf.rs @@ -47,7 +47,7 @@ fn setup(mut commands: Commands, asset_server: Res) { NormalPrepass, DepthPrepass, MotionVectorPrepass, - //DeferredPrepass, + DeferredPrepass, Fxaa::default(), )); From 30c8ed8133f1b8576d4614514551ccb96e94b3c5 Mon Sep 17 00:00:00 2001 From: DGriffin91 Date: Wed, 31 May 2023 18:41:38 -0700 Subject: [PATCH 009/138] use rgb9e5 for emissive --- .../deferred_lighting/deferred_lighting.wgsl | 3 +- crates/bevy_pbr/src/lib.rs | 8 +++ crates/bevy_pbr/src/render/pbr_prepass.wgsl | 3 +- crates/bevy_pbr/src/render/rgb9e5.wgsl | 72 +++++++++++++++++++ 4 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 crates/bevy_pbr/src/render/rgb9e5.wgsl diff --git a/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl b/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl index eced22faa8b7b..48051f0592155 100644 --- a/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl +++ b/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl @@ -3,6 +3,7 @@ #import bevy_pbr::mesh_types #import bevy_pbr::mesh_view_bindings +#import bevy_pbr::rgb9e5 #import bevy_pbr::pbr_types #import bevy_pbr::utils #import bevy_pbr::clustered_forward @@ -94,7 +95,7 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { let deferred_data = textureLoad(deferred_prepass_texture, vec2(frag_coord.xy), 0); let base_color = unpack4x8unorm(deferred_data.r); - let emissive = unpack4x8unorm(deferred_data.g); + let emissive = vec4(rgb9e5_to_float3(deferred_data.g), 1.0); let misc = unpack4x8unorm(deferred_data.b); let flags = deferred_data.a; let metallic = misc.r; diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 0f710f69942eb..87b69bcbefc25 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -84,6 +84,8 @@ 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 RGB9E5_FUNCTIONS_HANDLE: HandleUntyped = + HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2659010996143919192); pub const PBR_AMBIENT_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2441520459096337034); pub const PARALLAX_MAPPING_SHADER_HANDLE: HandleUntyped = @@ -143,6 +145,12 @@ impl Plugin for PbrPlugin { "render/pbr_functions.wgsl", Shader::from_wgsl ); + load_internal_asset!( + app, + RGB9E5_FUNCTIONS_HANDLE, + "render/rgb9e5.wgsl", + Shader::from_wgsl + ); load_internal_asset!( app, PBR_AMBIENT_HANDLE, diff --git a/crates/bevy_pbr/src/render/pbr_prepass.wgsl b/crates/bevy_pbr/src/render/pbr_prepass.wgsl index bffc76f003925..ace44e939e15d 100644 --- a/crates/bevy_pbr/src/render/pbr_prepass.wgsl +++ b/crates/bevy_pbr/src/render/pbr_prepass.wgsl @@ -1,5 +1,6 @@ #import bevy_pbr::prepass_bindings #import bevy_pbr::pbr_bindings +#import bevy_pbr::rgb9e5 #ifdef NORMAL_PREPASS #import bevy_pbr::pbr_functions #endif // NORMAL_PREPASS @@ -162,7 +163,7 @@ fn fragment(in: FragmentInput) -> FragmentOutput { } out.deferred = vec4( pack4x8unorm(output_color), - pack4x8unorm(vec4(emissive, 0.0)), + float3_to_rgb9e5(emissive), pack4x8unorm(vec4(metallic, perceptual_roughness, occlusion, reflectance)), material.flags, ); diff --git a/crates/bevy_pbr/src/render/rgb9e5.wgsl b/crates/bevy_pbr/src/render/rgb9e5.wgsl new file mode 100644 index 0000000000000..74598de70f806 --- /dev/null +++ b/crates/bevy_pbr/src/render/rgb9e5.wgsl @@ -0,0 +1,72 @@ +#define_import_path bevy_pbr::rgb9e5 + +// https://github.com/EmbarkStudios/kajiya/blob/d3b6ac22c5306cc9d3ea5e2d62fd872bea58d8d6/assets/shaders/inc/pack_unpack.hlsl#L101 +const RGB9E5_EXPONENT_BITS = 5u; +const RGB9E5_MANTISSA_BITS = 9; +const RGB9E5_MANTISSA_BITSU = 9u; +const RGB9E5_EXP_BIAS = 15; +const RGB9E5_MAX_VALID_BIASED_EXP = 31u; + +//#define MAX_RGB9E5_EXP (RGB9E5_MAX_VALID_BIASED_EXP - RGB9E5_EXP_BIAS) +//#define RGB9E5_MANTISSA_VALUES (1< f32 { + return clamp(x, 0.0, MAX_RGB9E5); +} + +fn floor_log2(x: f32) -> i32 { + let f = bitcast(x); + let biasedexponent = (f & 0x7F800000u) >> 23u; + return i32(biasedexponent) - 127; +} + +// https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_shared_exponent.txt +fn float3_to_rgb9e5(rgb: vec3) -> u32 { + let rc = clamp_range_for_rgb9e5(rgb.x); + let gc = clamp_range_for_rgb9e5(rgb.y); + let bc = clamp_range_for_rgb9e5(rgb.z); + + let maxrgb = max(rc, max(gc, bc)); + var exp_shared = max(-RGB9E5_EXP_BIAS - 1, floor_log2(maxrgb)) + 1 + RGB9E5_EXP_BIAS; + var denom = exp2(f32(exp_shared - RGB9E5_EXP_BIAS - RGB9E5_MANTISSA_BITS)); + + let maxm = i32(floor(maxrgb / denom + 0.5)); + if (maxm == MAX_RGB9E5_MANTISSA + 1) { + denom *= 2.0; + exp_shared += 1; + } + + let rm = i32(floor(rc / denom + 0.5)); + let gm = i32(floor(gc / denom + 0.5)); + let bm = i32(floor(bc / denom + 0.5)); + + return (u32(rm) << (32u - 9u)) + | (u32(gm) << (32u - 9u * 2u)) + | (u32(bm) << (32u - 9u * 3u)) + | u32(exp_shared); +} + +fn bitfield_extract(value: u32, offset: u32, bits: u32) -> u32 { + let mask = (1u << bits) - 1u; + return (value >> offset) & mask; +} + +fn rgb9e5_to_float3(v: u32) -> vec3 { + let exponent = i32(bitfield_extract(v, 0u, RGB9E5_EXPONENT_BITS)) - RGB9E5_EXP_BIAS - RGB9E5_MANTISSA_BITS; + let scale = exp2(f32(exponent)); + + return vec3( + f32(bitfield_extract(v, 32u - RGB9E5_MANTISSA_BITSU, RGB9E5_MANTISSA_BITSU)) * scale, + f32(bitfield_extract(v, 32u - RGB9E5_MANTISSA_BITSU * 2u, RGB9E5_MANTISSA_BITSU)) * scale, + f32(bitfield_extract(v, 32u - RGB9E5_MANTISSA_BITSU * 3u, RGB9E5_MANTISSA_BITSU)) * scale + ); +} \ No newline at end of file From 764205f403f8c98cfe5176af060a63484fedf38b Mon Sep 17 00:00:00 2001 From: DGriffin91 Date: Thu, 1 Jun 2023 02:47:58 -0700 Subject: [PATCH 010/138] make deferred and forward work together --- Cargo.toml | 10 ++ .../src/core_3d/main_opaque_pass_3d_node.rs | 27 +-- crates/bevy_core_pipeline/src/core_3d/mod.rs | 4 + crates/bevy_core_pipeline/src/deferred/mod.rs | 102 +++++++++++ .../bevy_core_pipeline/src/deferred/node.rs | 168 ++++++++++++++++++ crates/bevy_core_pipeline/src/lib.rs | 1 + crates/bevy_core_pipeline/src/prepass/mod.rs | 1 - crates/bevy_core_pipeline/src/prepass/node.rs | 36 ++-- .../deferred_lighting/deferred_lighting.wgsl | 9 +- crates/bevy_pbr/src/deferred_lighting/mod.rs | 2 +- crates/bevy_pbr/src/lib.rs | 3 + crates/bevy_pbr/src/material.rs | 75 ++++++-- crates/bevy_pbr/src/pbr_material.rs | 13 +- crates/bevy_pbr/src/prepass/mod.rs | 136 ++++++++++---- crates/bevy_pbr/src/render/mesh.rs | 4 - crates/bevy_pbr/src/render/pbr_prepass.wgsl | 2 +- examples/3d/deferred_rendering.rs | 119 +++++++++++++ examples/3d/load_gltf.rs | 18 -- 18 files changed, 621 insertions(+), 109 deletions(-) create mode 100644 crates/bevy_core_pipeline/src/deferred/mod.rs create mode 100644 crates/bevy_core_pipeline/src/deferred/node.rs create mode 100644 examples/3d/deferred_rendering.rs diff --git a/Cargo.toml b/Cargo.toml index 70817549915b7..3a372f3422b22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -537,6 +537,16 @@ description = "Illustrates bloom configuration using HDR and emissive materials" category = "3D Rendering" wasm = true +[[example]] +name = "deferred_rendering" +path = "examples/3d/deferred_rendering.rs" + +[package.metadata.example.deferred_rendering] +name = "Deferred Rendering" +description = "Renders meshes with both forward and deferred piplines" +category = "3D Rendering" +wasm = true + [[example]] name = "load_gltf" path = "examples/3d/load_gltf.rs" diff --git a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs index c54a2e8b02845..4a092ab9b61c2 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs @@ -61,9 +61,17 @@ impl ViewNode for MainOpaquePass3dNode { ): QueryItem, world: &World, ) -> Result<(), NodeRunError> { - if deferred_prepass.is_some() { - return Ok(()); // TODO allow forward materials - } + let load = if deferred_prepass.is_none() { + match camera_3d.clear_color { + ClearColorConfig::Default => LoadOp::Clear(world.resource::().0.into()), + ClearColorConfig::Custom(color) => LoadOp::Clear(color.into()), + ClearColorConfig::None => LoadOp::Load, + } + } else { + // If the deferred pass has run, don't clear again in this pass. + LoadOp::Load + }; + // Run the opaque pass, sorted front-to-back // NOTE: Scoped to drop the mutable borrow of render_context #[cfg(feature = "trace")] @@ -74,16 +82,9 @@ impl ViewNode for MainOpaquePass3dNode { label: Some("main_opaque_pass_3d"), // NOTE: The opaque pass loads the color // buffer as well as writing to it. - color_attachments: &[Some(target.get_color_attachment(Operations { - load: match camera_3d.clear_color { - ClearColorConfig::Default => { - LoadOp::Clear(world.resource::().0.into()) - } - ClearColorConfig::Custom(color) => LoadOp::Clear(color.into()), - ClearColorConfig::None => LoadOp::Load, - }, - store: true, - }))], + color_attachments: &[Some( + target.get_color_attachment(Operations { load, store: true }), + )], depth_stencil_attachment: Some(RenderPassDepthStencilAttachment { view: &depth.view, // NOTE: The opaque main pass loads the depth buffer and possibly overwrites it diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index ef8b16c5a6791..b9afcdf917954 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -10,6 +10,7 @@ pub mod graph { pub mod node { pub const MSAA_WRITEBACK: &str = "msaa_writeback"; pub const PREPASS: &str = "prepass"; + pub const DEFERRED: &str = "deferred"; pub const START_MAIN_PASS: &str = "start_main_pass"; pub const MAIN_OPAQUE_PASS: &str = "main_opaque_pass"; pub const MAIN_TRANSPARENT_PASS: &str = "main_transparent_pass"; @@ -53,6 +54,7 @@ use bevy_render::{ use bevy_utils::{FloatOrd, HashMap}; use crate::{ + deferred::node::DeferredNode, prepass::{node::PrepassNode, DepthPrepass}, skybox::SkyboxPlugin, tonemapping::TonemappingNode, @@ -94,6 +96,7 @@ impl Plugin for Core3dPlugin { render_app .add_render_sub_graph(CORE_3D) .add_render_graph_node::>(CORE_3D, PREPASS) + .add_render_graph_node::>(CORE_3D, DEFERRED) .add_render_graph_node::(CORE_3D, START_MAIN_PASS) .add_render_graph_node::>( CORE_3D, @@ -111,6 +114,7 @@ impl Plugin for Core3dPlugin { CORE_3D, &[ PREPASS, + DEFERRED, START_MAIN_PASS, MAIN_OPAQUE_PASS, MAIN_TRANSPARENT_PASS, diff --git a/crates/bevy_core_pipeline/src/deferred/mod.rs b/crates/bevy_core_pipeline/src/deferred/mod.rs new file mode 100644 index 0000000000000..1156205b55811 --- /dev/null +++ b/crates/bevy_core_pipeline/src/deferred/mod.rs @@ -0,0 +1,102 @@ +pub mod node; + +use std::cmp::Reverse; + +use bevy_ecs::prelude::*; +use bevy_render::{ + render_phase::{CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem}, + render_resource::{CachedRenderPipelineId, TextureFormat}, +}; +use bevy_utils::FloatOrd; + +pub const DEFERRED_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgba32Uint; + +/// Opaque phase of the 3D Deferred pass. +/// +/// Sorted front-to-back by the z-distance in front of the camera. +/// +/// Used to render all 3D meshes with materials that have no transparency. +pub struct Opaque3dDeferred { + pub distance: f32, + pub entity: Entity, + pub pipeline_id: CachedRenderPipelineId, + pub draw_function: DrawFunctionId, +} + +impl PhaseItem for Opaque3dDeferred { + // NOTE: Values increase towards the camera. Front-to-back ordering for opaque means we need a descending sort. + type SortKey = Reverse; + + #[inline] + fn entity(&self) -> Entity { + self.entity + } + + #[inline] + fn sort_key(&self) -> Self::SortKey { + Reverse(FloatOrd(self.distance)) + } + + #[inline] + fn draw_function(&self) -> DrawFunctionId { + self.draw_function + } + + #[inline] + fn sort(items: &mut [Self]) { + // Key negated to match reversed SortKey ordering + radsort::sort_by_key(items, |item| -item.distance); + } +} + +impl CachedRenderPipelinePhaseItem for Opaque3dDeferred { + #[inline] + fn cached_pipeline(&self) -> CachedRenderPipelineId { + self.pipeline_id + } +} + +/// Alpha mask phase of the 3D Deferred pass. +/// +/// Sorted front-to-back by the z-distance in front of the camera. +/// +/// Used to render all meshes with a material with an alpha mask. +pub struct AlphaMask3dDeferred { + pub distance: f32, + pub entity: Entity, + pub pipeline_id: CachedRenderPipelineId, + pub draw_function: DrawFunctionId, +} + +impl PhaseItem for AlphaMask3dDeferred { + // NOTE: Values increase towards the camera. Front-to-back ordering for alpha mask means we need a descending sort. + type SortKey = Reverse; + + #[inline] + fn entity(&self) -> Entity { + self.entity + } + + #[inline] + fn sort_key(&self) -> Self::SortKey { + Reverse(FloatOrd(self.distance)) + } + + #[inline] + fn draw_function(&self) -> DrawFunctionId { + self.draw_function + } + + #[inline] + fn sort(items: &mut [Self]) { + // Key negated to match reversed SortKey ordering + radsort::sort_by_key(items, |item| -item.distance); + } +} + +impl CachedRenderPipelinePhaseItem for AlphaMask3dDeferred { + #[inline] + fn cached_pipeline(&self) -> CachedRenderPipelineId { + self.pipeline_id + } +} diff --git a/crates/bevy_core_pipeline/src/deferred/node.rs b/crates/bevy_core_pipeline/src/deferred/node.rs new file mode 100644 index 0000000000000..25fb50a44629e --- /dev/null +++ b/crates/bevy_core_pipeline/src/deferred/node.rs @@ -0,0 +1,168 @@ +use bevy_ecs::prelude::*; +use bevy_ecs::query::QueryItem; +use bevy_render::render_graph::ViewNode; +use bevy_render::{ + camera::ExtractedCamera, + prelude::Color, + render_graph::{NodeRunError, RenderGraphContext}, + render_phase::RenderPhase, + render_resource::{ + LoadOp, Operations, RenderPassColorAttachment, RenderPassDepthStencilAttachment, + RenderPassDescriptor, + }, + renderer::RenderContext, + view::ViewDepthTexture, +}; +#[cfg(feature = "trace")] +use bevy_utils::tracing::info_span; + +use crate::core_3d::{Camera3d, Camera3dDepthLoadOp}; +use crate::prepass::{DepthPrepass, MotionVectorPrepass, NormalPrepass, ViewPrepassTextures}; + +use super::{AlphaMask3dDeferred, Opaque3dDeferred}; + +/// Render node used by the prepass. +/// +/// By default, inserted before the main pass in the render graph. +#[derive(Default)] +pub struct DeferredNode; + +impl ViewNode for DeferredNode { + type ViewQuery = ( + &'static ExtractedCamera, + &'static RenderPhase, + &'static RenderPhase, + &'static ViewDepthTexture, + &'static ViewPrepassTextures, + &'static Camera3d, + Option<&'static DepthPrepass>, + Option<&'static NormalPrepass>, + Option<&'static MotionVectorPrepass>, + ); + + fn run( + &self, + graph: &mut RenderGraphContext, + render_context: &mut RenderContext, + ( + camera, + opaque_deferred_phase, + alpha_mask_deferred_phase, + view_depth_texture, + view_prepass_textures, + camera_3d, + depth_prepass, + normal_prepass, + motion_vector_prepass, + ): QueryItem, + world: &World, + ) -> Result<(), NodeRunError> { + let view_entity = graph.view_entity(); + + let mut color_attachments = vec![]; + color_attachments.push( + view_prepass_textures + .normal + .as_ref() + .map(|view_normals_texture| RenderPassColorAttachment { + view: &view_normals_texture.default_view, + resolve_target: None, + ops: Operations { + load: if normal_prepass.is_some() { + LoadOp::Load // load if the normal_prepass has run + } else { + LoadOp::Clear(Color::BLACK.into()) + }, + store: true, + }, + }), + ); + color_attachments.push(view_prepass_textures.motion_vectors.as_ref().map( + |view_motion_vectors_texture| RenderPassColorAttachment { + view: &view_motion_vectors_texture.default_view, + resolve_target: None, + ops: Operations { + load: if motion_vector_prepass.is_some() { + LoadOp::Load // load if the motion_vector_prepass has run + } else { + LoadOp::Clear(Color::rgb_linear(0.0, 0.0, 0.0).into()) + }, + store: true, + }, + }, + )); + color_attachments.push( + view_prepass_textures + .deferred + .as_ref() + .map(|deferred_texture| RenderPassColorAttachment { + view: &deferred_texture.default_view, + resolve_target: None, + ops: Operations { + load: LoadOp::Clear(Color::rgba_linear(0.0, 0.0, 0.0, 0.0).into()), + store: true, + }, + }), + ); + if color_attachments.iter().all(Option::is_none) { + // all attachments are none: clear the attachment list so that no fragment shader is required + color_attachments.clear(); + } + + { + // Set up the pass descriptor with the depth attachment and optional color attachments + let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor { + label: Some("deferred"), + color_attachments: &color_attachments, + depth_stencil_attachment: Some(RenderPassDepthStencilAttachment { + view: &view_depth_texture.view, + depth_ops: Some(Operations { + load: if depth_prepass.is_some() + || normal_prepass.is_some() + || motion_vector_prepass.is_some() + { + // if any prepass runs, it will generate a depth buffer so we should use it + Camera3dDepthLoadOp::Load + } else { + // NOTE: 0.0 is the far plane due to bevy's use of reverse-z projections. + camera_3d.depth_load_op.clone() + } + .into(), + store: true, + }), + stencil_ops: None, + }), + }); + + if let Some(viewport) = camera.viewport.as_ref() { + render_pass.set_camera_viewport(viewport); + } + + // Always run opaque pass to ensure screen is cleared + { + // Run the prepass, sorted front-to-back + #[cfg(feature = "trace")] + let _opaque_prepass_span = info_span!("opaque_deferred").entered(); + opaque_deferred_phase.render(&mut render_pass, world, view_entity); + } + + if !alpha_mask_deferred_phase.items.is_empty() { + // Run the deferred, sorted front-to-back + #[cfg(feature = "trace")] + let _alpha_mask_deferred_span = info_span!("alpha_mask_deferred").entered(); + alpha_mask_deferred_phase.render(&mut render_pass, world, view_entity); + } + } + + 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(), + prepass_depth_texture.texture.as_image_copy(), + view_prepass_textures.size, + ); + } + + Ok(()) + } +} diff --git a/crates/bevy_core_pipeline/src/lib.rs b/crates/bevy_core_pipeline/src/lib.rs index f169cdd89e08d..3c9320a81e1b7 100644 --- a/crates/bevy_core_pipeline/src/lib.rs +++ b/crates/bevy_core_pipeline/src/lib.rs @@ -6,6 +6,7 @@ pub mod clear_color; pub mod contrast_adaptive_sharpening; pub mod core_2d; pub mod core_3d; +pub mod deferred; pub mod fullscreen_vertex_shader; pub mod fxaa; pub mod msaa_writeback; diff --git a/crates/bevy_core_pipeline/src/prepass/mod.rs b/crates/bevy_core_pipeline/src/prepass/mod.rs index d4721118e4f7f..af637a3588e5a 100644 --- a/crates/bevy_core_pipeline/src/prepass/mod.rs +++ b/crates/bevy_core_pipeline/src/prepass/mod.rs @@ -41,7 +41,6 @@ use bevy_utils::FloatOrd; pub const DEPTH_PREPASS_FORMAT: TextureFormat = TextureFormat::Depth32Float; pub const NORMAL_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgb10a2Unorm; pub const MOTION_VECTOR_PREPASS_FORMAT: TextureFormat = TextureFormat::Rg16Float; -pub const DEFERRED_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgba32Uint; /// If added to a [`crate::prelude::Camera3d`] then depth values will be copied to a separate texture available to the main pass. #[derive(Component, Default, Reflect)] diff --git a/crates/bevy_core_pipeline/src/prepass/node.rs b/crates/bevy_core_pipeline/src/prepass/node.rs index f7f8c750ba40e..662e1517201e4 100644 --- a/crates/bevy_core_pipeline/src/prepass/node.rs +++ b/crates/bevy_core_pipeline/src/prepass/node.rs @@ -16,7 +16,7 @@ use bevy_render::{ #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; -use super::{AlphaMask3dPrepass, Opaque3dPrepass, ViewPrepassTextures}; +use super::{AlphaMask3dPrepass, DeferredPrepass, Opaque3dPrepass, ViewPrepassTextures}; /// Render node used by the prepass. /// @@ -31,6 +31,7 @@ impl ViewNode for PrepassNode { &'static RenderPhase, &'static ViewDepthTexture, &'static ViewPrepassTextures, + Option<&'static DeferredPrepass>, ); fn run( @@ -43,6 +44,7 @@ impl ViewNode for PrepassNode { alpha_mask_prepass_phase, view_depth_texture, view_prepass_textures, + deferred_prepass, ): QueryItem, world: &World, ) -> Result<(), NodeRunError> { @@ -75,19 +77,6 @@ impl ViewNode for PrepassNode { }, }, )); - color_attachments.push( - view_prepass_textures - .deferred - .as_ref() - .map(|deferred_texture| RenderPassColorAttachment { - view: &deferred_texture.default_view, - resolve_target: None, - ops: Operations { - load: LoadOp::Clear(Color::rgba_linear(0.0, 0.0, 0.0, 0.0).into()), - store: true, - }, - }), - ); if color_attachments.iter().all(Option::is_none) { // all attachments are none: clear the attachment list so that no fragment shader is required color_attachments.clear(); @@ -127,16 +116,17 @@ impl ViewNode for PrepassNode { alpha_mask_prepass_phase.render(&mut render_pass, world, view_entity); } } - - 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(), - prepass_depth_texture.texture.as_image_copy(), - view_prepass_textures.size, - ); + if deferred_prepass.is_none() { + // copy if deferred isn't going to + 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(), + prepass_depth_texture.texture.as_image_copy(), + view_prepass_textures.size, + ); + } } - Ok(()) } } diff --git a/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl b/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl index 48051f0592155..99ce14b09bff2 100644 --- a/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl +++ b/crates/bevy_pbr/src/deferred_lighting/deferred_lighting.wgsl @@ -84,6 +84,13 @@ fn frag_coord_to_ndc(frag_coord: vec4) -> vec3 { @fragment fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { let depth = prepass_depth(in.position, 0u); + + // TODO Griffin also discard based on flags if this frag shouldn't be lit by this pass + if depth == 0.0 { + // if depth is inf then discard, leaves clear color + discard; + } + let frag_coord = vec4(in.position.xy, depth, 0.0); let world_position = vec4(position_ndc_to_world(frag_coord_to_ndc(frag_coord)), 1.0); @@ -112,7 +119,7 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { pbr_input.material.perceptual_roughness = perceptual_roughness; pbr_input.material.metallic = metallic; pbr_input.material.reflectance = reflectance; - //pbr_input.material.flags = + //pbr_input.material.flags = // TODO Griffin include material flags pbr_input.material.alpha_cutoff = 0.5; pbr_input.material.parallax_depth_scale = 0.1; //default pbr_input.material.max_parallax_layer_count = 16.0; //default diff --git a/crates/bevy_pbr/src/deferred_lighting/mod.rs b/crates/bevy_pbr/src/deferred_lighting/mod.rs index 6fc3414c657d6..7b8b75937c552 100644 --- a/crates/bevy_pbr/src/deferred_lighting/mod.rs +++ b/crates/bevy_pbr/src/deferred_lighting/mod.rs @@ -74,7 +74,7 @@ impl Plugin for DeferredLightingPlugin { .add_render_graph_edges( core_3d::graph::NAME, &[ - core_3d::graph::node::PREPASS, + core_3d::graph::node::DEFERRED, DeferredLightingNode::NAME, core_3d::graph::node::START_MAIN_PASS, ], diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 87b69bcbefc25..ce6af27466545 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -66,6 +66,8 @@ use bevy_render::{ use bevy_transform::TransformSystem; use environment_map::EnvironmentMapPlugin; +use crate::deferred_lighting::DeferredLightingPlugin; + pub const PBR_TYPES_SHADER_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 1708015359337029744); pub const PBR_BINDINGS_SHADER_HANDLE: HandleUntyped = @@ -192,6 +194,7 @@ impl Plugin for PbrPlugin { ..Default::default() }) .add_plugin(EnvironmentMapPlugin) + .add_plugin(DeferredLightingPlugin) .init_resource::() .init_resource::() .init_resource::() diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 29815775d10d3..8bb6888e26b8d 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -19,9 +19,10 @@ use bevy_ecs::{ SystemParamItem, }, }; -use bevy_reflect::TypeUuid; +use bevy_reflect::{FromReflect, Reflect, TypeUuid}; use bevy_render::{ extract_component::ExtractComponentPlugin, + extract_resource::{ExtractResource, ExtractResourcePlugin}, mesh::{Mesh, MeshVertexBufferLayout}, prelude::Image, render_asset::{PrepareAssetSet, RenderAssets}, @@ -126,6 +127,14 @@ pub trait Material: AsBindGroup + Send + Sync + Clone + TypeUuid + Sized + 'stat AlphaMode::Opaque } + /// Returns if this material should be rendered by the deferred renderer when + /// AlphaMode::Opaque or AlphaMode::Mask + /// If None, it will default to the DefaultOpaqueRendererMethod + #[inline] + fn deferred(&self) -> Option { + None + } + #[inline] /// Add a bias to the view depth of the mesh which can be used to force a specific render order /// for meshes with similar depth, to avoid z-fighting. @@ -187,7 +196,10 @@ where M::Data: PartialEq + Eq + Hash + Clone, { fn build(&self, app: &mut App) { - app.add_asset::() + app.register_type::() + .init_resource::() + .add_plugin(ExtractResourcePlugin::::default()) + .add_asset::() .add_plugin(ExtractComponentPlugin::>::extract_visible()); if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { @@ -381,6 +393,7 @@ pub fn queue_material_meshes( render_materials: Res>, material_meshes: Query<(&Handle, &Handle, &MeshUniform)>, images: Res>, + default_opaque_render_method: Res, mut views: Query<( &ExtractedView, &VisibleEntities, @@ -470,6 +483,15 @@ pub fn queue_material_meshes( render_meshes.get(mesh_handle), render_materials.get(material_handle), ) { + let method = match &material.properties.deferred { + Some(method) => method, + None => &default_opaque_render_method.0, + }; + let forward = match method { + OpaqueRendererMethod::Forward => true, + OpaqueRendererMethod::Deferred => false, + }; + let mut mesh_key = MeshPipelineKey::from_primitive_topology(mesh.primitive_topology) | view_key; @@ -491,6 +513,10 @@ pub fn queue_material_meshes( _ => (), } + if deferred_prepass.is_some() && !forward { + mesh_key |= MeshPipelineKey::DEFERRED_PREPASS; + } + let pipeline_id = pipelines.specialize( &pipeline_cache, &material_pipeline, @@ -512,20 +538,24 @@ pub fn queue_material_meshes( + material.properties.depth_bias; match material.properties.alpha_mode { AlphaMode::Opaque => { - opaque_phase.add(Opaque3d { - entity: *visible_entity, - draw_function: draw_opaque_pbr, - pipeline: pipeline_id, - distance, - }); + if forward { + opaque_phase.add(Opaque3d { + entity: *visible_entity, + draw_function: draw_opaque_pbr, + pipeline: pipeline_id, + distance, + }); + } } AlphaMode::Mask(_) => { - alpha_mask_phase.add(AlphaMask3d { - entity: *visible_entity, - draw_function: draw_alpha_mask_pbr, - pipeline: pipeline_id, - distance, - }); + if forward { + alpha_mask_phase.add(AlphaMask3d { + entity: *visible_entity, + draw_function: draw_alpha_mask_pbr, + pipeline: pipeline_id, + distance, + }); + } } AlphaMode::Blend | AlphaMode::Premultiplied @@ -545,8 +575,24 @@ pub fn queue_material_meshes( } } +/// Default render method used for opaque materials +#[derive(Default, Resource, Clone, Debug, ExtractResource, Reflect)] +pub struct DefaultOpaqueRendererMethod(pub OpaqueRendererMethod); + +/// Render method used for opaque materials +#[derive(Default, Clone, Copy, Debug, Reflect, FromReflect)] +pub enum OpaqueRendererMethod { + #[default] + Forward, + Deferred, +} + /// Common [`Material`] properties, calculated for a specific material instance. pub struct MaterialProperties { + /// Is this material should be rendered by the deferred renderer when + /// AlphaMode::Opaque or AlphaMode::Mask + /// If None, it will default to the DefaultOpaqueRendererMethod + pub deferred: Option, /// The [`AlphaMode`] of this material. pub alpha_mode: AlphaMode, /// Add a bias to the view depth of the mesh which can be used to force a specific render order @@ -706,6 +752,7 @@ fn prepare_material( properties: MaterialProperties { alpha_mode: material.alpha_mode(), depth_bias: material.depth_bias(), + deferred: material.deferred(), }, }) } diff --git a/crates/bevy_pbr/src/pbr_material.rs b/crates/bevy_pbr/src/pbr_material.rs index 1eadd479af5af..180ac2b2bd1fb 100644 --- a/crates/bevy_pbr/src/pbr_material.rs +++ b/crates/bevy_pbr/src/pbr_material.rs @@ -1,6 +1,6 @@ use crate::{ - AlphaMode, Material, MaterialPipeline, MaterialPipelineKey, ParallaxMappingMethod, - PBR_PREPASS_SHADER_HANDLE, PBR_SHADER_HANDLE, + AlphaMode, Material, MaterialPipeline, MaterialPipelineKey, OpaqueRendererMethod, + ParallaxMappingMethod, PBR_PREPASS_SHADER_HANDLE, PBR_SHADER_HANDLE, }; use bevy_asset::Handle; use bevy_math::Vec4; @@ -309,6 +309,9 @@ pub struct StandardMaterial { /// /// Default is `16.0`. pub max_parallax_layer_count: f32, + + /// Render method used for opaque materials + pub opaque_render_method: Option, } impl Default for StandardMaterial { @@ -342,6 +345,7 @@ impl Default for StandardMaterial { parallax_depth_scale: 0.1, max_parallax_layer_count: 16.0, parallax_mapping_method: ParallaxMappingMethod::Occlusion, + opaque_render_method: None, } } } @@ -577,4 +581,9 @@ impl Material for StandardMaterial { fn depth_bias(&self) -> f32 { self.depth_bias } + + #[inline] + fn deferred(&self) -> Option { + self.opaque_render_method + } } diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 1a7ef6fa40894..6de889bdc97bc 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -1,11 +1,12 @@ use bevy_app::{Plugin, PreUpdate, Update}; use bevy_asset::{load_internal_asset, AssetServer, Handle, HandleUntyped}; use bevy_core_pipeline::{ + deferred::{AlphaMask3dDeferred, Opaque3dDeferred, DEFERRED_PREPASS_FORMAT}, prelude::Camera3d, prepass::{ AlphaMask3dPrepass, DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass, - Opaque3dPrepass, ViewPrepassTextures, DEFERRED_PREPASS_FORMAT, DEPTH_PREPASS_FORMAT, - MOTION_VECTOR_PREPASS_FORMAT, NORMAL_PREPASS_FORMAT, + Opaque3dPrepass, ViewPrepassTextures, DEPTH_PREPASS_FORMAT, MOTION_VECTOR_PREPASS_FORMAT, + NORMAL_PREPASS_FORMAT, }, }; use bevy_ecs::{ @@ -47,9 +48,10 @@ use bevy_transform::prelude::GlobalTransform; use bevy_utils::{tracing::error, HashMap}; use crate::{ - prepare_lights, AlphaMode, DrawMesh, Material, MaterialPipeline, MaterialPipelineKey, - MeshPipeline, MeshPipelineKey, MeshUniform, RenderMaterials, SetMaterialBindGroup, - SetMeshBindGroup, MAX_CASCADES_PER_LIGHT, MAX_DIRECTIONAL_LIGHTS, + prepare_lights, AlphaMode, DefaultOpaqueRendererMethod, DrawMesh, Material, MaterialPipeline, + MaterialPipelineKey, MeshPipeline, MeshPipelineKey, MeshUniform, OpaqueRendererMethod, + RenderMaterials, SetMaterialBindGroup, SetMeshBindGroup, MAX_CASCADES_PER_LIGHT, + MAX_DIRECTIONAL_LIGHTS, }; use std::{hash::Hash, marker::PhantomData}; @@ -156,6 +158,8 @@ where render_app .init_resource::>() .init_resource::>() + .init_resource::>() + .init_resource::>() .add_systems(ExtractSchedule, extract_camera_prepass_phase) .add_systems( Render, @@ -172,6 +176,8 @@ where .after(prepare_lights), sort_phase_system::.in_set(RenderSet::PhaseSort), sort_phase_system::.in_set(RenderSet::PhaseSort), + sort_phase_system::.in_set(RenderSet::PhaseSort), + sort_phase_system::.in_set(RenderSet::PhaseSort), ), ); } @@ -179,6 +185,8 @@ where render_app .add_render_command::>() .add_render_command::>() + .add_render_command::>() + .add_render_command::>() .add_systems( Render, queue_prepass_material_meshes::.in_set(RenderSet::Queue), @@ -462,15 +470,19 @@ where write_mask: ColorWrites::ALL, }), ); - targets.push( - key.mesh_key - .contains(MeshPipelineKey::DEFERRED_PREPASS) - .then_some(ColorTargetState { - format: DEFERRED_PREPASS_FORMAT, - blend: None, - write_mask: ColorWrites::ALL, - }), - ); + + if key.mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) { + targets.push( + key.mesh_key + .contains(MeshPipelineKey::DEFERRED_PREPASS) + .then_some(ColorTargetState { + format: DEFERRED_PREPASS_FORMAT, + blend: None, + write_mask: ColorWrites::ALL, + }), + ); + } + if targets.iter().all(Option::is_none) { // if no targets are required then clear the list, so that no fragment shader is required // (though one may still be used for discarding depth buffer writes) @@ -642,7 +654,7 @@ pub fn get_bindings<'a>( }; let deferred_fallback = &fallback_format_images - .image_for_samplecount(msaa.samples(), TextureFormat::Rgba32Uint) + .image_for_samplecount(1, TextureFormat::Rgba32Uint) .texture_view; let deferred_view = match prepass_textures.and_then(|x| x.deferred.as_ref()) { @@ -704,7 +716,6 @@ pub fn extract_camera_prepass_phase( if depth_prepass.is_some() || normal_prepass.is_some() || motion_vector_prepass.is_some() - || deferred_prepass.is_some() { entity.insert(( RenderPhase::::default(), @@ -712,6 +723,13 @@ pub fn extract_camera_prepass_phase( )); } + if deferred_prepass.is_some() { + entity.insert(( + RenderPhase::::default(), + RenderPhase::::default(), + )); + } + if depth_prepass.is_some() { entity.insert(DepthPrepass); } @@ -970,18 +988,23 @@ pub fn queue_prepass_view_bind_group( pub fn queue_prepass_material_meshes( opaque_draw_functions: Res>, alpha_mask_draw_functions: Res>, + opaque_draw_deferred_functions: Res>, + alpha_mask_draw_deferred_functions: Res>, prepass_pipeline: Res>, mut pipelines: ResMut>>, pipeline_cache: Res, msaa: Res, render_meshes: Res>, render_materials: Res>, + default_opaque_render_method: Res, material_meshes: Query<(&Handle, &Handle, &MeshUniform)>, mut views: Query<( &ExtractedView, &VisibleEntities, &mut RenderPhase, &mut RenderPhase, + Option<&mut RenderPhase>, + Option<&mut RenderPhase>, Option<&DepthPrepass>, Option<&NormalPrepass>, Option<&MotionVectorPrepass>, @@ -998,11 +1021,21 @@ pub fn queue_prepass_material_meshes( .read() .get_id::>() .unwrap(); + let opaque_draw_deferred = opaque_draw_deferred_functions + .read() + .get_id::>() + .unwrap(); + let alpha_mask_draw_deferred = alpha_mask_draw_deferred_functions + .read() + .get_id::>() + .unwrap(); for ( view, visible_entities, mut opaque_phase, mut alpha_mask_phase, + mut opaque_phase_deferred, + mut alpha_mask_phase_deferred, depth_prepass, normal_prepass, motion_vector_prepass, @@ -1019,9 +1052,9 @@ pub fn queue_prepass_material_meshes( if motion_vector_prepass.is_some() { view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS; } - if deferred_prepass.is_some() { - view_key |= MeshPipelineKey::DEFERRED_PREPASS; - } + + let mut opaque_phase_deferred = opaque_phase_deferred.as_mut(); + let mut alpha_mask_phase_deferred = alpha_mask_phase_deferred.as_mut(); let rangefinder = view.rangefinder3d(); @@ -1039,6 +1072,12 @@ pub fn queue_prepass_material_meshes( let mut mesh_key = MeshPipelineKey::from_primitive_topology(mesh.primitive_topology) | view_key; + + let method = match &material.properties.deferred { + Some(method) => method, + None => &default_opaque_render_method.0, + }; + let alpha_mode = material.properties.alpha_mode; match alpha_mode { AlphaMode::Opaque => {} @@ -1049,6 +1088,17 @@ pub fn queue_prepass_material_meshes( | AlphaMode::Multiply => continue, } + let forward = match method { + OpaqueRendererMethod::Forward => true, + OpaqueRendererMethod::Deferred => false, + }; + + let deferred = deferred_prepass.is_some() && !forward; + + if deferred { + mesh_key |= MeshPipelineKey::DEFERRED_PREPASS; + } + let pipeline_id = pipelines.specialize( &pipeline_cache, &prepass_pipeline, @@ -1070,20 +1120,44 @@ pub fn queue_prepass_material_meshes( rangefinder.distance(&mesh_uniform.transform) + material.properties.depth_bias; match alpha_mode { AlphaMode::Opaque => { - opaque_phase.add(Opaque3dPrepass { - entity: *visible_entity, - draw_function: opaque_draw_prepass, - pipeline_id, - distance, - }); + if deferred { + opaque_phase_deferred + .as_mut() + .unwrap() + .add(Opaque3dDeferred { + entity: *visible_entity, + draw_function: opaque_draw_deferred, + pipeline_id, + distance, + }); + } else { + opaque_phase.add(Opaque3dPrepass { + entity: *visible_entity, + draw_function: opaque_draw_prepass, + pipeline_id, + distance, + }); + } } AlphaMode::Mask(_) => { - alpha_mask_phase.add(AlphaMask3dPrepass { - entity: *visible_entity, - draw_function: alpha_mask_draw_prepass, - pipeline_id, - distance, - }); + if deferred { + alpha_mask_phase_deferred + .as_mut() + .unwrap() + .add(AlphaMask3dDeferred { + entity: *visible_entity, + draw_function: alpha_mask_draw_deferred, + pipeline_id, + distance, + }); + } else { + alpha_mask_phase.add(AlphaMask3dPrepass { + entity: *visible_entity, + draw_function: alpha_mask_draw_prepass, + pipeline_id, + distance, + }); + } } AlphaMode::Blend | AlphaMode::Premultiplied diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 5e209b0d35904..ce0cfb5a6a27f 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -675,10 +675,6 @@ impl SpecializedMeshPipeline for MeshPipeline { shader_defs.push("LOAD_PREPASS_NORMALS".into()); } - if key.contains(MeshPipelineKey::DEFERRED_PREPASS) { - shader_defs.push("LOAD_PREPASS_DEFERRED".into()); - } - if layout.contains(Mesh::ATTRIBUTE_POSITION) { shader_defs.push("VERTEX_POSITIONS".into()); vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0)); diff --git a/crates/bevy_pbr/src/render/pbr_prepass.wgsl b/crates/bevy_pbr/src/render/pbr_prepass.wgsl index ace44e939e15d..e1884ff0cb2b4 100644 --- a/crates/bevy_pbr/src/render/pbr_prepass.wgsl +++ b/crates/bevy_pbr/src/render/pbr_prepass.wgsl @@ -165,7 +165,7 @@ fn fragment(in: FragmentInput) -> FragmentOutput { pack4x8unorm(output_color), float3_to_rgb9e5(emissive), pack4x8unorm(vec4(metallic, perceptual_roughness, occlusion, reflectance)), - material.flags, + mesh.flags, //material.flags, // TODO Griffin fit both ); #endif // DEFERRED_PREPASS diff --git a/examples/3d/deferred_rendering.rs b/examples/3d/deferred_rendering.rs new file mode 100644 index 0000000000000..ef458c75f17ed --- /dev/null +++ b/examples/3d/deferred_rendering.rs @@ -0,0 +1,119 @@ +//! Loads and renders a glTF file as a scene. + +use std::f32::consts::*; + +use bevy::{ + pbr::{CascadeShadowConfigBuilder, DirectionalLightShadowMap}, + prelude::*, +}; +use bevy_internal::{ + core_pipeline::{ + fxaa::Fxaa, + prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass}, + }, + pbr::{DefaultOpaqueRendererMethod, OpaqueRendererMethod}, +}; + +fn main() { + App::new() + .insert_resource(Msaa::Off) + .insert_resource(AmbientLight { + color: Color::WHITE, + brightness: 1.0 / 5.0f32, + }) + .insert_resource(DirectionalLightShadowMap { size: 4096 }) + .add_plugins(DefaultPlugins) + .insert_resource(DefaultOpaqueRendererMethod(OpaqueRendererMethod::Deferred)) + .add_systems(Startup, setup) + .add_systems(Update, animate_light_direction) + .run(); +} + +fn setup( + mut commands: Commands, + asset_server: Res, + mut materials: ResMut>, + mut meshes: ResMut>, +) { + commands.spawn(( + Camera3dBundle { + camera: Camera { + //hdr: true, + ..default() + }, + transform: Transform::from_xyz(0.7, 0.7, 1.0) + .looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y), + ..default() + }, + EnvironmentMapLight { + diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"), + specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), + }, + NormalPrepass, + DepthPrepass, + MotionVectorPrepass, + DeferredPrepass, + Fxaa::default(), + )); + + commands.spawn(DirectionalLightBundle { + directional_light: DirectionalLight { + shadows_enabled: true, + ..default() + }, + cascade_shadow_config: CascadeShadowConfigBuilder { + num_cascades: 3, + maximum_distance: 5.0, + ..default() + } + .into(), + ..default() + }); + + // FlightHelmet + commands.spawn(SceneBundle { + scene: asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0"), + ..default() + }); + + let mut forward_mat: StandardMaterial = Color::rgb(0.3, 0.5, 0.3).into(); + forward_mat.opaque_render_method = Some(OpaqueRendererMethod::Forward); + let forward_mat_h = materials.add(forward_mat); + + // plane + commands.spawn(PbrBundle { + mesh: meshes.add(shape::Plane::from_size(5.0).into()), + material: forward_mat_h.clone(), + ..default() + }); + + let cube_h = meshes.add(Mesh::from(shape::Cube { size: 0.1 })); + + // cubes + commands.spawn(PbrBundle { + mesh: cube_h.clone(), + material: forward_mat_h.clone(), + transform: Transform::from_xyz(-0.3, 0.5, -0.2), + ..default() + }); + commands.spawn(PbrBundle { + mesh: cube_h, + material: forward_mat_h, + transform: Transform::from_xyz(0.2, 0.5, 0.2), + ..default() + }); +} + +fn animate_light_direction( + time: Res