From 3ee96383164a3e7e55f14eee1c85dc2e9d02396c Mon Sep 17 00:00:00 2001 From: IceSentry Date: Thu, 2 Mar 2023 02:23:06 +0000 Subject: [PATCH] Allow prepass in webgl (#7537) # Objective - Use the prepass textures in webgl ## Solution - Bind the prepass textures even when using webgl, but only if msaa is disabled - Also did some refactors to centralize how textures are bound, similar to the EnvironmentMapLight PR - ~~Also did some refactors of the example to make it work in webgl~~ - ~~To make the example work in webgl, I needed to use a sampler for the depth texture, the resulting code looks a bit weird, but it's simple enough and I think it's worth it to show how it works when using webgl~~ --- Cargo.toml | 2 +- assets/shaders/show_prepass.wgsl | 19 +++-- crates/bevy_pbr/src/prepass/mod.rs | 84 +++++++++++++++++-- .../bevy_pbr/src/prepass/prepass_utils.wgsl | 4 +- crates/bevy_pbr/src/render/mesh.rs | 75 +++++------------ examples/README.md | 2 +- examples/shader/shader_prepass.rs | 40 +++++---- 7 files changed, 133 insertions(+), 93 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e6b38cbe7ff81f..aac8ea12e6f464 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1399,7 +1399,7 @@ path = "examples/shader/shader_prepass.rs" [package.metadata.example.shader_prepass] name = "Material Prepass" -description = "A shader that uses the depth texture generated in a prepass" +description = "A shader that uses the various textures generated by the prepass" category = "Shaders" wasm = false diff --git a/assets/shaders/show_prepass.wgsl b/assets/shaders/show_prepass.wgsl index 87c48993aa2035..bc344ade971c6f 100644 --- a/assets/shaders/show_prepass.wgsl +++ b/assets/shaders/show_prepass.wgsl @@ -2,10 +2,14 @@ #import bevy_pbr::mesh_view_bindings #import bevy_pbr::prepass_utils +struct ShowPrepassSettings { + show_depth: u32, + show_normals: u32, + padding_1: u32, + padding_2: u32, +} @group(1) @binding(0) -var show_depth: f32; -@group(1) @binding(1) -var show_normal: f32; +var settings: ShowPrepassSettings; @fragment fn fragment( @@ -13,14 +17,13 @@ fn fragment( @builtin(sample_index) sample_index: u32, #import bevy_pbr::mesh_vertex_output ) -> @location(0) vec4 { - if show_depth == 1.0 { + if settings.show_depth == 1u { let depth = prepass_depth(frag_coord, sample_index); return vec4(depth, depth, depth, 1.0); - } else if show_normal == 1.0 { + } else if settings.show_normals == 1u { let normal = prepass_normal(frag_coord, sample_index); return vec4(normal, 1.0); - } else { - // transparent - return vec4(0.0); } + + return vec4(0.0); } diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 9f63d7b9ff4ea4..436f39350e63ac 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -26,16 +26,17 @@ use bevy_render::{ }, render_resource::{ BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor, - BindGroupLayoutEntry, BindingType, BlendState, BufferBindingType, ColorTargetState, - ColorWrites, CompareFunction, DepthBiasState, DepthStencilState, Extent3d, FragmentState, - FrontFace, MultisampleState, PipelineCache, PolygonMode, PrimitiveState, - RenderPipelineDescriptor, Shader, ShaderDefVal, ShaderRef, ShaderStages, ShaderType, - SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines, - StencilFaceState, StencilState, TextureDescriptor, TextureDimension, TextureFormat, - TextureUsages, VertexState, + BindGroupLayoutEntry, BindingResource, BindingType, BlendState, BufferBindingType, + ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, DepthStencilState, + Extent3d, FragmentState, FrontFace, MultisampleState, PipelineCache, PolygonMode, + PrimitiveState, RenderPipelineDescriptor, Shader, ShaderDefVal, ShaderRef, ShaderStages, + ShaderType, SpecializedMeshPipeline, SpecializedMeshPipelineError, + SpecializedMeshPipelines, StencilFaceState, StencilState, TextureDescriptor, + TextureDimension, TextureFormat, TextureSampleType, TextureUsages, TextureViewDimension, + VertexState, }, renderer::RenderDevice, - texture::TextureCache, + texture::{FallbackImagesDepth, FallbackImagesMsaa, TextureCache}, view::{ExtractedView, Msaa, ViewUniform, ViewUniformOffset, ViewUniforms, VisibleEntities}, Extract, ExtractSchedule, RenderApp, RenderSet, }; @@ -333,6 +334,73 @@ where } } +pub fn get_bind_group_layout_entries( + bindings: [u32; 2], + multisampled: bool, +) -> [BindGroupLayoutEntry; 2] { + [ + // Depth texture + BindGroupLayoutEntry { + binding: bindings[0], + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled, + sample_type: TextureSampleType::Depth, + view_dimension: TextureViewDimension::D2, + }, + count: None, + }, + // Normal texture + BindGroupLayoutEntry { + binding: bindings[1], + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled, + sample_type: TextureSampleType::Float { filterable: true }, + view_dimension: TextureViewDimension::D2, + }, + count: None, + }, + ] +} + +pub fn get_bindings<'a>( + prepass_textures: Option<&'a ViewPrepassTextures>, + fallback_images: &'a mut FallbackImagesMsaa, + fallback_depths: &'a mut FallbackImagesDepth, + msaa: &'a Msaa, + bindings: [u32; 2], +) -> [BindGroupEntry<'a>; 2] { + let depth_view = match prepass_textures.and_then(|x| x.depth.as_ref()) { + Some(texture) => &texture.default_view, + None => { + &fallback_depths + .image_for_samplecount(msaa.samples()) + .texture_view + } + }; + + let normal_view = match prepass_textures.and_then(|x| x.normal.as_ref()) { + Some(texture) => &texture.default_view, + None => { + &fallback_images + .image_for_samplecount(msaa.samples()) + .texture_view + } + }; + + [ + BindGroupEntry { + binding: bindings[0], + resource: BindingResource::TextureView(depth_view), + }, + BindGroupEntry { + binding: bindings[1], + resource: BindingResource::TextureView(normal_view), + }, + ] +} + // Extract the render phases for the prepass pub fn extract_camera_prepass_phase( mut commands: Commands, diff --git a/crates/bevy_pbr/src/prepass/prepass_utils.wgsl b/crates/bevy_pbr/src/prepass/prepass_utils.wgsl index 1fd1365a650a42..62329d3d70e2aa 100644 --- a/crates/bevy_pbr/src/prepass/prepass_utils.wgsl +++ b/crates/bevy_pbr/src/prepass/prepass_utils.wgsl @@ -6,7 +6,7 @@ fn prepass_normal(frag_coord: vec4, sample_index: u32) -> vec3 { let normal_sample = textureLoad(normal_prepass_texture, vec2(frag_coord.xy), i32(sample_index)); #else let normal_sample = textureLoad(normal_prepass_texture, vec2(frag_coord.xy), 0); -#endif +#endif // MULTISAMPLED return normal_sample.xyz * 2.0 - vec3(1.0); } #endif // NORMAL_PREPASS @@ -17,7 +17,7 @@ fn prepass_depth(frag_coord: vec4, sample_index: u32) -> f32 { let depth_sample = textureLoad(depth_prepass_texture, vec2(frag_coord.xy), i32(sample_index)); #else let depth_sample = textureLoad(depth_prepass_texture, vec2(frag_coord.xy), 0); -#endif +#endif // MULTISAMPLED return depth_sample; } #endif // DEPTH_PREPASS diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 477e29795014ad..119836ae582e64 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -1,9 +1,9 @@ use crate::{ - environment_map, queue_shadow_view_bind_group, EnvironmentMapLight, FogMeta, GlobalLightMeta, - GpuFog, GpuLights, GpuPointLights, LightMeta, NotShadowCaster, NotShadowReceiver, - ShadowPipeline, ViewClusterBindings, ViewFogUniformOffset, ViewLightsUniformOffset, - ViewShadowBindings, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, MAX_CASCADES_PER_LIGHT, - MAX_DIRECTIONAL_LIGHTS, + environment_map, prepass, queue_shadow_view_bind_group, EnvironmentMapLight, FogMeta, + GlobalLightMeta, GpuFog, GpuLights, GpuPointLights, LightMeta, NotShadowCaster, + NotShadowReceiver, ShadowPipeline, ViewClusterBindings, ViewFogUniformOffset, + ViewLightsUniformOffset, ViewShadowBindings, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, + MAX_CASCADES_PER_LIGHT, MAX_DIRECTIONAL_LIGHTS, }; use bevy_app::{IntoSystemAppConfigs, Plugin}; use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped}; @@ -432,29 +432,11 @@ impl FromWorld for MeshPipeline { let tonemapping_lut_entries = get_lut_bind_group_layout_entries([14, 15]); entries.extend_from_slice(&tonemapping_lut_entries); - if cfg!(not(feature = "webgl")) { - // Depth texture - entries.push(BindGroupLayoutEntry { - binding: 16, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled, - sample_type: TextureSampleType::Depth, - view_dimension: TextureViewDimension::D2, - }, - count: None, - }); - // Normal texture - entries.push(BindGroupLayoutEntry { - binding: 17, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled, - sample_type: TextureSampleType::Float { filterable: true }, - view_dimension: TextureViewDimension::D2, - }, - count: None, - }); + if cfg!(not(feature = "webgl")) || (cfg!(feature = "webgl") && !multisampled) { + entries.extend_from_slice(&prepass::get_bind_group_layout_entries( + [16, 17], + multisampled, + )); } entries @@ -1067,34 +1049,15 @@ pub fn queue_mesh_view_bind_groups( get_lut_bindings(&images, &tonemapping_luts, tonemapping, [14, 15]); entries.extend_from_slice(&tonemapping_luts); - // When using WebGL with MSAA, we can't create the fallback textures required by the prepass - // When using WebGL, and MSAA is disabled, we can't bind the textures either - if cfg!(not(feature = "webgl")) { - let depth_view = match prepass_textures.and_then(|x| x.depth.as_ref()) { - Some(texture) => &texture.default_view, - None => { - &fallback_depths - .image_for_samplecount(msaa.samples()) - .texture_view - } - }; - entries.push(BindGroupEntry { - binding: 16, - resource: BindingResource::TextureView(depth_view), - }); - - let normal_view = match prepass_textures.and_then(|x| x.normal.as_ref()) { - Some(texture) => &texture.default_view, - None => { - &fallback_images - .image_for_samplecount(msaa.samples()) - .texture_view - } - }; - entries.push(BindGroupEntry { - binding: 17, - resource: BindingResource::TextureView(normal_view), - }); + // When using WebGL, we can't have a depth texture with multisampling + if cfg!(not(feature = "webgl")) || (cfg!(feature = "webgl") && msaa.samples() == 1) { + entries.extend_from_slice(&prepass::get_bindings( + prepass_textures, + &mut fallback_images, + &mut fallback_depths, + &msaa, + [16, 17], + )); } let view_bind_group = render_device.create_bind_group(&BindGroupDescriptor { diff --git a/examples/README.md b/examples/README.md index 41eaddbc0bdd3c..7358f3e7c6915c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -276,7 +276,7 @@ Example | Description [Material](../examples/shader/shader_material.rs) | A shader and a material that uses it [Material - GLSL](../examples/shader/shader_material_glsl.rs) | A shader that uses the GLSL shading language [Material - Screenspace Texture](../examples/shader/shader_material_screenspace_texture.rs) | A shader that samples a texture with view-independent UV coordinates -[Material Prepass](../examples/shader/shader_prepass.rs) | A shader that uses the depth texture generated in a prepass +[Material Prepass](../examples/shader/shader_prepass.rs) | A shader that uses the various textures generated by the prepass [Post Processing](../examples/shader/post_processing.rs) | A custom post processing effect, using two cameras, with one reusing the render texture of the first one [Shader Defs](../examples/shader/shader_defs.rs) | A shader that uses "shaders defs" (a bevy tool to selectively toggle parts of a shader) [Texture Binding Array (Bindless Textures)](../examples/shader/texture_binding_array.rs) | A shader that shows how to bind and sample multiple textures as a binding array (a.k.a. bindless textures). diff --git a/examples/shader/shader_prepass.rs b/examples/shader/shader_prepass.rs index 0f9697bcadf6b5..688152c5a935af 100644 --- a/examples/shader/shader_prepass.rs +++ b/examples/shader/shader_prepass.rs @@ -10,7 +10,7 @@ use bevy::{ pbr::{NotShadowCaster, PbrPlugin}, prelude::*, reflect::TypeUuid, - render::render_resource::{AsBindGroup, ShaderRef}, + render::render_resource::{AsBindGroup, ShaderRef, ShaderType}, }; fn main() { @@ -30,7 +30,7 @@ fn main() { }) .add_startup_system(setup) .add_system(rotate) - .add_system(update) + .add_system(toggle_prepass_view) .run(); } @@ -69,8 +69,7 @@ fn setup( MaterialMeshBundle { mesh: meshes.add(shape::Quad::new(Vec2::new(20.0, 20.0)).into()), material: depth_materials.add(PrepassOutputMaterial { - show_depth: 0.0, - show_normal: 0.0, + settings: ShowPrepassSettings::default(), }), transform: Transform::from_xyz(-0.75, 1.25, 3.0) .looking_at(Vec3::new(2.0, -2.5, -5.0), Vec3::Y), @@ -193,14 +192,20 @@ fn rotate(mut q: Query<&mut Transform, With>, time: Res