Use frostbite's specular sampling direction for environment map light (#16711)

Adopt a slightly more accurate lighting model.

Before:

![image](https://github.com/user-attachments/assets/3ea47278-f62d-4ca8-b741-1df6d4aa82c1)

After:

![image](https://github.com/user-attachments/assets/396dca2e-e5a7-4a7f-8be6-74d9cf06a085)

## Changelog
- EnvironmentMapLight lighting is now slightly more realistic for
metallic materials with high roughness
This commit is contained in:
JMS55 2024-12-16 15:41:29 -08:00 committed by GitHub
parent 1e5d2c8867
commit 1666b1c497
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -4,9 +4,7 @@
#import bevy_pbr::mesh_view_bindings as bindings
#import bevy_pbr::mesh_view_bindings::light_probes
#import bevy_pbr::mesh_view_bindings::environment_map_uniform
#import bevy_pbr::lighting::{
F_Schlick_vec, LayerLightingInput, LightingInput, LAYER_BASE, LAYER_CLEARCOAT
}
#import bevy_pbr::lighting::{F_Schlick_vec, LightingInput, LayerLightingInput, LAYER_BASE, LAYER_CLEARCOAT}
#import bevy_pbr::clustered_forward::ClusterableObjectIndexRanges
struct EnvironmentMapLight {
@ -26,16 +24,17 @@ struct EnvironmentMapRadiances {
#ifdef MULTIPLE_LIGHT_PROBES_IN_ARRAY
fn compute_radiances(
input: ptr<function, LightingInput>,
input: LayerLightingInput,
clusterable_object_index_ranges: ptr<function, ClusterableObjectIndexRanges>,
layer: u32,
world_position: vec3<f32>,
found_diffuse_indirect: bool,
) -> EnvironmentMapRadiances {
// Unpack.
let perceptual_roughness = (*input).layers[layer].perceptual_roughness;
let N = (*input).layers[layer].N;
let R = (*input).layers[layer].R;
let N = input.N;
let R = input.R;
let NdotV = input.NdotV;
let perceptual_roughness = input.perceptual_roughness;
let roughness = input.roughness;
var radiances: EnvironmentMapRadiances;
@ -65,7 +64,7 @@ fn compute_radiances(
if (!found_diffuse_indirect) {
var irradiance_sample_dir = N;
// Rotating the world space ray direction by the environment light map transform matrix, it is
// Rotating the world space ray direction by the environment light map transform matrix, it is
// equivalent to rotating the diffuse environment cubemap itself.
irradiance_sample_dir = (environment_map_uniform.transform * vec4(irradiance_sample_dir, 1.0)).xyz;
// Cube maps are left-handed so we negate the z coordinate.
@ -77,8 +76,8 @@ fn compute_radiances(
0.0).rgb * query_result.intensity;
}
var radiance_sample_dir = R;
// Rotating the world space ray direction by the environment light map transform matrix, it is
var radiance_sample_dir = radiance_sample_direction(N, R, roughness);
// Rotating the world space ray direction by the environment light map transform matrix, it is
// equivalent to rotating the specular environment cubemap itself.
radiance_sample_dir = (environment_map_uniform.transform * vec4(radiance_sample_dir, 1.0)).xyz;
// Cube maps are left-handed so we negate the z coordinate.
@ -95,16 +94,17 @@ fn compute_radiances(
#else // MULTIPLE_LIGHT_PROBES_IN_ARRAY
fn compute_radiances(
input: ptr<function, LightingInput>,
input: LayerLightingInput,
clusterable_object_index_ranges: ptr<function, ClusterableObjectIndexRanges>,
layer: u32,
world_position: vec3<f32>,
found_diffuse_indirect: bool,
) -> EnvironmentMapRadiances {
// Unpack.
let perceptual_roughness = (*input).layers[layer].perceptual_roughness;
let N = (*input).layers[layer].N;
let R = (*input).layers[layer].R;
let N = input.N;
let R = input.R;
let NdotV = input.NdotV;
let perceptual_roughness = input.perceptual_roughness;
let roughness = input.roughness;
var radiances: EnvironmentMapRadiances;
@ -123,7 +123,7 @@ fn compute_radiances(
if (!found_diffuse_indirect) {
var irradiance_sample_dir = N;
// Rotating the world space ray direction by the environment light map transform matrix, it is
// Rotating the world space ray direction by the environment light map transform matrix, it is
// equivalent to rotating the diffuse environment cubemap itself.
irradiance_sample_dir = (environment_map_uniform.transform * vec4(irradiance_sample_dir, 1.0)).xyz;
// Cube maps are left-handed so we negate the z coordinate.
@ -135,8 +135,8 @@ fn compute_radiances(
0.0).rgb * intensity;
}
var radiance_sample_dir = R;
// Rotating the world space ray direction by the environment light map transform matrix, it is
var radiance_sample_dir = radiance_sample_direction(N, R, roughness);
// Rotating the world space ray direction by the environment light map transform matrix, it is
// equivalent to rotating the specular environment cubemap itself.
radiance_sample_dir = (environment_map_uniform.transform * vec4(radiance_sample_dir, 1.0)).xyz;
// Cube maps are left-handed so we negate the z coordinate.
@ -174,9 +174,8 @@ fn environment_map_light_clearcoat(
let inv_Fc = 1.0 - Fc;
let clearcoat_radiances = compute_radiances(
input,
(*input).layers[LAYER_CLEARCOAT],
clusterable_object_index_ranges,
LAYER_CLEARCOAT,
world_position,
found_diffuse_indirect,
);
@ -206,9 +205,8 @@ fn environment_map_light(
var out: EnvironmentMapLight;
let radiances = compute_radiances(
input,
(*input).layers[LAYER_BASE],
clusterable_object_index_ranges,
LAYER_BASE,
world_position,
found_diffuse_indirect,
);
@ -256,3 +254,11 @@ fn environment_map_light(
return out;
}
// "Moving Frostbite to Physically Based Rendering 3.0", listing 22
// https://seblagarde.wordpress.com/wp-content/uploads/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf#page=70
fn radiance_sample_direction(N: vec3<f32>, R: vec3<f32>, roughness: f32) -> vec3<f32> {
let smoothness = saturate(1.0 - roughness);
let lerp_factor = smoothness * (sqrt(smoothness) + roughness);
return mix(N, R, lerp_factor);
}