EEVEE: change Principled BSDF to match Cycles #111754

Merged
Weizhen Huang merged 8 commits from weizhen/blender:eevee-principled into main 2023-09-01 11:28:19 +02:00
6 changed files with 53 additions and 45 deletions
Showing only changes of commit 80fd0d6874 - Show all commits

View File

@ -95,7 +95,7 @@ float ambient_occlusion_eval(vec3 normal,
vec3 safe_normalize(vec3 N);
float fast_sqrt(float a);
vec3 cameraVec(vec3 P);
vec2 btdf_lut(float a, float b, float c);
vec2 btdf_lut(float a, float b, float c, float d);
vec2 brdf_lut(float a, float b);
vec3 F_brdf_multi_scatter(vec3 a, vec3 b, vec2 c);
vec3 F_brdf_single_scatter(vec3 a, vec3 b, vec2 c);

View File

@ -74,7 +74,7 @@ vec3 lut_coords_btdf(float cos_theta, float roughness, float ior)
}
/* Returns GGX BTDF in first component and fresnel in second. */
vec2 btdf_lut(float cos_theta, float roughness, float ior)
vec2 btdf_lut(float cos_theta, float roughness, float ior, float do_multiscatter)
{
if (ior <= 1e-5) {
return vec2(0.0);
@ -83,16 +83,16 @@ vec2 btdf_lut(float cos_theta, float roughness, float ior)
if (ior >= 1.0) {
vec2 split_sum = brdf_lut(cos_theta, roughness);
float f0 = f0_from_ior(ior);
/* Baked IOR for GGX BRDF. */
const float specular = 1.0;
const float eta_brdf = (2.0 / (1.0 - sqrt(0.08 * specular))) - 1.0;
/* Avoid harsh transition coming from ior == 1. */
float f90 = fast_sqrt(saturate(f0 / (f0_from_ior(eta_brdf) * 0.25)));
float fresnel = F_brdf_single_scatter(vec3(f0), vec3(f90), split_sum).r;
/* Setting the BTDF to one is not really important since it is only used for multi-scatter
* and it's already quite close to ground truth. */
float btdf = 1.0;
return vec2(btdf, fresnel);
/* Gradually increase `f90` from 0 to 1 when IOR is in the range of [1.0, 1.33], to avoid harsh
* transition at `IOR == 1`. */
float f90 = fast_sqrt(saturate(f0 / 0.02));
float brdf = F_brdf_multi_scatter(vec3(f0), vec3(f90), split_sum).r;
/* Energy conservation. */
float btdf = 1.0 - brdf;
/* Assuming the energy loss caused by single-scattering is distributed proportionally in the
* reflection and refraction lobes. */
return vec2(btdf, brdf) * ((do_multiscatter == 0.0) ? sum(split_sum) : 1.0);
}
vec3 coords = lut_coords_btdf(cos_theta, roughness, ior);
@ -101,15 +101,21 @@ vec2 btdf_lut(float cos_theta, float roughness, float ior)
float layer_floored = floor(layer);
coords.z = lut_btdf_layer_first + layer_floored;
vec2 btdf_low = textureLod(utilTex, coords, 0.0).rg;
vec2 btdf_brdf_low = textureLod(utilTex, coords, 0.0).rg;
coords.z += 1.0;
vec2 btdf_high = textureLod(utilTex, coords, 0.0).rg;
vec2 btdf_brdf_high = textureLod(utilTex, coords, 0.0).rg;
/* Manual trilinear interpolation. */
vec2 btdf = mix(btdf_low, btdf_high, layer - layer_floored);
vec2 btdf_brdf = mix(btdf_brdf_low, btdf_brdf_high, layer - layer_floored);
return btdf;
if (do_multiscatter != 0.0) {
/* For energy-conserving BSDF the reflection and refraction lobes should sum to one. Assuming
* the energy loss of single-scattering is distributed proportionally in the two lobes. */
btdf_brdf /= (btdf_brdf.x + btdf_brdf.y);
}
return btdf_brdf;
}
/** \} */

View File

@ -38,7 +38,7 @@ void main()
}
/* Stubs */
vec2 btdf_lut(float a, float b, float c)
vec2 btdf_lut(float a, float b, float c, float d)
{
return vec2(0.0);
}

View File

@ -316,7 +316,7 @@ vec2 brdf_lut(float cos_theta, float roughness)
#endif
}
vec2 btdf_lut(float cos_theta, float roughness, float ior)
vec2 btdf_lut(float cos_theta, float roughness, float ior, float do_multiscatter)
{
if (ior <= 1e-5) {
return vec2(0.0);
@ -325,16 +325,16 @@ vec2 btdf_lut(float cos_theta, float roughness, float ior)
if (ior >= 1.0) {
vec2 split_sum = brdf_lut(cos_theta, roughness);
float f0 = F0_from_ior(ior);
/* Baked IOR for GGX BRDF. */
const float specular = 1.0;
const float eta_brdf = (2.0 / (1.0 - sqrt(0.08 * specular))) - 1.0;
/* Avoid harsh transition coming from ior == 1. */
float f90 = fast_sqrt(saturate(f0 / (F0_from_ior(eta_brdf) * 0.25)));
float fresnel = F_brdf_single_scatter(vec3(f0), vec3(f90), split_sum).r;
/* Setting the BTDF to one is not really important since it is only used for multi-scatter
* and it's already quite close to ground truth. */
float btdf = 1.0;
return vec2(btdf, fresnel);
/* Gradually increase `f90` from 0 to 1 when IOR is in the range of [1.0, 1.33], to avoid harsh
* transition at `IOR == 1`. */
float f90 = fast_sqrt(saturate(f0 / 0.02));
float brdf = F_brdf_multi_scatter(vec3(f0), vec3(f90), split_sum).r;
/* Energy conservation. */
float btdf = 1.0 - brdf;
/* Assuming the energy loss caused by single-scattering is distributed proportionally in the
* reflection and refraction lobes. */
return vec2(btdf, brdf) * ((do_multiscatter == 0.0) ? sum(split_sum) : 1.0);
}
/* IOR is sin of critical angle. */
@ -355,11 +355,18 @@ vec2 btdf_lut(float cos_theta, float roughness, float ior)
#ifdef EEVEE_UTILITY_TX
coords.z = UTIL_BTDF_LAYER + layer_floored;
vec2 btdf_low = utility_tx_sample_lut(utility_tx, coords.xy, coords.z).rg;
vec2 btdf_high = utility_tx_sample_lut(utility_tx, coords.xy, coords.z + 1.0).rg;
vec2 btdf_brdf_low = utility_tx_sample_lut(utility_tx, coords.xy, coords.z).rg;
vec2 btdf_brdf_high = utility_tx_sample_lut(utility_tx, coords.xy, coords.z + 1.0).rg;
/* Manual trilinear interpolation. */
vec2 btdf = mix(btdf_low, btdf_high, layer - layer_floored);
return btdf;
vec2 btdf_brdf = mix(btdf_brdf_low, btdf_brdf_high, layer - layer_floored);
if (do_multiscatter != 0.0) {
/* For energy-conserving BSDF the reflection and refraction lobes should sum to one. Assuming
* the energy loss of single-scattering is distributed proportionally in the two lobes. */
btdf_brdf /= (btdf_brdf.x + btdf_brdf.y);
}
return btdf_brdf;
#else
return vec2(0.0);
#endif

View File

@ -14,20 +14,17 @@ void node_bsdf_glass(vec4 color,
vec3 V = cameraVec(g_data.P);
float NV = dot(N, V);
vec2 split_sum = btdf_lut(NV, roughness, ior);
float fresnel = (do_multiscatter != 0.0) ? split_sum.y : F_eta(ior, NV);
float btdf = (do_multiscatter != 0.0) ? 1.0 : split_sum.x;
vec2 bsdf = btdf_lut(NV, roughness, ior, do_multiscatter);
ClosureReflection reflection_data;
reflection_data.weight = fresnel * weight;
reflection_data.weight = bsdf.y * weight;
reflection_data.color = color.rgb;
reflection_data.N = N;
reflection_data.roughness = roughness;
ClosureRefraction refraction_data;
refraction_data.weight = (1.0 - fresnel) * weight;
refraction_data.color = color.rgb * btdf;
refraction_data.weight = bsdf.x * weight;
refraction_data.color = color.rgb;
refraction_data.N = N;
refraction_data.roughness = roughness;
refraction_data.ior = ior;

View File

@ -64,9 +64,9 @@ void node_bsdf_principled(vec4 base_color,
vec3 V = cameraVec(g_data.P);
float NV = dot(N, V);
float fresnel = (do_multiscatter != 0.0) ? btdf_lut(NV, roughness, ior).y : F_eta(ior, NV);
float glass_reflection_weight = fresnel * transmission;
float glass_transmission_weight = (1.0 - fresnel) * transmission;
vec2 glass_bsdf = btdf_lut(NV, roughness, ior, do_multiscatter);
float glass_reflection_weight = glass_bsdf.y * transmission;
float glass_transmission_weight = glass_bsdf.x * transmission;
vec3 base_color_tint = tint_from_color(base_color.rgb);
@ -144,9 +144,7 @@ void node_bsdf_principled(vec4 base_color,
/* Refraction. */
ClosureRefraction refraction_data;
refraction_data.weight = glass_transmission_weight * weight;
float btdf = (do_multiscatter != 0.0) ? 1.0 : btdf_lut(NV, roughness, ior).x;
refraction_data.color = base_color.rgb * btdf;
refraction_data.color = base_color.rgb;
refraction_data.N = N;
refraction_data.roughness = roughness;
refraction_data.ior = ior;