diff options
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; } |