From d8e4fe320784dedee4fec01e0ee461a75c320b83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Fri, 14 Jul 2023 19:03:00 +0200 Subject: [PATCH] EEVEE-Next: Add back fresnel functions --- .../engines/eevee_next/eevee_shader_shared.hh | 8 ++ .../shaders/eevee_nodetree_lib.glsl | 107 ++++++++++++++++-- .../shaders/infos/eevee_material_info.hh | 1 + 3 files changed, 105 insertions(+), 11 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index d60cf912931..ac28cf72960 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -1095,6 +1095,14 @@ float4 utility_tx_sample(sampler2DArray util_tx, float2 uv, float layer) { return textureLod(util_tx, float3(uv, layer), 0.0); } + +/* Sample at uv position but with scale and bias so that uv space bounds lie on texel centers. */ +float4 utility_tx_sample_lut(sampler2DArray util_tx, float2 uv, float layer) +{ + /* Scale and bias coordinates, for correct filtered lookup. */ + uv = uv * ((UTIL_TEX_SIZE - 1.0) / UTIL_TEX_SIZE) + (0.5 / UTIL_TEX_SIZE); + return textureLod(util_tx, float3(uv, layer), 0.0); +} #endif /** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl index c74af4fcefd..ef6b263c626 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl @@ -235,26 +235,111 @@ float nodetree_thickness(); vec4 closure_to_rgba(Closure cl); #endif -/* Stubs. */ -vec2 btdf_lut(float a, float b, float c) +/* Fresnel monochromatic, perfect mirror */ +float F_eta(float eta, float cos_theta) { - return vec2(1, 0); + /* Compute fresnel reflectance without explicitly computing + * the refracted direction. */ + float c = abs(cos_theta); + float g = eta * eta - 1.0 + c * c; + if (g > 0.0) { + g = sqrt(g); + float A = (g - c) / (g + c); + float B = (c * (g + c) - 1.0) / (c * (g - c) + 1.0); + return 0.5 * A * A * (1.0 + B * B); + } + /* Total internal reflections. */ + return 1.0; } -vec2 brdf_lut(float a, float b) + +/* Simplified form of F_eta(eta, 1.0). */ +float F0_from_ior(float eta) { - return vec2(1, 0); + float A = (eta - 1.0) / (eta + 1.0); + return A * A; } -vec3 F_brdf_multi_scatter(vec3 a, vec3 b, vec2 c) + +/* Return the fresnel color from a precomputed LUT value (from brdf_lutb). */ +vec3 F_brdf_single_scatter(vec3 f0, vec3 f90, vec2 lut) { - return a; + return lut.y * f90 + lut.x * f0; } -vec3 F_brdf_single_scatter(vec3 a, vec3 b, vec2 c) + +/* Return the fresnel color from a precomputed LUT value (from brdf_lutb). */ +vec3 F_brdf_multi_scatter(vec3 f0, vec3 f90, vec2 lut) { - return a; + /** + * From "A Multiple-Scattering Microfacet Model for Real-Time Image-based Lighting" + * by Carmelo J. Fdez-Aguera + * https://jcgt.org/published/0008/01/03/paper.pdf + */ + vec3 FssEss = lut.y * f90 + lut.x * f0; + + float Ess = lut.x + lut.y; + float Ems = 1.0 - Ess; + vec3 Favg = f0 + (1.0 - f0) / 21.0; + vec3 Fms = FssEss * Favg / (1.0 - (1.0 - Ess) * Favg); + /* We don't do anything special for diffuse surfaces because the principle bsdf + * does not care about energy conservation of the specular layer for dielectrics. */ + return FssEss + Fms * Ems; } -float F_eta(float a, float b) + +vec2 brdf_lut(float cos_theta, float roughness) { - return a; +#ifdef EEVEE_UTILITY_TX + return utility_tx_sample_lut(utility_tx, vec2(cos_theta, roughness), UTIL_BSDF_LAYER).rg; +#else + return vec2(1.0, 0.0); +#endif +} + +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); + /* 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 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 = 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); + + float layer = coords.z * UTIL_BTDF_LAYER_COUNT; + float layer_floored = floor(layer); + +#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; + /* Manual trilinear interpolation. */ + vec2 btdf = mix(btdf_low, btdf_high, layer - layer_floored); + return btdf; +#else + return vec2(0.0); +#endif } void output_renderpass_color(int id, vec4 color) diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index a71706c6ff0..25a9cda06a6 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -20,6 +20,7 @@ GPU_SHADER_CREATE_INFO(eevee_sampling_data) .storage_buf(SAMPLING_BUF_SLOT, Qualifier::READ, "SamplingData", "sampling_buf"); GPU_SHADER_CREATE_INFO(eevee_utility_texture) + .define("EEVEE_UTILITY_TX") .sampler(RBUFS_UTILITY_TEX_SLOT, ImageType::FLOAT_2D_ARRAY, "utility_tx"); GPU_SHADER_CREATE_INFO(eevee_camera).uniform_buf(CAMERA_BUF_SLOT, "CameraData", "camera_buf");