Skip to content

Commit 9c478d9

Browse files
Use directional albedo for prefiltered environment lighting (#2808)
While working on other changes, I noticed that the GLSL prefiltered environment lighting code path was only performing a single evaluation of the `F` and `G` terms, leading to innaccurate results for rougher materials. To get an approximation of the correct behaviour, we need to account for the particular Fresnel function in use. To that end, I've introduced a new variation of `mx_ggx_dir_albedo()`.
1 parent 62a59c3 commit 9c478d9

2 files changed

Lines changed: 32 additions & 4 deletions

File tree

libraries/pbrlib/genglsl/lib/mx_environment_prefilter.glsl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@ vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distributio
1515
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
1616

1717
float avgAlpha = mx_average_alpha(alpha);
18-
vec3 F = mx_compute_fresnel(NdotV, fd);
19-
float G = mx_ggx_smith_G2(NdotV, NdotV, avgAlpha);
20-
vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : F * G;
18+
vec3 FG = mx_ggx_dir_albedo(NdotV, avgAlpha, fd);
19+
FG = fd.refraction ? vec3(1.0) - FG : FG;
2120

2221
vec3 Li = mx_latlong_map_lookup(L, $envMatrix, mx_latlong_alpha_to_lod(avgAlpha), $envRadiance);
2322
return Li * FG * $envLightIntensity;

libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,12 +476,41 @@ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
476476
{
477477
return mx_fresnel_conductor(cosTheta, fd.ior, fd.extinction);
478478
}
479-
else
479+
else // FRESNEL_MODEL_SCHLICK
480480
{
481481
return mx_fresnel_hoffman_schlick(cosTheta, fd);
482482
}
483483
}
484484

485+
// Directional albedo accounting for different Fresnel functions.
486+
vec3 mx_ggx_dir_albedo(float NdotV, float alpha, FresnelData fd)
487+
{
488+
if (fd.airy)
489+
{
490+
// Approximation using a blend between mirror (alpha = 0)
491+
// and rougher cases. This helps to maintain angular
492+
// color variation at lower roughness values.
493+
vec3 mirrorDirAlbedo = mx_compute_fresnel(NdotV, fd);
494+
vec3 F0 = mx_fresnel_airy(1.0, fd);
495+
vec3 roughDirAlbedo = mx_ggx_dir_albedo(NdotV, alpha, F0, vec3(1.0));
496+
return mix(mirrorDirAlbedo, roughDirAlbedo, sqrt(alpha));
497+
}
498+
else if (fd.model == FRESNEL_MODEL_DIELECTRIC)
499+
{
500+
float F0 = mx_ior_to_f0(fd.ior.x);
501+
return mx_ggx_dir_albedo(NdotV, alpha, vec3(F0), vec3(1.0));
502+
}
503+
else if (fd.model == FRESNEL_MODEL_CONDUCTOR)
504+
{
505+
vec3 F0 = mx_fresnel_conductor(1.0, fd.ior, fd.extinction);
506+
return mx_ggx_dir_albedo(NdotV, alpha, F0, vec3(1.0));
507+
}
508+
else // FRESNEL_MODEL_SCHLICK
509+
{
510+
return mx_ggx_dir_albedo(NdotV, alpha, fd.F0, fd.F90);
511+
}
512+
}
513+
485514
// Compute the refraction of a ray through a solid sphere.
486515
vec3 mx_refraction_solid_sphere(vec3 R, vec3 N, float ior)
487516
{

0 commit comments

Comments
 (0)