From 3079eaa020930c59fcaed865109e861502e4d3c2 Mon Sep 17 00:00:00 2001 From: Marco Buono Date: Mon, 23 Oct 2023 00:26:20 -0300 Subject: [PATCH] =?UTF-8?q?Use=20=E2=80=9Cspecular=20occlusion=E2=80=9D=20?= =?UTF-8?q?term=20to=20consistently=20extinguish=20fresnel=20on=20Ambient?= =?UTF-8?q?=20and=20Environment=20Map=20lights=20(#10182)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Objective Even at `reflectance == 0.0`, our ambient and environment map light implementations still produce fresnel/specular highlights. Such a low `reflectance` value lies outside of the physically possible range and is already used by our directional, point and spot light implementations (via the `fresnel()` function) to enable artistic control, effectively disabling the fresnel "look" for non-physically realistic materials. Since ambient and environment lights use a different formulation, they were not honoring this same principle. This PR aims to bring consistency to all light types, offering the same fresnel extinguishing control to ambient and environment lights. Thanks to `@nathanf` for [pointing out](https://discord.com/channels/691052431525675048/743663924229963868/1164083373514440744) the [Filament docs section about this](https://google.github.io/filament/Filament.md.html#lighting/occlusion/specularocclusion). ## Solution - We use [the same formulation](https://github.com/bevyengine/bevy/blob/ffc572728fb7874996a13c31a82e86ef98515995/crates/bevy_pbr/src/render/pbr_lighting.wgsl#L99) already used by the `fresnel()` function in `bevy_pbr::lighting` to modulate the F90, to modulate the specular component of Ambient and Environment Map lights. ## Comparison ⚠️ **Modified version of the PBR example for demo purposes, that shows reflectance (_NOT_ part of this PR)** ⚠️ Also, keep in mind this is a very subtle difference (look for the fresnel highlights on the lower left spheres, you might need to zoom in. ### Before Screenshot 2023-10-18 at 23 02 25 ### After Screenshot 2023-10-18 at 23 01 43 --- ## Changelog - Ambient and Environment Map lights will now honor values of `reflectance` that are below the physically possible range (⪅ 0.35) by extinguishing their fresnel highlights. (Just like point, directional and spot lights already did.) This allows for more consistent artistic control and for non-physically realistic looks with all light types. ## Migration Guide - If Fresnel highlights from Ambient and Environment Map lights are no longer visible in your materials, make sure you're using a higher, physically plausible value of `reflectance` (⪆ 0.35). --- crates/bevy_pbr/src/environment_map/environment_map.wgsl | 7 ++++++- crates/bevy_pbr/src/render/pbr_ambient.wgsl | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) 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; }