From 461fb136c9bdff0a9924234c5206fdae2026ceac Mon Sep 17 00:00:00 2001 From: Aevyrie Date: Sat, 30 Dec 2023 13:40:27 -0800 Subject: [PATCH 1/6] Attenuate ambient specular indirect light with ambient occlusion --- crates/bevy_pbr/src/render/pbr_functions.wgsl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index 3f2e8c661f609..2268272d298b5 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -323,7 +323,13 @@ fn apply_pbr_lighting( // Environment map light (indirect) #ifdef ENVIRONMENT_MAP let environment_light = environment_map::environment_map_light(perceptual_roughness, roughness, diffuse_color, NdotV, f_ab, in.N, R, F0); - indirect_light += (environment_light.diffuse * occlusion) + environment_light.specular; + // This is technically not physically corrent. Ambient occlusion should only attenuate the + // diffuse term, not the specular term. However, without ray traced or screen space reflections, + // the physically correct math will tend to over-brighten specular,as it completely ignores + // occlusion. This results in, for example, a wheel in a vehicle's wheel well appearing to glow + // because it is not accounting for the fact that light cannot reach the wheel. Thus, ambient + // occlusion serves as an imperfect but decent *approximation* of specular occlusion. + indirect_light += (environment_light.diffuse + environment_light.specular) * occlusion; // we'll use the specular component of the transmitted environment // light in the call to `specular_transmissive_light()` below From 4eed87b061485fdc1c13fb9999c2c7e3230dfa56 Mon Sep 17 00:00:00 2001 From: Aevyrie Date: Tue, 2 Jan 2024 18:50:36 -0800 Subject: [PATCH 2/6] Implement Lagarde specular occlusion --- .../src/deferred/deferred_lighting.wgsl | 11 ++++++++++- .../src/deferred/pbr_deferred_functions.wgsl | 8 ++++---- crates/bevy_pbr/src/render/pbr_fragment.wgsl | 19 +++++++++++++------ crates/bevy_pbr/src/render/pbr_functions.wgsl | 17 ++++++----------- crates/bevy_pbr/src/render/pbr_types.wgsl | 6 ++++-- 5 files changed, 37 insertions(+), 24 deletions(-) diff --git a/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl b/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl index bf837bab55934..fa9cde9cf2183 100644 --- a/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl +++ b/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl @@ -4,6 +4,7 @@ pbr_functions, pbr_deferred_functions::pbr_input_from_deferred_gbuffer, pbr_deferred_types::unpack_unorm3x4_plus_unorm_20_, + lighting, mesh_view_bindings::deferred_prepass_texture, } @@ -64,7 +65,15 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2(in.position.xy), 0i).r; let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb); - pbr_input.occlusion = min(pbr_input.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" + let NdotV = max(dot(pbr_input.N, pbr_input.V), 0.0001); + var perceptual_roughness: f32 = pbr_input.material.perceptual_roughness; + let roughness = lighting::perceptualRoughnessToRoughness(perceptual_roughness); + + // Use ssao to estimate the specular occlusion. [Lagarde et al., 2014] + pbr_input.specular_occlusion = clamp(pow(NdotV + ssao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ssao, 0.0, 1.0); #endif // SCREEN_SPACE_AMBIENT_OCCLUSION output_color = pbr_functions::apply_pbr_lighting(pbr_input); diff --git a/crates/bevy_pbr/src/deferred/pbr_deferred_functions.wgsl b/crates/bevy_pbr/src/deferred/pbr_deferred_functions.wgsl index d401805eb9d18..7c2696f6c48dc 100644 --- a/crates/bevy_pbr/src/deferred/pbr_deferred_functions.wgsl +++ b/crates/bevy_pbr/src/deferred/pbr_deferred_functions.wgsl @@ -22,18 +22,18 @@ fn deferred_gbuffer_from_pbr_input(in: PbrInput) -> vec4 { // Real time occlusion is applied in the deferred lighting pass. // Deriving luminance via Rec. 709. coefficients // https://en.wikipedia.org/wiki/Rec._709 - let occlusion = dot(in.occlusion, vec3(0.2126, 0.7152, 0.0722)); + let diffuse_occlusion = dot(in.diffuse_occlusion, vec3(0.2126, 0.7152, 0.0722)); #ifdef WEBGL2 // More crunched for webgl so we can also fit depth. var props = deferred_types::pack_unorm3x4_plus_unorm_20_(vec4( in.material.reflectance, in.material.metallic, - occlusion, + diffuse_occlusion, in.frag_coord.z)); #else var props = deferred_types::pack_unorm4x8_(vec4( in.material.reflectance, // could be fewer bits in.material.metallic, // could be fewer bits - occlusion, // is this worth including? + diffuse_occlusion, // is this worth including? 0.0)); // spare #endif // WEBGL2 let flags = deferred_types::deferred_flags_from_mesh_material_flags(in.flags, in.material.flags); @@ -85,7 +85,7 @@ fn pbr_input_from_deferred_gbuffer(frag_coord: vec4, gbuffer: vec4) -> pbr.material.reflectance = props.r; #endif // WEBGL2 pbr.material.metallic = props.g; - pbr.occlusion = vec3(props.b); + pbr.diffuse_occlusion = vec3(props.b); let octahedral_normal = deferred_types::unpack_24bit_normal(gbuffer.a); let N = octahedral_decode(octahedral_normal); diff --git a/crates/bevy_pbr/src/render/pbr_fragment.wgsl b/crates/bevy_pbr/src/render/pbr_fragment.wgsl index acc1b5942178f..1e81eedd4cffb 100644 --- a/crates/bevy_pbr/src/render/pbr_fragment.wgsl +++ b/crates/bevy_pbr/src/render/pbr_fragment.wgsl @@ -5,6 +5,7 @@ pbr_bindings, pbr_types, prepass_utils, + lighting, mesh_bindings::mesh, mesh_view_bindings::view, parallax_mapping::parallaxed_uv, @@ -68,6 +69,9 @@ fn pbr_input_from_standard_material( pbr_input.material.base_color *= pbr_bindings::material.base_color; pbr_input.material.deferred_lighting_pass_id = pbr_bindings::material.deferred_lighting_pass_id; + // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886" + let NdotV = max(dot(pbr_input.N, pbr_input.V), 0.0001); + #ifdef VERTEX_UVS var uv = in.uv; @@ -120,6 +124,7 @@ fn pbr_input_from_standard_material( // metallic and perceptual roughness var metallic: f32 = pbr_bindings::material.metallic; var perceptual_roughness: f32 = pbr_bindings::material.perceptual_roughness; + let roughness = lighting::perceptualRoughnessToRoughness(perceptual_roughness); #ifdef VERTEX_UVS if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) { let metallic_roughness = textureSampleBias(pbr_bindings::metallic_roughness_texture, pbr_bindings::metallic_roughness_sampler, uv, view.mip_bias); @@ -159,20 +164,22 @@ fn pbr_input_from_standard_material( #endif pbr_input.material.diffuse_transmission = diffuse_transmission; - // occlusion - // TODO: Split into diffuse/specular occlusion? - var occlusion: vec3 = vec3(1.0); + var diffuse_occlusion: vec3 = vec3(1.0); + var specular_occlusion: f32 = 1.0; #ifdef VERTEX_UVS if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) { - occlusion = vec3(textureSampleBias(pbr_bindings::occlusion_texture, pbr_bindings::occlusion_sampler, uv, view.mip_bias).r); + diffuse_occlusion = vec3(textureSampleBias(pbr_bindings::occlusion_texture, pbr_bindings::occlusion_sampler, uv, view.mip_bias).r); } #endif #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2(in.position.xy), 0i).r; let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb); - occlusion = min(occlusion, ssao_multibounce); + diffuse_occlusion = min(diffuse_occlusion, ssao_multibounce); + // Use ssao to estimate the specular occlusion. [Lagarde et al., 2014] + specular_occlusion = clamp(pow(NdotV + ssao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ssao, 0.0, 1.0); #endif - pbr_input.occlusion = occlusion; + pbr_input.diffuse_occlusion = diffuse_occlusion; + pbr_input.specular_occlusion = specular_occlusion; // N (normal vector) #ifndef LOAD_PREPASS_NORMALS diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index 2268272d298b5..093188d8f3abc 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -164,7 +164,8 @@ fn apply_pbr_lighting( let specular_transmissive_color = specular_transmission * in.material.base_color.rgb; - let occlusion = in.occlusion; + let diffuse_occlusion = in.diffuse_occlusion; + let specular_occlusion = in.specular_occlusion; // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886" let NdotV = max(dot(in.N, in.V), 0.0001); @@ -306,7 +307,7 @@ fn apply_pbr_lighting( } // Ambient light (indirect) - var indirect_light = ambient::ambient_light(in.world_position, in.N, in.V, NdotV, diffuse_color, F0, perceptual_roughness, occlusion); + var indirect_light = ambient::ambient_light(in.world_position, in.N, in.V, NdotV, diffuse_color, F0, perceptual_roughness, diffuse_occlusion); if diffuse_transmission > 0.0 { // NOTE: We use the diffuse transmissive color, the second Lambertian lobe's calculated @@ -316,20 +317,14 @@ fn apply_pbr_lighting( // perceptual_roughness = 1.0; // NdotV = 1.0; // F0 = vec3(0.0) - // occlusion = vec3(1.0) + // diffuse_occlusion = vec3(1.0) transmitted_light += ambient::ambient_light(diffuse_transmissive_lobe_world_position, -in.N, -in.V, 1.0, diffuse_transmissive_color, vec3(0.0), 1.0, vec3(1.0)); } // Environment map light (indirect) #ifdef ENVIRONMENT_MAP let environment_light = environment_map::environment_map_light(perceptual_roughness, roughness, diffuse_color, NdotV, f_ab, in.N, R, F0); - // This is technically not physically corrent. Ambient occlusion should only attenuate the - // diffuse term, not the specular term. However, without ray traced or screen space reflections, - // the physically correct math will tend to over-brighten specular,as it completely ignores - // occlusion. This results in, for example, a wheel in a vehicle's wheel well appearing to glow - // because it is not accounting for the fact that light cannot reach the wheel. Thus, ambient - // occlusion serves as an imperfect but decent *approximation* of specular occlusion. - indirect_light += (environment_light.diffuse + environment_light.specular) * occlusion; + indirect_light += (environment_light.diffuse * diffuse_occlusion) + (environment_light.specular * specular_occlusion); // we'll use the specular component of the transmitted environment // light in the call to `specular_transmissive_light()` below @@ -344,7 +339,7 @@ fn apply_pbr_lighting( // NdotV = 1.0; // R = T // see definition below // F0 = vec3(1.0) - // occlusion = 1.0 + // diffuse_occlusion = 1.0 // // (This one is slightly different from the other light types above, because the environment // map light returns both diffuse and specular components separately, and we want to use both) diff --git a/crates/bevy_pbr/src/render/pbr_types.wgsl b/crates/bevy_pbr/src/render/pbr_types.wgsl index a4bde633331fc..cd7170275b2f5 100644 --- a/crates/bevy_pbr/src/render/pbr_types.wgsl +++ b/crates/bevy_pbr/src/render/pbr_types.wgsl @@ -80,7 +80,8 @@ fn standard_material_new() -> StandardMaterial { struct PbrInput { material: StandardMaterial, - occlusion: vec3, + diffuse_occlusion: vec3, + specular_occlusion: f32, frag_coord: vec4, world_position: vec4, // Normalized world normal used for shadow mapping as normal-mapping is not used for shadow @@ -101,7 +102,8 @@ fn pbr_input_new() -> PbrInput { var pbr_input: PbrInput; pbr_input.material = standard_material_new(); - pbr_input.occlusion = vec3(1.0); + pbr_input.diffuse_occlusion = vec3(1.0); + pbr_input.specular_occlusion = 1.0; pbr_input.frag_coord = vec4(0.0, 0.0, 0.0, 1.0); pbr_input.world_position = vec4(0.0, 0.0, 0.0, 1.0); From 5c11ded6c95a6ca5e0b8008dccdc461e22b02ca5 Mon Sep 17 00:00:00 2001 From: Aevyrie Date: Wed, 3 Jan 2024 01:02:50 -0800 Subject: [PATCH 3/6] Apply review feedback --- crates/bevy_pbr/src/deferred/deferred_lighting.wgsl | 4 ++-- crates/bevy_pbr/src/render/pbr_fragment.wgsl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl b/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl index fa9cde9cf2183..c8182362cd253 100644 --- a/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl +++ b/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl @@ -72,8 +72,8 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { var perceptual_roughness: f32 = pbr_input.material.perceptual_roughness; let roughness = lighting::perceptualRoughnessToRoughness(perceptual_roughness); - // Use ssao to estimate the specular occlusion. [Lagarde et al., 2014] - pbr_input.specular_occlusion = clamp(pow(NdotV + ssao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ssao, 0.0, 1.0); + // Use SSAO to estimate the specular occlusion. [Lagarde et al., 2014] + pbr_input.specular_occlusion = saturate(pow(NdotV + ssao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ssao); #endif // SCREEN_SPACE_AMBIENT_OCCLUSION output_color = pbr_functions::apply_pbr_lighting(pbr_input); diff --git a/crates/bevy_pbr/src/render/pbr_fragment.wgsl b/crates/bevy_pbr/src/render/pbr_fragment.wgsl index 1e81eedd4cffb..3151fea42b1a2 100644 --- a/crates/bevy_pbr/src/render/pbr_fragment.wgsl +++ b/crates/bevy_pbr/src/render/pbr_fragment.wgsl @@ -175,8 +175,8 @@ fn pbr_input_from_standard_material( let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2(in.position.xy), 0i).r; let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb); diffuse_occlusion = min(diffuse_occlusion, ssao_multibounce); - // Use ssao to estimate the specular occlusion. [Lagarde et al., 2014] - specular_occlusion = clamp(pow(NdotV + ssao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ssao, 0.0, 1.0); + // Use SSAO to estimate the specular occlusion. [Lagarde et al., 2014] + specular_occlusion = saturate(pow(NdotV + ssao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ssao); #endif pbr_input.diffuse_occlusion = diffuse_occlusion; pbr_input.specular_occlusion = specular_occlusion; From 73766801a4a5c6e4beaebc3190ffab0ca560a16a Mon Sep 17 00:00:00 2001 From: Aevyrie Date: Wed, 3 Jan 2024 20:17:28 -0800 Subject: [PATCH 4/6] Add paper title to source --- crates/bevy_pbr/src/deferred/deferred_lighting.wgsl | 3 ++- crates/bevy_pbr/src/render/pbr_fragment.wgsl | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl b/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl index c8182362cd253..e098ab3eff760 100644 --- a/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl +++ b/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl @@ -72,7 +72,8 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { var perceptual_roughness: f32 = pbr_input.material.perceptual_roughness; let roughness = lighting::perceptualRoughnessToRoughness(perceptual_roughness); - // Use SSAO to estimate the specular occlusion. [Lagarde et al., 2014] + // Use SSAO to estimate the specular occlusion. + // Lagarde and Rousiers 2014, "Moving Frostbite to Physically Based Rendering" pbr_input.specular_occlusion = saturate(pow(NdotV + ssao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ssao); #endif // SCREEN_SPACE_AMBIENT_OCCLUSION diff --git a/crates/bevy_pbr/src/render/pbr_fragment.wgsl b/crates/bevy_pbr/src/render/pbr_fragment.wgsl index 3151fea42b1a2..169d42f0a6f81 100644 --- a/crates/bevy_pbr/src/render/pbr_fragment.wgsl +++ b/crates/bevy_pbr/src/render/pbr_fragment.wgsl @@ -175,7 +175,8 @@ fn pbr_input_from_standard_material( let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2(in.position.xy), 0i).r; let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb); diffuse_occlusion = min(diffuse_occlusion, ssao_multibounce); - // Use SSAO to estimate the specular occlusion. [Lagarde et al., 2014] + // Use SSAO to estimate the specular occlusion. + // Lagarde and Rousiers 2014, "Moving Frostbite to Physically Based Rendering" specular_occlusion = saturate(pow(NdotV + ssao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ssao); #endif pbr_input.diffuse_occlusion = diffuse_occlusion; From 0ea07e33f2b8a7d49efa0e34b3a55bfdeca45bdb Mon Sep 17 00:00:00 2001 From: Aevyrie Date: Wed, 3 Jan 2024 20:27:29 -0800 Subject: [PATCH 5/6] Use min of baked ao in specular occlusion calc --- crates/bevy_pbr/src/deferred/deferred_lighting.wgsl | 5 ++++- crates/bevy_pbr/src/render/pbr_fragment.wgsl | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl b/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl index e098ab3eff760..f2a64d45cce73 100644 --- a/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl +++ b/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl @@ -63,15 +63,18 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { if ((pbr_input.material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) { #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION + let baked_ao = pbr_input.diffuse_occlusion; let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2(in.position.xy), 0i).r; let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb); - pbr_input.diffuse_occlusion = min(pbr_input.diffuse_occlusion, ssao_multibounce); + pbr_input.diffuse_occlusion = min(baked_ao, ssao_multibounce); // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886" let NdotV = max(dot(pbr_input.N, pbr_input.V), 0.0001); var perceptual_roughness: f32 = pbr_input.material.perceptual_roughness; let roughness = lighting::perceptualRoughnessToRoughness(perceptual_roughness); + // Use the min (most occluded) between screen space and baked AO: + let ao = min(baked_ao.r, ssao); // Use SSAO to estimate the specular occlusion. // Lagarde and Rousiers 2014, "Moving Frostbite to Physically Based Rendering" pbr_input.specular_occlusion = saturate(pow(NdotV + ssao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ssao); diff --git a/crates/bevy_pbr/src/render/pbr_fragment.wgsl b/crates/bevy_pbr/src/render/pbr_fragment.wgsl index 169d42f0a6f81..fa345eb7680b6 100644 --- a/crates/bevy_pbr/src/render/pbr_fragment.wgsl +++ b/crates/bevy_pbr/src/render/pbr_fragment.wgsl @@ -175,9 +175,12 @@ fn pbr_input_from_standard_material( let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2(in.position.xy), 0i).r; let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb); diffuse_occlusion = min(diffuse_occlusion, ssao_multibounce); + + // Use the min (most occluded) between screen space and baked AO: + let ao = min(diffuse_occlusion.r, ssao); // Use SSAO to estimate the specular occlusion. // Lagarde and Rousiers 2014, "Moving Frostbite to Physically Based Rendering" - specular_occlusion = saturate(pow(NdotV + ssao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ssao); + specular_occlusion = saturate(pow(NdotV + ao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ssao); #endif pbr_input.diffuse_occlusion = diffuse_occlusion; pbr_input.specular_occlusion = specular_occlusion; From f0eb179045003a5b7356863c8d911673575fb877 Mon Sep 17 00:00:00 2001 From: Aevyrie Date: Wed, 3 Jan 2024 21:01:26 -0800 Subject: [PATCH 6/6] Remove baked AO min --- crates/bevy_pbr/src/deferred/deferred_lighting.wgsl | 6 +----- crates/bevy_pbr/src/render/pbr_fragment.wgsl | 5 +---- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl b/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl index f2a64d45cce73..de191ce295b6b 100644 --- a/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl +++ b/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl @@ -63,18 +63,14 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { if ((pbr_input.material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) { #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION - let baked_ao = pbr_input.diffuse_occlusion; let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2(in.position.xy), 0i).r; let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb); - pbr_input.diffuse_occlusion = min(baked_ao, 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" let NdotV = max(dot(pbr_input.N, pbr_input.V), 0.0001); var perceptual_roughness: f32 = pbr_input.material.perceptual_roughness; let roughness = lighting::perceptualRoughnessToRoughness(perceptual_roughness); - - // Use the min (most occluded) between screen space and baked AO: - let ao = min(baked_ao.r, ssao); // Use SSAO to estimate the specular occlusion. // Lagarde and Rousiers 2014, "Moving Frostbite to Physically Based Rendering" pbr_input.specular_occlusion = saturate(pow(NdotV + ssao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ssao); diff --git a/crates/bevy_pbr/src/render/pbr_fragment.wgsl b/crates/bevy_pbr/src/render/pbr_fragment.wgsl index fa345eb7680b6..169d42f0a6f81 100644 --- a/crates/bevy_pbr/src/render/pbr_fragment.wgsl +++ b/crates/bevy_pbr/src/render/pbr_fragment.wgsl @@ -175,12 +175,9 @@ fn pbr_input_from_standard_material( let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2(in.position.xy), 0i).r; let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb); diffuse_occlusion = min(diffuse_occlusion, ssao_multibounce); - - // Use the min (most occluded) between screen space and baked AO: - let ao = min(diffuse_occlusion.r, ssao); // Use SSAO to estimate the specular occlusion. // Lagarde and Rousiers 2014, "Moving Frostbite to Physically Based Rendering" - specular_occlusion = saturate(pow(NdotV + ao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ssao); + specular_occlusion = saturate(pow(NdotV + ssao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ssao); #endif pbr_input.diffuse_occlusion = diffuse_occlusion; pbr_input.specular_occlusion = specular_occlusion;