Skip to content

Commit

Permalink
Use “specular occlusion” term to consistently extinguish fresnel on A…
Browse files Browse the repository at this point in the history
…mbient and Environment Map lights (bevyengine#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](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
<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).
  • Loading branch information
coreh authored Oct 23, 2023
1 parent 1e92589 commit e59085a
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 2 deletions.
7 changes: 6 additions & 1 deletion crates/bevy_pbr/src/environment_map/environment_map.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
7 changes: 6 additions & 1 deletion crates/bevy_pbr/src/render/pbr_ambient.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

0 comments on commit e59085a

Please sign in to comment.