Use “specular occlusion” term to consistently extinguish fresnel on Ambient and Environment Map lights (#10182)
# 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](ffc572728f/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
<img width="1392" alt="Screenshot 2023-10-18 at 23 02 25"
src="https://github.com/bevyengine/bevy/assets/418473/ec0efb58-9a98-4377-87bc-726a1b0a3ff3">
### After
<img width="1392" alt="Screenshot 2023-10-18 at 23 01 43"
src="https://github.com/bevyengine/bevy/assets/418473/a2809325-5728-405e-af02-9b5779767843">
---
## 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).
This commit is contained in:
parent
1e9258910c
commit
e59085a67f
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user