Added visibility bitmask as an alternative SSAO method (#13454)
Early implementation. I still have to fix the documentation and consider writing a small migration guide. Questions left to answer: * [x] should thickness be an overridable constant? * [x] is there a better way to implement `Eq`/`Hash` for `SSAOMethod`? * [x] do we want to keep the linear sampler for the depth texture? * [x] is there a better way to separate the logic than preprocessor macros?  ## Migration guide SSAO algorithm was changed from GTAO to VBAO (visibility bitmasks). A new field, `constant_object_thickness`, was added to `ScreenSpaceAmbientOcclusion`. `ScreenSpaceAmbientOcclusion` also lost its `Eq` and `Hash` implementations. --------- Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									c841dd92a1
								
							
						
					
					
						commit
						ba7907cae7
					
				| @ -10,7 +10,7 @@ | |||||||
| 
 | 
 | ||||||
| #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION | #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION | ||||||
| #import bevy_pbr::mesh_view_bindings::screen_space_ambient_occlusion_texture | #import bevy_pbr::mesh_view_bindings::screen_space_ambient_occlusion_texture | ||||||
| #import bevy_pbr::gtao_utils::gtao_multibounce | #import bevy_pbr::ssao_utils::ssao_multibounce | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| struct FullscreenVertexOutput { | struct FullscreenVertexOutput { | ||||||
| @ -64,7 +64,7 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> { | |||||||
| 
 | 
 | ||||||
| #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION | #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION | ||||||
|         let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2<i32>(in.position.xy), 0i).r; |         let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2<i32>(in.position.xy), 0i).r; | ||||||
|         let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb); |         let ssao_multibounce = ssao_multibounce(ssao, pbr_input.material.base_color.rgb); | ||||||
|         pbr_input.diffuse_occlusion = min(pbr_input.diffuse_occlusion, ssao_multibounce); |         pbr_input.diffuse_occlusion = min(pbr_input.diffuse_occlusion, ssao_multibounce); | ||||||
| 
 | 
 | ||||||
|         // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886" |         // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886" | ||||||
|  | |||||||
| @ -44,7 +44,7 @@ use crate::{ | |||||||
|     }, |     }, | ||||||
|     prepass, EnvironmentMapUniformBuffer, FogMeta, GlobalClusterableObjectMeta, |     prepass, EnvironmentMapUniformBuffer, FogMeta, GlobalClusterableObjectMeta, | ||||||
|     GpuClusterableObjects, GpuFog, GpuLights, LightMeta, LightProbesBuffer, LightProbesUniform, |     GpuClusterableObjects, GpuFog, GpuLights, LightMeta, LightProbesBuffer, LightProbesUniform, | ||||||
|     MeshPipeline, MeshPipelineKey, RenderViewLightProbes, ScreenSpaceAmbientOcclusionTextures, |     MeshPipeline, MeshPipelineKey, RenderViewLightProbes, ScreenSpaceAmbientOcclusionResources, | ||||||
|     ScreenSpaceReflectionsBuffer, ScreenSpaceReflectionsUniform, ShadowSamplers, |     ScreenSpaceReflectionsBuffer, ScreenSpaceReflectionsUniform, ShadowSamplers, | ||||||
|     ViewClusterBindings, ViewShadowBindings, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, |     ViewClusterBindings, ViewShadowBindings, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, | ||||||
| }; | }; | ||||||
| @ -462,7 +462,7 @@ pub fn prepare_mesh_view_bind_groups( | |||||||
|         &ViewShadowBindings, |         &ViewShadowBindings, | ||||||
|         &ViewClusterBindings, |         &ViewClusterBindings, | ||||||
|         &Msaa, |         &Msaa, | ||||||
|         Option<&ScreenSpaceAmbientOcclusionTextures>, |         Option<&ScreenSpaceAmbientOcclusionResources>, | ||||||
|         Option<&ViewPrepassTextures>, |         Option<&ViewPrepassTextures>, | ||||||
|         Option<&ViewTransmissionTexture>, |         Option<&ViewTransmissionTexture>, | ||||||
|         &Tonemapping, |         &Tonemapping, | ||||||
| @ -507,7 +507,7 @@ pub fn prepare_mesh_view_bind_groups( | |||||||
|             shadow_bindings, |             shadow_bindings, | ||||||
|             cluster_bindings, |             cluster_bindings, | ||||||
|             msaa, |             msaa, | ||||||
|             ssao_textures, |             ssao_resources, | ||||||
|             prepass_textures, |             prepass_textures, | ||||||
|             transmission_texture, |             transmission_texture, | ||||||
|             tonemapping, |             tonemapping, | ||||||
| @ -519,7 +519,7 @@ pub fn prepare_mesh_view_bind_groups( | |||||||
|                 .image_for_samplecount(1, TextureFormat::bevy_default()) |                 .image_for_samplecount(1, TextureFormat::bevy_default()) | ||||||
|                 .texture_view |                 .texture_view | ||||||
|                 .clone(); |                 .clone(); | ||||||
|             let ssao_view = ssao_textures |             let ssao_view = ssao_resources | ||||||
|                 .map(|t| &t.screen_space_ambient_occlusion_texture.default_view) |                 .map(|t| &t.screen_space_ambient_occlusion_texture.default_view) | ||||||
|                 .unwrap_or(&fallback_ssao); |                 .unwrap_or(&fallback_ssao); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -15,7 +15,7 @@ | |||||||
| 
 | 
 | ||||||
| #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION | #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION | ||||||
| #import bevy_pbr::mesh_view_bindings::screen_space_ambient_occlusion_texture | #import bevy_pbr::mesh_view_bindings::screen_space_ambient_occlusion_texture | ||||||
| #import bevy_pbr::gtao_utils::gtao_multibounce | #import bevy_pbr::ssao_utils::ssao_multibounce | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef MESHLET_MESH_MATERIAL_PASS | #ifdef MESHLET_MESH_MATERIAL_PASS | ||||||
| @ -344,7 +344,7 @@ fn pbr_input_from_standard_material( | |||||||
| #endif | #endif | ||||||
| #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION | #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION | ||||||
|         let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2<i32>(in.position.xy), 0i).r; |         let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2<i32>(in.position.xy), 0i).r; | ||||||
|         let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb); |         let ssao_multibounce = ssao_multibounce(ssao, pbr_input.material.base_color.rgb); | ||||||
|         diffuse_occlusion = min(diffuse_occlusion, ssao_multibounce); |         diffuse_occlusion = min(diffuse_occlusion, ssao_multibounce); | ||||||
|         // Use SSAO to estimate the specular occlusion. |         // Use SSAO to estimate the specular occlusion. | ||||||
|         // Lagarde and Rousiers 2014, "Moving Frostbite to Physically Based Rendering" |         // Lagarde and Rousiers 2014, "Moving Frostbite to Physically Based Rendering" | ||||||
|  | |||||||
| @ -42,9 +42,9 @@ use bevy_utils::{ | |||||||
| use core::mem; | use core::mem; | ||||||
| 
 | 
 | ||||||
| const PREPROCESS_DEPTH_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(102258915420479); | const PREPROCESS_DEPTH_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(102258915420479); | ||||||
| const GTAO_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(253938746510568); | const SSAO_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(253938746510568); | ||||||
| const SPATIAL_DENOISE_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(466162052558226); | const SPATIAL_DENOISE_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(466162052558226); | ||||||
| const GTAO_UTILS_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(366465052568786); | const SSAO_UTILS_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(366465052568786); | ||||||
| 
 | 
 | ||||||
| /// Plugin for screen space ambient occlusion.
 | /// Plugin for screen space ambient occlusion.
 | ||||||
| pub struct ScreenSpaceAmbientOcclusionPlugin; | pub struct ScreenSpaceAmbientOcclusionPlugin; | ||||||
| @ -57,7 +57,7 @@ impl Plugin for ScreenSpaceAmbientOcclusionPlugin { | |||||||
|             "preprocess_depth.wgsl", |             "preprocess_depth.wgsl", | ||||||
|             Shader::from_wgsl |             Shader::from_wgsl | ||||||
|         ); |         ); | ||||||
|         load_internal_asset!(app, GTAO_SHADER_HANDLE, "gtao.wgsl", Shader::from_wgsl); |         load_internal_asset!(app, SSAO_SHADER_HANDLE, "ssao.wgsl", Shader::from_wgsl); | ||||||
|         load_internal_asset!( |         load_internal_asset!( | ||||||
|             app, |             app, | ||||||
|             SPATIAL_DENOISE_SHADER_HANDLE, |             SPATIAL_DENOISE_SHADER_HANDLE, | ||||||
| @ -66,8 +66,8 @@ impl Plugin for ScreenSpaceAmbientOcclusionPlugin { | |||||||
|         ); |         ); | ||||||
|         load_internal_asset!( |         load_internal_asset!( | ||||||
|             app, |             app, | ||||||
|             GTAO_UTILS_SHADER_HANDLE, |             SSAO_UTILS_SHADER_HANDLE, | ||||||
|             "gtao_utils.wgsl", |             "ssao_utils.wgsl", | ||||||
|             Shader::from_wgsl |             Shader::from_wgsl | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
| @ -158,13 +158,28 @@ pub struct ScreenSpaceAmbientOcclusionBundle { | |||||||
| /// TAA ([`bevy_core_pipeline::experimental::taa::TemporalAntiAliasing`]).
 | /// TAA ([`bevy_core_pipeline::experimental::taa::TemporalAntiAliasing`]).
 | ||||||
| /// Doing so greatly reduces SSAO noise.
 | /// Doing so greatly reduces SSAO noise.
 | ||||||
| ///
 | ///
 | ||||||
| /// SSAO is not supported on `WebGL2`, and is not currently supported on `WebGPU` or `DirectX12`.
 | /// SSAO is not supported on `WebGL2`, and is not currently supported on `WebGPU`.
 | ||||||
| #[derive(Component, ExtractComponent, Reflect, PartialEq, Eq, Hash, Clone, Default, Debug)] | #[derive(Component, ExtractComponent, Reflect, PartialEq, Clone, Debug)] | ||||||
| #[reflect(Component, Debug, Default, Hash, PartialEq)] | #[reflect(Component, Debug, Default, PartialEq)] | ||||||
| #[require(DepthPrepass, NormalPrepass)] | #[require(DepthPrepass, NormalPrepass)] | ||||||
| #[doc(alias = "Ssao")] | #[doc(alias = "Ssao")] | ||||||
| pub struct ScreenSpaceAmbientOcclusion { | pub struct ScreenSpaceAmbientOcclusion { | ||||||
|  |     /// Quality of the SSAO effect.
 | ||||||
|     pub quality_level: ScreenSpaceAmbientOcclusionQualityLevel, |     pub quality_level: ScreenSpaceAmbientOcclusionQualityLevel, | ||||||
|  |     /// A constant estimated thickness of objects.
 | ||||||
|  |     ///
 | ||||||
|  |     /// This value is used to decide how far behind an object a ray of light needs to be in order
 | ||||||
|  |     /// to pass behind it. Any ray closer than that will be occluded.
 | ||||||
|  |     pub constant_object_thickness: f32, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Default for ScreenSpaceAmbientOcclusion { | ||||||
|  |     fn default() -> Self { | ||||||
|  |         Self { | ||||||
|  |             quality_level: ScreenSpaceAmbientOcclusionQualityLevel::default(), | ||||||
|  |             constant_object_thickness: 0.25, | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[deprecated(since = "0.15.0", note = "Renamed to `ScreenSpaceAmbientOcclusion`")] | #[deprecated(since = "0.15.0", note = "Renamed to `ScreenSpaceAmbientOcclusion`")] | ||||||
| @ -224,7 +239,7 @@ impl ViewNode for SsaoNode { | |||||||
|             Some(camera_size), |             Some(camera_size), | ||||||
|             Some(preprocess_depth_pipeline), |             Some(preprocess_depth_pipeline), | ||||||
|             Some(spatial_denoise_pipeline), |             Some(spatial_denoise_pipeline), | ||||||
|             Some(gtao_pipeline), |             Some(ssao_pipeline), | ||||||
|         ) = ( |         ) = ( | ||||||
|             camera.physical_viewport_size, |             camera.physical_viewport_size, | ||||||
|             pipeline_cache.get_compute_pipeline(pipelines.preprocess_depth_pipeline), |             pipeline_cache.get_compute_pipeline(pipelines.preprocess_depth_pipeline), | ||||||
| @ -260,21 +275,21 @@ impl ViewNode for SsaoNode { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         { |         { | ||||||
|             let mut gtao_pass = |             let mut ssao_pass = | ||||||
|                 render_context |                 render_context | ||||||
|                     .command_encoder() |                     .command_encoder() | ||||||
|                     .begin_compute_pass(&ComputePassDescriptor { |                     .begin_compute_pass(&ComputePassDescriptor { | ||||||
|                         label: Some("ssao_gtao_pass"), |                         label: Some("ssao_ssao_pass"), | ||||||
|                         timestamp_writes: None, |                         timestamp_writes: None, | ||||||
|                     }); |                     }); | ||||||
|             gtao_pass.set_pipeline(gtao_pipeline); |             ssao_pass.set_pipeline(ssao_pipeline); | ||||||
|             gtao_pass.set_bind_group(0, &bind_groups.gtao_bind_group, &[]); |             ssao_pass.set_bind_group(0, &bind_groups.ssao_bind_group, &[]); | ||||||
|             gtao_pass.set_bind_group( |             ssao_pass.set_bind_group( | ||||||
|                 1, |                 1, | ||||||
|                 &bind_groups.common_bind_group, |                 &bind_groups.common_bind_group, | ||||||
|                 &[view_uniform_offset.offset], |                 &[view_uniform_offset.offset], | ||||||
|             ); |             ); | ||||||
|             gtao_pass.dispatch_workgroups( |             ssao_pass.dispatch_workgroups( | ||||||
|                 div_ceil(camera_size.x, 8), |                 div_ceil(camera_size.x, 8), | ||||||
|                 div_ceil(camera_size.y, 8), |                 div_ceil(camera_size.y, 8), | ||||||
|                 1, |                 1, | ||||||
| @ -315,11 +330,12 @@ struct SsaoPipelines { | |||||||
| 
 | 
 | ||||||
|     common_bind_group_layout: BindGroupLayout, |     common_bind_group_layout: BindGroupLayout, | ||||||
|     preprocess_depth_bind_group_layout: BindGroupLayout, |     preprocess_depth_bind_group_layout: BindGroupLayout, | ||||||
|     gtao_bind_group_layout: BindGroupLayout, |     ssao_bind_group_layout: BindGroupLayout, | ||||||
|     spatial_denoise_bind_group_layout: BindGroupLayout, |     spatial_denoise_bind_group_layout: BindGroupLayout, | ||||||
| 
 | 
 | ||||||
|     hilbert_index_lut: TextureView, |     hilbert_index_lut: TextureView, | ||||||
|     point_clamp_sampler: Sampler, |     point_clamp_sampler: Sampler, | ||||||
|  |     linear_clamp_sampler: Sampler, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl FromWorld for SsaoPipelines { | impl FromWorld for SsaoPipelines { | ||||||
| @ -358,6 +374,14 @@ impl FromWorld for SsaoPipelines { | |||||||
|             address_mode_v: AddressMode::ClampToEdge, |             address_mode_v: AddressMode::ClampToEdge, | ||||||
|             ..Default::default() |             ..Default::default() | ||||||
|         }); |         }); | ||||||
|  |         let linear_clamp_sampler = render_device.create_sampler(&SamplerDescriptor { | ||||||
|  |             min_filter: FilterMode::Linear, | ||||||
|  |             mag_filter: FilterMode::Linear, | ||||||
|  |             mipmap_filter: FilterMode::Nearest, | ||||||
|  |             address_mode_u: AddressMode::ClampToEdge, | ||||||
|  |             address_mode_v: AddressMode::ClampToEdge, | ||||||
|  |             ..Default::default() | ||||||
|  |         }); | ||||||
| 
 | 
 | ||||||
|         let common_bind_group_layout = render_device.create_bind_group_layout( |         let common_bind_group_layout = render_device.create_bind_group_layout( | ||||||
|             "ssao_common_bind_group_layout", |             "ssao_common_bind_group_layout", | ||||||
| @ -365,6 +389,7 @@ impl FromWorld for SsaoPipelines { | |||||||
|                 ShaderStages::COMPUTE, |                 ShaderStages::COMPUTE, | ||||||
|                 ( |                 ( | ||||||
|                     sampler(SamplerBindingType::NonFiltering), |                     sampler(SamplerBindingType::NonFiltering), | ||||||
|  |                     sampler(SamplerBindingType::Filtering), | ||||||
|                     uniform_buffer::<ViewUniform>(true), |                     uniform_buffer::<ViewUniform>(true), | ||||||
|                 ), |                 ), | ||||||
|             ), |             ), | ||||||
| @ -385,17 +410,18 @@ impl FromWorld for SsaoPipelines { | |||||||
|             ), |             ), | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         let gtao_bind_group_layout = render_device.create_bind_group_layout( |         let ssao_bind_group_layout = render_device.create_bind_group_layout( | ||||||
|             "ssao_gtao_bind_group_layout", |             "ssao_ssao_bind_group_layout", | ||||||
|             &BindGroupLayoutEntries::sequential( |             &BindGroupLayoutEntries::sequential( | ||||||
|                 ShaderStages::COMPUTE, |                 ShaderStages::COMPUTE, | ||||||
|                 ( |                 ( | ||||||
|                     texture_2d(TextureSampleType::Float { filterable: false }), |                     texture_2d(TextureSampleType::Float { filterable: true }), | ||||||
|                     texture_2d(TextureSampleType::Float { filterable: false }), |                     texture_2d(TextureSampleType::Float { filterable: false }), | ||||||
|                     texture_2d(TextureSampleType::Uint), |                     texture_2d(TextureSampleType::Uint), | ||||||
|                     texture_storage_2d(TextureFormat::R16Float, StorageTextureAccess::WriteOnly), |                     texture_storage_2d(TextureFormat::R16Float, StorageTextureAccess::WriteOnly), | ||||||
|                     texture_storage_2d(TextureFormat::R32Uint, StorageTextureAccess::WriteOnly), |                     texture_storage_2d(TextureFormat::R32Uint, StorageTextureAccess::WriteOnly), | ||||||
|                     uniform_buffer::<GlobalsUniform>(false), |                     uniform_buffer::<GlobalsUniform>(false), | ||||||
|  |                     uniform_buffer::<f32>(false), | ||||||
|                 ), |                 ), | ||||||
|             ), |             ), | ||||||
|         ); |         ); | ||||||
| @ -444,18 +470,19 @@ impl FromWorld for SsaoPipelines { | |||||||
| 
 | 
 | ||||||
|             common_bind_group_layout, |             common_bind_group_layout, | ||||||
|             preprocess_depth_bind_group_layout, |             preprocess_depth_bind_group_layout, | ||||||
|             gtao_bind_group_layout, |             ssao_bind_group_layout, | ||||||
|             spatial_denoise_bind_group_layout, |             spatial_denoise_bind_group_layout, | ||||||
| 
 | 
 | ||||||
|             hilbert_index_lut, |             hilbert_index_lut, | ||||||
|             point_clamp_sampler, |             point_clamp_sampler, | ||||||
|  |             linear_clamp_sampler, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(PartialEq, Eq, Hash, Clone)] | #[derive(PartialEq, Eq, Hash, Clone)] | ||||||
| struct SsaoPipelineKey { | struct SsaoPipelineKey { | ||||||
|     ssao_settings: ScreenSpaceAmbientOcclusion, |     quality_level: ScreenSpaceAmbientOcclusionQualityLevel, | ||||||
|     temporal_jitter: bool, |     temporal_jitter: bool, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -463,7 +490,7 @@ impl SpecializedComputePipeline for SsaoPipelines { | |||||||
|     type Key = SsaoPipelineKey; |     type Key = SsaoPipelineKey; | ||||||
| 
 | 
 | ||||||
|     fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor { |     fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor { | ||||||
|         let (slice_count, samples_per_slice_side) = key.ssao_settings.quality_level.sample_counts(); |         let (slice_count, samples_per_slice_side) = key.quality_level.sample_counts(); | ||||||
| 
 | 
 | ||||||
|         let mut shader_defs = vec![ |         let mut shader_defs = vec![ | ||||||
|             ShaderDefVal::Int("SLICE_COUNT".to_string(), slice_count as i32), |             ShaderDefVal::Int("SLICE_COUNT".to_string(), slice_count as i32), | ||||||
| @ -478,15 +505,15 @@ impl SpecializedComputePipeline for SsaoPipelines { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         ComputePipelineDescriptor { |         ComputePipelineDescriptor { | ||||||
|             label: Some("ssao_gtao_pipeline".into()), |             label: Some("ssao_ssao_pipeline".into()), | ||||||
|             layout: vec![ |             layout: vec![ | ||||||
|                 self.gtao_bind_group_layout.clone(), |                 self.ssao_bind_group_layout.clone(), | ||||||
|                 self.common_bind_group_layout.clone(), |                 self.common_bind_group_layout.clone(), | ||||||
|             ], |             ], | ||||||
|             push_constant_ranges: vec![], |             push_constant_ranges: vec![], | ||||||
|             shader: GTAO_SHADER_HANDLE, |             shader: SSAO_SHADER_HANDLE, | ||||||
|             shader_defs, |             shader_defs, | ||||||
|             entry_point: "gtao".into(), |             entry_point: "ssao".into(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -517,20 +544,21 @@ fn extract_ssao_settings( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Component)] | #[derive(Component)] | ||||||
| pub struct ScreenSpaceAmbientOcclusionTextures { | pub struct ScreenSpaceAmbientOcclusionResources { | ||||||
|     preprocessed_depth_texture: CachedTexture, |     preprocessed_depth_texture: CachedTexture, | ||||||
|     ssao_noisy_texture: CachedTexture, // Pre-spatially denoised texture
 |     ssao_noisy_texture: CachedTexture, // Pre-spatially denoised texture
 | ||||||
|     pub screen_space_ambient_occlusion_texture: CachedTexture, // Spatially denoised texture
 |     pub screen_space_ambient_occlusion_texture: CachedTexture, // Spatially denoised texture
 | ||||||
|     depth_differences_texture: CachedTexture, |     depth_differences_texture: CachedTexture, | ||||||
|  |     thickness_buffer: Buffer, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn prepare_ssao_textures( | fn prepare_ssao_textures( | ||||||
|     mut commands: Commands, |     mut commands: Commands, | ||||||
|     mut texture_cache: ResMut<TextureCache>, |     mut texture_cache: ResMut<TextureCache>, | ||||||
|     render_device: Res<RenderDevice>, |     render_device: Res<RenderDevice>, | ||||||
|     views: Query<(Entity, &ExtractedCamera), With<ScreenSpaceAmbientOcclusion>>, |     views: Query<(Entity, &ExtractedCamera, &ScreenSpaceAmbientOcclusion)>, | ||||||
| ) { | ) { | ||||||
|     for (entity, camera) in &views { |     for (entity, camera, ssao_settings) in &views { | ||||||
|         let Some(physical_viewport_size) = camera.physical_viewport_size else { |         let Some(physical_viewport_size) = camera.physical_viewport_size else { | ||||||
|             continue; |             continue; | ||||||
|         }; |         }; | ||||||
| @ -596,13 +624,20 @@ fn prepare_ssao_textures( | |||||||
|             }, |             }, | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|  |         let thickness_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { | ||||||
|  |             label: Some("thickness_buffer"), | ||||||
|  |             contents: &ssao_settings.constant_object_thickness.to_le_bytes(), | ||||||
|  |             usage: BufferUsages::UNIFORM, | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|         commands |         commands | ||||||
|             .entity(entity) |             .entity(entity) | ||||||
|             .insert(ScreenSpaceAmbientOcclusionTextures { |             .insert(ScreenSpaceAmbientOcclusionResources { | ||||||
|                 preprocessed_depth_texture, |                 preprocessed_depth_texture, | ||||||
|                 ssao_noisy_texture, |                 ssao_noisy_texture, | ||||||
|                 screen_space_ambient_occlusion_texture: ssao_texture, |                 screen_space_ambient_occlusion_texture: ssao_texture, | ||||||
|                 depth_differences_texture, |                 depth_differences_texture, | ||||||
|  |                 thickness_buffer, | ||||||
|             }); |             }); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -622,7 +657,7 @@ fn prepare_ssao_pipelines( | |||||||
|             &pipeline_cache, |             &pipeline_cache, | ||||||
|             &pipeline, |             &pipeline, | ||||||
|             SsaoPipelineKey { |             SsaoPipelineKey { | ||||||
|                 ssao_settings: ssao_settings.clone(), |                 quality_level: ssao_settings.quality_level, | ||||||
|                 temporal_jitter, |                 temporal_jitter, | ||||||
|             }, |             }, | ||||||
|         ); |         ); | ||||||
| @ -635,7 +670,7 @@ fn prepare_ssao_pipelines( | |||||||
| struct SsaoBindGroups { | struct SsaoBindGroups { | ||||||
|     common_bind_group: BindGroup, |     common_bind_group: BindGroup, | ||||||
|     preprocess_depth_bind_group: BindGroup, |     preprocess_depth_bind_group: BindGroup, | ||||||
|     gtao_bind_group: BindGroup, |     ssao_bind_group: BindGroup, | ||||||
|     spatial_denoise_bind_group: BindGroup, |     spatial_denoise_bind_group: BindGroup, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -647,7 +682,7 @@ fn prepare_ssao_bind_groups( | |||||||
|     global_uniforms: Res<GlobalsBuffer>, |     global_uniforms: Res<GlobalsBuffer>, | ||||||
|     views: Query<( |     views: Query<( | ||||||
|         Entity, |         Entity, | ||||||
|         &ScreenSpaceAmbientOcclusionTextures, |         &ScreenSpaceAmbientOcclusionResources, | ||||||
|         &ViewPrepassTextures, |         &ViewPrepassTextures, | ||||||
|     )>, |     )>, | ||||||
| ) { | ) { | ||||||
| @ -658,15 +693,19 @@ fn prepare_ssao_bind_groups( | |||||||
|         return; |         return; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     for (entity, ssao_textures, prepass_textures) in &views { |     for (entity, ssao_resources, prepass_textures) in &views { | ||||||
|         let common_bind_group = render_device.create_bind_group( |         let common_bind_group = render_device.create_bind_group( | ||||||
|             "ssao_common_bind_group", |             "ssao_common_bind_group", | ||||||
|             &pipelines.common_bind_group_layout, |             &pipelines.common_bind_group_layout, | ||||||
|             &BindGroupEntries::sequential((&pipelines.point_clamp_sampler, view_uniforms.clone())), |             &BindGroupEntries::sequential(( | ||||||
|  |                 &pipelines.point_clamp_sampler, | ||||||
|  |                 &pipelines.linear_clamp_sampler, | ||||||
|  |                 view_uniforms.clone(), | ||||||
|  |             )), | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         let create_depth_view = |mip_level| { |         let create_depth_view = |mip_level| { | ||||||
|             ssao_textures |             ssao_resources | ||||||
|                 .preprocessed_depth_texture |                 .preprocessed_depth_texture | ||||||
|                 .texture |                 .texture | ||||||
|                 .create_view(&TextureViewDescriptor { |                 .create_view(&TextureViewDescriptor { | ||||||
| @ -692,16 +731,17 @@ fn prepare_ssao_bind_groups( | |||||||
|             )), |             )), | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         let gtao_bind_group = render_device.create_bind_group( |         let ssao_bind_group = render_device.create_bind_group( | ||||||
|             "ssao_gtao_bind_group", |             "ssao_ssao_bind_group", | ||||||
|             &pipelines.gtao_bind_group_layout, |             &pipelines.ssao_bind_group_layout, | ||||||
|             &BindGroupEntries::sequential(( |             &BindGroupEntries::sequential(( | ||||||
|                 &ssao_textures.preprocessed_depth_texture.default_view, |                 &ssao_resources.preprocessed_depth_texture.default_view, | ||||||
|                 prepass_textures.normal_view().unwrap(), |                 prepass_textures.normal_view().unwrap(), | ||||||
|                 &pipelines.hilbert_index_lut, |                 &pipelines.hilbert_index_lut, | ||||||
|                 &ssao_textures.ssao_noisy_texture.default_view, |                 &ssao_resources.ssao_noisy_texture.default_view, | ||||||
|                 &ssao_textures.depth_differences_texture.default_view, |                 &ssao_resources.depth_differences_texture.default_view, | ||||||
|                 globals_uniforms.clone(), |                 globals_uniforms.clone(), | ||||||
|  |                 ssao_resources.thickness_buffer.as_entire_binding(), | ||||||
|             )), |             )), | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
| @ -709,9 +749,9 @@ fn prepare_ssao_bind_groups( | |||||||
|             "ssao_spatial_denoise_bind_group", |             "ssao_spatial_denoise_bind_group", | ||||||
|             &pipelines.spatial_denoise_bind_group_layout, |             &pipelines.spatial_denoise_bind_group_layout, | ||||||
|             &BindGroupEntries::sequential(( |             &BindGroupEntries::sequential(( | ||||||
|                 &ssao_textures.ssao_noisy_texture.default_view, |                 &ssao_resources.ssao_noisy_texture.default_view, | ||||||
|                 &ssao_textures.depth_differences_texture.default_view, |                 &ssao_resources.depth_differences_texture.default_view, | ||||||
|                 &ssao_textures |                 &ssao_resources | ||||||
|                     .screen_space_ambient_occlusion_texture |                     .screen_space_ambient_occlusion_texture | ||||||
|                     .default_view, |                     .default_view, | ||||||
|             )), |             )), | ||||||
| @ -720,7 +760,7 @@ fn prepare_ssao_bind_groups( | |||||||
|         commands.entity(entity).insert(SsaoBindGroups { |         commands.entity(entity).insert(SsaoBindGroups { | ||||||
|             common_bind_group, |             common_bind_group, | ||||||
|             preprocess_depth_bind_group, |             preprocess_depth_bind_group, | ||||||
|             gtao_bind_group, |             ssao_bind_group, | ||||||
|             spatial_denoise_bind_group, |             spatial_denoise_bind_group, | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -14,7 +14,8 @@ | |||||||
| @group(0) @binding(4) var preprocessed_depth_mip3: texture_storage_2d<r16float, write>; | @group(0) @binding(4) var preprocessed_depth_mip3: texture_storage_2d<r16float, write>; | ||||||
| @group(0) @binding(5) var preprocessed_depth_mip4: texture_storage_2d<r16float, write>; | @group(0) @binding(5) var preprocessed_depth_mip4: texture_storage_2d<r16float, write>; | ||||||
| @group(1) @binding(0) var point_clamp_sampler: sampler; | @group(1) @binding(0) var point_clamp_sampler: sampler; | ||||||
| @group(1) @binding(1) var<uniform> view: View; | @group(1) @binding(1) var linear_clamp_sampler: sampler; | ||||||
|  | @group(1) @binding(2) var<uniform> view: View; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| // Using 4 depths from the previous MIP, compute a weighted average for the depth of the current MIP | // Using 4 depths from the previous MIP, compute a weighted average for the depth of the current MIP | ||||||
|  | |||||||
| @ -15,7 +15,8 @@ | |||||||
| @group(0) @binding(1) var depth_differences: texture_2d<u32>; | @group(0) @binding(1) var depth_differences: texture_2d<u32>; | ||||||
| @group(0) @binding(2) var ambient_occlusion: texture_storage_2d<r16float, write>; | @group(0) @binding(2) var ambient_occlusion: texture_storage_2d<r16float, write>; | ||||||
| @group(1) @binding(0) var point_clamp_sampler: sampler; | @group(1) @binding(0) var point_clamp_sampler: sampler; | ||||||
| @group(1) @binding(1) var<uniform> view: View; | @group(1) @binding(1) var linear_clamp_sampler: sampler; | ||||||
|  | @group(1) @binding(2) var<uniform> view: View; | ||||||
| 
 | 
 | ||||||
| @compute | @compute | ||||||
| @workgroup_size(8, 8, 1) | @workgroup_size(8, 8, 1) | ||||||
|  | |||||||
| @ -1,11 +1,16 @@ | |||||||
| // Ground Truth-based Ambient Occlusion (GTAO) | // Visibility Bitmask Ambient Occlusion (VBAO) | ||||||
| // Paper: https://www.activision.com/cdn/research/Practical_Real_Time_Strategies_for_Accurate_Indirect_Occlusion_NEW%20VERSION_COLOR.pdf | // Paper: ttps://ar5iv.labs.arxiv.org/html/2301.11376 | ||||||
| // Presentation: https://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf |  | ||||||
| 
 | 
 | ||||||
| // Source code heavily based on XeGTAO v1.30 from Intel | // Source code heavily based on XeGTAO v1.30 from Intel | ||||||
| // https://github.com/GameTechDev/XeGTAO/blob/0d177ce06bfa642f64d8af4de1197ad1bcb862d4/Source/Rendering/Shaders/XeGTAO.hlsli | // https://github.com/GameTechDev/XeGTAO/blob/0d177ce06bfa642f64d8af4de1197ad1bcb862d4/Source/Rendering/Shaders/XeGTAO.hlsli | ||||||
| 
 | 
 | ||||||
| #import bevy_pbr::gtao_utils::fast_acos | // Source code based on the existing XeGTAO implementation and | ||||||
|  | // https://cdrinmatane.github.io/posts/ssaovb-code/ | ||||||
|  | 
 | ||||||
|  | // Source code base on SSRT3 implementation | ||||||
|  | // https://github.com/cdrinmatane/SSRT3 | ||||||
|  | 
 | ||||||
|  | #import bevy_pbr::ssao_utils::fast_acos | ||||||
| 
 | 
 | ||||||
| #import bevy_render::{ | #import bevy_render::{ | ||||||
|     view::View, |     view::View, | ||||||
| @ -19,8 +24,10 @@ | |||||||
| @group(0) @binding(3) var ambient_occlusion: texture_storage_2d<r16float, write>; | @group(0) @binding(3) var ambient_occlusion: texture_storage_2d<r16float, write>; | ||||||
| @group(0) @binding(4) var depth_differences: texture_storage_2d<r32uint, write>; | @group(0) @binding(4) var depth_differences: texture_storage_2d<r32uint, write>; | ||||||
| @group(0) @binding(5) var<uniform> globals: Globals; | @group(0) @binding(5) var<uniform> globals: Globals; | ||||||
|  | @group(0) @binding(6) var<uniform> thickness: f32; | ||||||
| @group(1) @binding(0) var point_clamp_sampler: sampler; | @group(1) @binding(0) var point_clamp_sampler: sampler; | ||||||
| @group(1) @binding(1) var<uniform> view: View; | @group(1) @binding(1) var linear_clamp_sampler: sampler; | ||||||
|  | @group(1) @binding(2) var<uniform> view: View; | ||||||
| 
 | 
 | ||||||
| fn load_noise(pixel_coordinates: vec2<i32>) -> vec2<f32> { | fn load_noise(pixel_coordinates: vec2<i32>) -> vec2<f32> { | ||||||
|     var index = textureLoad(hilbert_index_lut, pixel_coordinates % 64, 0).r; |     var index = textureLoad(hilbert_index_lut, pixel_coordinates % 64, 0).r; | ||||||
| @ -81,13 +88,46 @@ fn reconstruct_view_space_position(depth: f32, uv: vec2<f32>) -> vec3<f32> { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn load_and_reconstruct_view_space_position(uv: vec2<f32>, sample_mip_level: f32) -> vec3<f32> { | fn load_and_reconstruct_view_space_position(uv: vec2<f32>, sample_mip_level: f32) -> vec3<f32> { | ||||||
|     let depth = textureSampleLevel(preprocessed_depth, point_clamp_sampler, uv, sample_mip_level).r; |     let depth = textureSampleLevel(preprocessed_depth, linear_clamp_sampler, uv, sample_mip_level).r; | ||||||
|     return reconstruct_view_space_position(depth, uv); |     return reconstruct_view_space_position(depth, uv); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | fn updateSectors( | ||||||
|  |     min_horizon: f32, | ||||||
|  |     max_horizon: f32, | ||||||
|  |     samples_per_slice: f32, | ||||||
|  |     bitmask: u32, | ||||||
|  | ) -> u32 { | ||||||
|  |     let start_horizon = u32(min_horizon * samples_per_slice); | ||||||
|  |     let angle_horizon = u32(ceil((max_horizon - min_horizon) * samples_per_slice)); | ||||||
|  | 
 | ||||||
|  |     return insertBits(bitmask, 0xFFFFFFFFu, start_horizon, angle_horizon); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn processSample( | ||||||
|  |     delta_position: vec3<f32>, | ||||||
|  |     view_vec: vec3<f32>, | ||||||
|  |     sampling_direction: f32, | ||||||
|  |     n: vec2<f32>, | ||||||
|  |     samples_per_slice: f32, | ||||||
|  |     bitmask: ptr<function, u32>, | ||||||
|  | ) { | ||||||
|  |     let delta_position_back_face = delta_position - view_vec * thickness; | ||||||
|  | 
 | ||||||
|  |     var front_back_horizon = vec2( | ||||||
|  |         fast_acos(dot(normalize(delta_position), view_vec)), | ||||||
|  |         fast_acos(dot(normalize(delta_position_back_face), view_vec)), | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     front_back_horizon = saturate(fma(vec2(sampling_direction), -front_back_horizon, n)); | ||||||
|  |     front_back_horizon = select(front_back_horizon.xy, front_back_horizon.yx, sampling_direction >= 0.0); | ||||||
|  | 
 | ||||||
|  |     *bitmask = updateSectors(front_back_horizon.x, front_back_horizon.y, samples_per_slice, *bitmask); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| @compute | @compute | ||||||
| @workgroup_size(8, 8, 1) | @workgroup_size(8, 8, 1) | ||||||
| fn gtao(@builtin(global_invocation_id) global_id: vec3<u32>) { | fn ssao(@builtin(global_invocation_id) global_id: vec3<u32>) { | ||||||
|     let slice_count = f32(#SLICE_COUNT); |     let slice_count = f32(#SLICE_COUNT); | ||||||
|     let samples_per_slice_side = f32(#SAMPLES_PER_SLICE_SIDE); |     let samples_per_slice_side = f32(#SAMPLES_PER_SLICE_SIDE); | ||||||
|     let effect_radius = 0.5 * 1.457; |     let effect_radius = 0.5 * 1.457; | ||||||
| @ -110,6 +150,7 @@ fn gtao(@builtin(global_invocation_id) global_id: vec3<u32>) { | |||||||
|     let sample_scale = (-0.5 * effect_radius * view.clip_from_view[0][0]) / pixel_position.z; |     let sample_scale = (-0.5 * effect_radius * view.clip_from_view[0][0]) / pixel_position.z; | ||||||
| 
 | 
 | ||||||
|     var visibility = 0.0; |     var visibility = 0.0; | ||||||
|  |     var occluded_sample_count = 0u; | ||||||
|     for (var slice_t = 0.0; slice_t < slice_count; slice_t += 1.0) { |     for (var slice_t = 0.0; slice_t < slice_count; slice_t += 1.0) { | ||||||
|         let slice = slice_t + noise.x; |         let slice = slice_t + noise.x; | ||||||
|         let phi = (PI / slice_count) * slice; |         let phi = (PI / slice_count) * slice; | ||||||
| @ -123,12 +164,10 @@ fn gtao(@builtin(global_invocation_id) global_id: vec3<u32>) { | |||||||
| 
 | 
 | ||||||
|         let sign_norm = sign(dot(orthographic_direction, projected_normal)); |         let sign_norm = sign(dot(orthographic_direction, projected_normal)); | ||||||
|         let cos_norm = saturate(dot(projected_normal, view_vec) / projected_normal_length); |         let cos_norm = saturate(dot(projected_normal, view_vec) / projected_normal_length); | ||||||
|         let n = sign_norm * fast_acos(cos_norm); |         let n = vec2((HALF_PI - sign_norm * fast_acos(cos_norm)) * (1.0 / PI)); | ||||||
|  | 
 | ||||||
|  |         var bitmask = 0u; | ||||||
| 
 | 
 | ||||||
|         let min_cos_horizon_1 = cos(n + HALF_PI); |  | ||||||
|         let min_cos_horizon_2 = cos(n - HALF_PI); |  | ||||||
|         var cos_horizon_1 = min_cos_horizon_1; |  | ||||||
|         var cos_horizon_2 = min_cos_horizon_2; |  | ||||||
|         let sample_mul = vec2<f32>(omega.x, -omega.y) * sample_scale; |         let sample_mul = vec2<f32>(omega.x, -omega.y) * sample_scale; | ||||||
|         for (var sample_t = 0.0; sample_t < samples_per_slice_side; sample_t += 1.0) { |         for (var sample_t = 0.0; sample_t < samples_per_slice_side; sample_t += 1.0) { | ||||||
|             var sample_noise = (slice_t + sample_t * samples_per_slice_side) * 0.6180339887498948482; |             var sample_noise = (slice_t + sample_t * samples_per_slice_side) * 0.6180339887498948482; | ||||||
| @ -145,27 +184,16 @@ fn gtao(@builtin(global_invocation_id) global_id: vec3<u32>) { | |||||||
| 
 | 
 | ||||||
|             let sample_difference_1 = sample_position_1 - pixel_position; |             let sample_difference_1 = sample_position_1 - pixel_position; | ||||||
|             let sample_difference_2 = sample_position_2 - pixel_position; |             let sample_difference_2 = sample_position_2 - pixel_position; | ||||||
|             let sample_distance_1 = length(sample_difference_1); |  | ||||||
|             let sample_distance_2 = length(sample_difference_2); |  | ||||||
|             var sample_cos_horizon_1 = dot(sample_difference_1 / sample_distance_1, view_vec); |  | ||||||
|             var sample_cos_horizon_2 = dot(sample_difference_2 / sample_distance_2, view_vec); |  | ||||||
| 
 | 
 | ||||||
|             let weight_1 = saturate(sample_distance_1 * falloff_mul + falloff_add); |             processSample(sample_difference_1, view_vec, -1.0, n, samples_per_slice_side * 2.0, &bitmask); | ||||||
|             let weight_2 = saturate(sample_distance_2 * falloff_mul + falloff_add); |             processSample(sample_difference_2, view_vec, 1.0, n, samples_per_slice_side * 2.0, &bitmask); | ||||||
|             sample_cos_horizon_1 = mix(min_cos_horizon_1, sample_cos_horizon_1, weight_1); |  | ||||||
|             sample_cos_horizon_2 = mix(min_cos_horizon_2, sample_cos_horizon_2, weight_2); |  | ||||||
| 
 |  | ||||||
|             cos_horizon_1 = max(cos_horizon_1, sample_cos_horizon_1); |  | ||||||
|             cos_horizon_2 = max(cos_horizon_2, sample_cos_horizon_2); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let horizon_1 = fast_acos(cos_horizon_1); |         occluded_sample_count += countOneBits(bitmask); | ||||||
|         let horizon_2 = -fast_acos(cos_horizon_2); |  | ||||||
|         let v1 = (cos_norm + 2.0 * horizon_1 * sin(n) - cos(2.0 * horizon_1 - n)) / 4.0; |  | ||||||
|         let v2 = (cos_norm + 2.0 * horizon_2 * sin(n) - cos(2.0 * horizon_2 - n)) / 4.0; |  | ||||||
|         visibility += projected_normal_length * (v1 + v2); |  | ||||||
|     } |     } | ||||||
|     visibility /= slice_count; | 
 | ||||||
|  |     visibility = 1.0 - f32(occluded_sample_count) / (slice_count * 2.0 * samples_per_slice_side); | ||||||
|  | 
 | ||||||
|     visibility = clamp(visibility, 0.03, 1.0); |     visibility = clamp(visibility, 0.03, 1.0); | ||||||
| 
 | 
 | ||||||
|     textureStore(ambient_occlusion, pixel_coordinates, vec4<f32>(visibility, 0.0, 0.0, 0.0)); |     textureStore(ambient_occlusion, pixel_coordinates, vec4<f32>(visibility, 0.0, 0.0, 0.0)); | ||||||
| @ -1,10 +1,10 @@ | |||||||
| #define_import_path bevy_pbr::gtao_utils | #define_import_path bevy_pbr::ssao_utils | ||||||
| 
 | 
 | ||||||
| #import bevy_render::maths::{PI, HALF_PI} | #import bevy_render::maths::{PI, HALF_PI} | ||||||
| 
 | 
 | ||||||
| // Approximates single-bounce ambient occlusion to multi-bounce ambient occlusion | // Approximates single-bounce ambient occlusion to multi-bounce ambient occlusion | ||||||
| // https://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf#page=78 | // https://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf#page=78 | ||||||
| fn gtao_multibounce(visibility: f32, base_color: vec3<f32>) -> vec3<f32> { | fn ssao_multibounce(visibility: f32, base_color: vec3<f32>) -> vec3<f32> { | ||||||
|     let a = 2.0404 * base_color - 0.3324; |     let a = 2.0404 * base_color - 0.3324; | ||||||
|     let b = -4.7951 * base_color + 0.6417; |     let b = -4.7951 * base_color + 0.6417; | ||||||
|     let c = 2.7552 * base_color + 0.6903; |     let c = 2.7552 * base_color + 0.6903; | ||||||
| @ -109,32 +109,52 @@ fn update( | |||||||
|     sphere.translation.y = ops::sin(time.elapsed_seconds() / 1.7) * 0.7; |     sphere.translation.y = ops::sin(time.elapsed_seconds() / 1.7) * 0.7; | ||||||
| 
 | 
 | ||||||
|     let (camera_entity, ssao, temporal_jitter) = camera.single(); |     let (camera_entity, ssao, temporal_jitter) = camera.single(); | ||||||
|  |     let current_ssao = ssao.cloned().unwrap_or_default(); | ||||||
| 
 | 
 | ||||||
|     let mut commands = commands.entity(camera_entity); |     let mut commands = commands.entity(camera_entity); | ||||||
|     commands |     commands | ||||||
|         .insert_if( |         .insert_if( | ||||||
|             ScreenSpaceAmbientOcclusion { |             ScreenSpaceAmbientOcclusion { | ||||||
|                 quality_level: ScreenSpaceAmbientOcclusionQualityLevel::Low, |                 quality_level: ScreenSpaceAmbientOcclusionQualityLevel::Low, | ||||||
|  |                 ..current_ssao | ||||||
|             }, |             }, | ||||||
|             || keycode.just_pressed(KeyCode::Digit2), |             || keycode.just_pressed(KeyCode::Digit2), | ||||||
|         ) |         ) | ||||||
|         .insert_if( |         .insert_if( | ||||||
|             ScreenSpaceAmbientOcclusion { |             ScreenSpaceAmbientOcclusion { | ||||||
|                 quality_level: ScreenSpaceAmbientOcclusionQualityLevel::Medium, |                 quality_level: ScreenSpaceAmbientOcclusionQualityLevel::Medium, | ||||||
|  |                 ..current_ssao | ||||||
|             }, |             }, | ||||||
|             || keycode.just_pressed(KeyCode::Digit3), |             || keycode.just_pressed(KeyCode::Digit3), | ||||||
|         ) |         ) | ||||||
|         .insert_if( |         .insert_if( | ||||||
|             ScreenSpaceAmbientOcclusion { |             ScreenSpaceAmbientOcclusion { | ||||||
|                 quality_level: ScreenSpaceAmbientOcclusionQualityLevel::High, |                 quality_level: ScreenSpaceAmbientOcclusionQualityLevel::High, | ||||||
|  |                 ..current_ssao | ||||||
|             }, |             }, | ||||||
|             || keycode.just_pressed(KeyCode::Digit4), |             || keycode.just_pressed(KeyCode::Digit4), | ||||||
|         ) |         ) | ||||||
|         .insert_if( |         .insert_if( | ||||||
|             ScreenSpaceAmbientOcclusion { |             ScreenSpaceAmbientOcclusion { | ||||||
|                 quality_level: ScreenSpaceAmbientOcclusionQualityLevel::Ultra, |                 quality_level: ScreenSpaceAmbientOcclusionQualityLevel::Ultra, | ||||||
|  |                 ..current_ssao | ||||||
|             }, |             }, | ||||||
|             || keycode.just_pressed(KeyCode::Digit5), |             || keycode.just_pressed(KeyCode::Digit5), | ||||||
|  |         ) | ||||||
|  |         .insert_if( | ||||||
|  |             ScreenSpaceAmbientOcclusion { | ||||||
|  |                 constant_object_thickness: (current_ssao.constant_object_thickness * 2.0).min(4.0), | ||||||
|  |                 ..current_ssao | ||||||
|  |             }, | ||||||
|  |             || keycode.just_pressed(KeyCode::ArrowUp), | ||||||
|  |         ) | ||||||
|  |         .insert_if( | ||||||
|  |             ScreenSpaceAmbientOcclusion { | ||||||
|  |                 constant_object_thickness: (current_ssao.constant_object_thickness * 0.5) | ||||||
|  |                     .max(0.0625), | ||||||
|  |                 ..current_ssao | ||||||
|  |             }, | ||||||
|  |             || keycode.just_pressed(KeyCode::ArrowDown), | ||||||
|         ); |         ); | ||||||
|     if keycode.just_pressed(KeyCode::Digit1) { |     if keycode.just_pressed(KeyCode::Digit1) { | ||||||
|         commands.remove::<ScreenSpaceAmbientOcclusion>(); |         commands.remove::<ScreenSpaceAmbientOcclusion>(); | ||||||
| @ -160,6 +180,13 @@ fn update( | |||||||
|         _ => unreachable!(), |         _ => unreachable!(), | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     if let Some(thickness) = ssao.map(|s| s.constant_object_thickness) { | ||||||
|  |         text.push_str(&format!( | ||||||
|  |             "Constant object thickness: {} (Up/Down)\n\n", | ||||||
|  |             thickness | ||||||
|  |         )); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     text.push_str("SSAO Quality:\n"); |     text.push_str("SSAO Quality:\n"); | ||||||
|     text.push_str(&format!("(1) {o}Off{o}\n")); |     text.push_str(&format!("(1) {o}Off{o}\n")); | ||||||
|     text.push_str(&format!("(2) {l}Low{l}\n")); |     text.push_str(&format!("(2) {l}Low{l}\n")); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Dragoș Tiselice
						Dragoș Tiselice