diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2021-02-10 19:02:06 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2021-02-10 19:02:06 +0300 |
commit | a9ac8d8871312cfa94d4721af82501ca520e44eb (patch) | |
tree | aa5eb09cdc4550ec5e39e925a6d309fa1f02d08d | |
parent | 9c2f45981691ee2a2c2774e7f51fa32582a1eaec (diff) |
EEVEE: Better fit of BTDF LUTeevee-ggx-lut-fix
This simplify the BTDF retreival removing the manual clean cut at
low roughness.
This maximize the precision of the LUT by scalling the sides by the
critical angle.
Also touched the ior > 1.0 approximation to be smoother.
3 files changed, 55 insertions, 33 deletions
diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl index 1ac965d24ca..cb4d8931af0 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -24,6 +24,7 @@ float ior_from_f0(float f0) return (-f - 1.0) / (f - 1.0); } +/* Simplified form of F_eta(eta, 1.0). */ float f0_from_ior(float eta) { float A = (eta - 1.0) / (eta + 1.0); @@ -69,7 +70,7 @@ float F_eta(float eta, float cos_theta) /* Fresnel color blend base on fresnel factor */ vec3 F_color_blend(float eta, float fresnel, vec3 f0_color) { - float f0 = F_eta(eta, 1.0); + float f0 = f0_from_ior(eta); float fac = saturate((fresnel - f0) / (1.0 - f0)); return mix(f0_color, vec3(1.0), fac); } diff --git a/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl b/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl index b504b78ee06..2ffe23a9197 100644 --- a/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl @@ -11,18 +11,17 @@ void main() float x = floor(gl_FragCoord.x) / (LUT_SIZE - 1.0); float y = floor(gl_FragCoord.y) / (LUT_SIZE - 1.0); - float ior = sqrt(x); - ior = clamp(sqrt(ior), 0.0, 0.99995); + float ior = clamp(sqrt(x), 0.05, 0.999); /* ior is sin of critical angle. */ float critical_cos = sqrt(1.0 - saturate(ior * ior)); - /* Manual fit to range. */ - y = y * 1.45 + 0.05; - /* Remap for better accuracy. */ - float NV = 1.0 - y * y; - /* Center LUT around critical angle to always have a sharp cut if roughness is 0. */ - NV += critical_cos; - NV = clamp(NV, 0.0, 0.9999); + y = y * 2.0 - 1.0; + /* Maximize texture usage on both sides of the critical angle. */ + y *= (y > 0.0) ? (1.0 - critical_cos) : critical_cos; + /* Center LUT around critical angle to avoid strange interpolation issues when the critical + * angle is changing. */ + y += critical_cos; + float NV = clamp(y, 1e-4, 0.9999); float a = z * z; float a2 = clamp(a * a, 1e-8, 0.9999); @@ -56,7 +55,7 @@ void main() vec3 L = refract(-V, H, eta); float NL = -L.z; - if ((NL > 0.0) && (fresnel < 1.0)) { + if ((NL > 0.0) && (fresnel < 0.999)) { float LH = dot(L, H); /* Balancing the adjustments made in G1_Smith. */ @@ -73,6 +72,17 @@ void main() btdf_accum /= sampleCount * sampleCount; fresnel_accum /= sampleCount * sampleCount; + if (z == 0.0) { + /* Perfect mirror. Increased precision because the roughness is clamped. */ + fresnel_accum = F_eta(ior, NV); + } + + if (x == 0.0) { + /* Special case. */ + fresnel_accum = 1.0; + btdf_accum = 0.0; + } + /* There is place to put multiscater result (which is a little bit different still) * and / or lobe fitting for better sampling of */ FragColor = vec4(btdf_accum, fresnel_accum, 0.0, 1.0); diff --git a/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl b/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl index 65338cf0dac..b0cd6ac8459 100644 --- a/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl @@ -40,35 +40,51 @@ vec2 brdf_lut(float cos_theta, float roughness) return textureLod(utilTex, vec3(lut_coords(cos_theta, roughness), BRDF_LUT_LAYER), 0.0).rg; } +/* Return texture coordinates to sample Surface LUT. */ +vec3 lut_coords_btdf(float cos_theta, float roughness, float ior) +{ + /* ior is sin of critical angle. */ + float critical_cos = sqrt(1.0 - ior * ior); + + vec3 coords; + coords.x = sqr(ior); + coords.y = cos_theta; + coords.y -= critical_cos; + coords.y /= (coords.y > 0.0) ? (1.0 - critical_cos) : critical_cos; + coords.y = coords.y * 0.5 + 0.5; + coords.z = roughness; + + coords = saturate(coords); + + /* scale and bias coordinates, for correct filtered lookup */ + coords.xy = coords.xy * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE; + + return coords; +} + /* Returns GGX BTDF in first component and fresnel in second. */ vec2 btdf_lut(float cos_theta, float roughness, float ior) { + if (ior <= 1e-5) { + return vec2(0.0); + } + if (ior >= 1.0) { vec2 split_sum = brdf_lut(cos_theta, roughness); float f0 = f0_from_ior(ior); - float fresnel = F_brdf_single_scatter(vec3(f0), vec3(1.0), split_sum).r; + /* 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 comming 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 multiscatter * and it's already quite close to ground truth. */ float btdf = 1.0; return vec2(btdf, fresnel); } - /* ior is sin of critical angle. */ - float critical_cos = sqrt(1.0 - ior * ior); - - vec3 coords; - coords.x = sqr(ior); - coords.y = sqrt(1.0 + critical_cos - cos_theta); - coords.y = (coords.y - 0.05) / 1.45; - coords.z = roughness; - - coords = saturate(coords); - - coords.xy = coords.xy * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE; - /* Bias the lookup in the NV direction to be able to do the clear cut - * at the end of the function. */ - float clear_cut = saturate(roughness * lut_btdf_layer_count * 0.5); - coords.y -= clear_cut * 0.5 / LUT_SIZE; + vec3 coords = lut_coords_btdf(cos_theta, roughness, ior); float layer = coords.z * lut_btdf_layer_count; float layer_floored = floor(layer); @@ -82,11 +98,6 @@ vec2 btdf_lut(float cos_theta, float roughness, float ior) /* Manual trilinear interpolation. */ vec2 btdf = mix(btdf_low, btdf_high, layer - layer_floored); - /* Do a manual trim if roughness is low enough to avoid seeing the bilinear interpolation. */ - if (clear_cut < 1.0 && cos_theta < critical_cos) { - btdf = mix(vec2(0.0, 1.0), btdf, clear_cut); - } - return btdf; } |