diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2021-02-13 20:50:09 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2021-02-13 20:52:19 +0300 |
commit | 83ac8628c490eda4fa5237b7a4256bc670dc0682 (patch) | |
tree | 89563a6d46dcea4ea51b70d23b3a73e7637161a6 /source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl | |
parent | 06492fd61984c1a92fb1f93d30028de97ead451f (diff) |
EEVEE: Update LUT GGX generation shader
This modifies the principled BSDF and the Glass BSDF which now
have better fit to multiscatter GGX.
Code to generate the LUT have been updated and can run at runtime.
The refraction LUT has been changed to have the critical angle always
centered around one pixel so that interpolation can be mitigated.
Offline LUT data will be updated in another commit
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.
I also touched the ior > 1.0 approximation to be smoother.
Also incluse some cleanup of bsdf_sampling.glsl
Diffstat (limited to 'source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl')
-rw-r--r-- | source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl | 101 |
1 files changed, 64 insertions, 37 deletions
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 d815d9d4e6b..2ffe23a9197 100644 --- a/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl @@ -1,62 +1,89 @@ +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) #pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) -uniform float a2; +uniform float sampleCount; +uniform float z; out vec4 FragColor; void main() { - vec3 N, T, B, V; + float x = floor(gl_FragCoord.x) / (LUT_SIZE - 1.0); + float y = floor(gl_FragCoord.y) / (LUT_SIZE - 1.0); - float x = gl_FragCoord.x / LUT_SIZE; - float y = gl_FragCoord.y / LUT_SIZE; - /* There is little variation if ior > 1.0 so we - * maximize LUT precision for ior < 1.0 */ - x = x * 1.1; - float ior = (x > 1.0) ? ior_from_f0((x - 1.0) * 10.0) : sqrt(x); - float NV = (1.0 - (clamp(y, 1e-4, 0.9999))); + float ior = clamp(sqrt(x), 0.05, 0.999); + /* ior is sin of critical angle. */ + float critical_cos = sqrt(1.0 - saturate(ior * ior)); - N = vec3(0.0, 0.0, 1.0); - T = vec3(1.0, 0.0, 0.0); - B = vec3(0.0, 1.0, 0.0); - V = vec3(sqrt(1.0 - NV * NV), 0.0, NV); + 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); - setup_noise(); + float a = z * z; + float a2 = clamp(a * a, 1e-8, 0.9999); + + vec3 V = vec3(sqrt(1.0 - NV * NV), 0.0, NV); /* Integrating BTDF */ float btdf_accum = 0.0; - for (float i = 0.0; i < sampleCount; i++) { - vec3 H = sample_ggx(i, a2, N, T, B); /* Microfacet normal */ + float fresnel_accum = 0.0; + for (float j = 0.0; j < sampleCount; j++) { + for (float i = 0.0; i < sampleCount; i++) { + vec3 Xi = (vec3(i, j, 0.0) + 0.5) / sampleCount; + Xi.yz = vec2(cos(Xi.y * M_2PI), sin(Xi.y * M_2PI)); - float VH = dot(V, H); + /* Microfacet normal. */ + vec3 H = sample_ggx(Xi, a2); - /* Check if there is total internal reflections. */ - float c = abs(VH); - float g = ior * ior - 1.0 + c * c; + float VH = dot(V, H); - float eta = 1.0 / ior; - if (dot(H, V) < 0.0) { - H = -H; - eta = ior; - } + /* Check if there is total internal reflections. */ + float fresnel = F_eta(ior, VH); + + fresnel_accum += fresnel; + + float eta = 1.0 / ior; + if (dot(H, V) < 0.0) { + H = -H; + eta = ior; + } - vec3 L = refract(-V, H, eta); - float NL = -dot(N, L); + vec3 L = refract(-V, H, eta); + float NL = -L.z; - if ((NL > 0.0) && (g > 0.0)) { - float LH = dot(L, H); + if ((NL > 0.0) && (fresnel < 0.999)) { + float LH = dot(L, H); - float G1_l = NL * 2.0 / - G1_Smith_GGX(NL, a2); /* Balancing the adjustments made in G1_Smith */ + /* Balancing the adjustments made in G1_Smith. */ + float G1_l = NL * 2.0 / G1_Smith_GGX(NL, a2); - /* btdf = abs(VH*LH) * (ior*ior) * D * G(V) * G(L) / (Ht2 * NV) - * pdf = (VH * abs(LH)) * (ior*ior) * D * G(V) / (Ht2 * NV) */ - float btdf = G1_l * abs(VH * LH) / (VH * abs(LH)); + /* btdf = abs(VH*LH) * (ior*ior) * D * G(V) * G(L) / (Ht2 * NV) + * pdf = (VH * abs(LH)) * (ior*ior) * D * G(V) / (Ht2 * NV) */ + float btdf = G1_l * abs(VH * LH) / (VH * abs(LH)); - btdf_accum += btdf; + btdf_accum += btdf; + } } } - btdf_accum /= sampleCount; + 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; + } - FragColor = vec4(btdf_accum, 0.0, 0.0, 1.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); } |