diff --git a/crates/bevy_pbr/src/environment_map/environment_map.wgsl b/crates/bevy_pbr/src/environment_map/environment_map.wgsl index 2288d7d07c8bb..f188a578e478f 100644 --- a/crates/bevy_pbr/src/environment_map/environment_map.wgsl +++ b/crates/bevy_pbr/src/environment_map/environment_map.wgsl @@ -25,12 +25,17 @@ fn environment_map_light( let irradiance = textureSample(bindings::environment_map_diffuse, bindings::environment_map_sampler, vec3(N.xy, -N.z)).rgb; let radiance = textureSampleLevel(bindings::environment_map_specular, bindings::environment_map_sampler, vec3(R.xy, -R.z), radiance_level).rgb; + // No real world material has specular values under 0.02, so we use this range as a + // "pre-baked specular occlusion" that extinguishes the fresnel term, for artistic control. + // See: https://google.github.io/filament/Filament.html#specularocclusion + let specular_occlusion = saturate(dot(F0, vec3(50.0 * 0.33))); + // Multiscattering approximation: https://www.jcgt.org/published/0008/01/03/paper.pdf // Useful reference: https://bruop.github.io/ibl let Fr = max(vec3(1.0 - roughness), F0) - F0; let kS = F0 + Fr * pow(1.0 - NdotV, 5.0); - let FssEss = kS * f_ab.x + f_ab.y; let Ess = f_ab.x + f_ab.y; + let FssEss = kS * Ess * specular_occlusion; let Ems = 1.0 - Ess; let Favg = F0 + (1.0 - F0) / 21.0; let Fms = FssEss * Favg / (1.0 - Ems * Favg); diff --git a/crates/bevy_pbr/src/render/pbr_ambient.wgsl b/crates/bevy_pbr/src/render/pbr_ambient.wgsl index 23d5cf29b235a..7b174da35c9db 100644 --- a/crates/bevy_pbr/src/render/pbr_ambient.wgsl +++ b/crates/bevy_pbr/src/render/pbr_ambient.wgsl @@ -20,5 +20,10 @@ fn ambient_light( let diffuse_ambient = EnvBRDFApprox(diffuse_color, F_AB(1.0, NdotV)); let specular_ambient = EnvBRDFApprox(specular_color, F_AB(perceptual_roughness, NdotV)); - return (diffuse_ambient + specular_ambient) * lights.ambient_color.rgb * occlusion; + // No real world material has specular values under 0.02, so we use this range as a + // "pre-baked specular occlusion" that extinguishes the fresnel term, for artistic control. + // See: https://google.github.io/filament/Filament.html#specularocclusion + let specular_occlusion = saturate(dot(specular_color, vec3(50.0 * 0.33))); + + return (diffuse_ambient + specular_ambient * specular_occlusion) * lights.ambient_color.rgb * occlusion; }