2023-09-03 13:33:38 +02:00
|
|
|
/* SPDX-FileCopyrightText: 2017-2023 Blender Authors
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Used to generate Look Up Tables. Not used in default configuration as the tables are stored in
|
|
|
|
|
* the blender executable. This is only used for reference or to update them.
|
|
|
|
|
*/
|
|
|
|
|
|
2025-09-25 10:57:02 +02:00
|
|
|
#include "infos/eevee_lut_infos.hh"
|
2024-12-02 21:26:15 +01:00
|
|
|
|
|
|
|
|
COMPUTE_SHADER_CREATE_INFO(eevee_lut)
|
|
|
|
|
|
2024-10-04 15:48:22 +02:00
|
|
|
#include "eevee_bxdf_sampling_lib.glsl"
|
|
|
|
|
#include "eevee_sampling_lib.glsl"
|
|
|
|
|
#include "gpu_shader_math_base_lib.glsl"
|
2023-09-03 13:33:38 +02:00
|
|
|
|
|
|
|
|
/* Generate BRDF LUT following "Real shading in unreal engine 4" by Brian Karis
|
|
|
|
|
* https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
|
2025-04-11 18:28:45 +02:00
|
|
|
* Parametrizing with `x = roughness` and `y = sqrt(1.0f - cos(theta))`.
|
2023-09-26 10:16:39 +02:00
|
|
|
* The result is interpreted as: `integral = F0 * scale + F90 * bias - F82_tint * metal_bias`.
|
2025-04-14 13:46:41 +02:00
|
|
|
* with `F82_tint = mix(F0, float3(1.0f), pow5f(6.0f / 7.0f)) * (7.0f / pow6f(6.0f / 7.0f)) * (1.0f
|
|
|
|
|
* - F82)`
|
2023-09-26 10:16:39 +02:00
|
|
|
*/
|
2025-04-14 13:46:41 +02:00
|
|
|
float4 ggx_brdf_split_sum(float3 lut_coord)
|
2023-09-03 13:33:38 +02:00
|
|
|
{
|
|
|
|
|
/* Squaring for perceptually linear roughness, see [Physically Based Shading at Disney]
|
|
|
|
|
* (https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf)
|
|
|
|
|
* Section 5.4. */
|
|
|
|
|
float roughness = square(lut_coord.x);
|
|
|
|
|
|
2025-04-11 18:28:45 +02:00
|
|
|
float NV = clamp(1.0f - square(lut_coord.y), 1e-4f, 0.9999f);
|
2025-04-14 13:46:41 +02:00
|
|
|
float3 V = float3(sqrt(1.0f - square(NV)), 0.0f, NV);
|
|
|
|
|
float3 N = float3(0.0f, 0.0f, 1.0f);
|
2023-09-03 13:33:38 +02:00
|
|
|
|
|
|
|
|
/* Integrating BRDF. */
|
2025-04-11 18:28:45 +02:00
|
|
|
float scale = 0.0f;
|
|
|
|
|
float bias = 0.0f;
|
|
|
|
|
float metal_bias = 0.0f;
|
2025-04-15 11:36:53 +02:00
|
|
|
constexpr uint sample_count = 512u * 512u;
|
2023-09-03 13:33:38 +02:00
|
|
|
for (uint i = 0u; i < sample_count; i++) {
|
2025-04-14 13:46:41 +02:00
|
|
|
float2 rand = hammersley_2d(i, sample_count);
|
|
|
|
|
float3 Xi = sample_cylinder(rand);
|
2023-09-03 13:33:38 +02:00
|
|
|
|
|
|
|
|
/* Microfacet normal. */
|
2025-04-14 13:46:41 +02:00
|
|
|
float3 L = bxdf_ggx_sample_reflection(Xi, V, roughness, false).direction;
|
|
|
|
|
float3 H = normalize(V + L);
|
2023-09-03 13:33:38 +02:00
|
|
|
float NL = L.z;
|
|
|
|
|
|
2025-04-11 18:28:45 +02:00
|
|
|
if (NL > 0.0f) {
|
2023-09-26 10:16:39 +02:00
|
|
|
float VH = saturate(dot(V, H));
|
2024-10-10 00:07:32 +02:00
|
|
|
float weight = bxdf_ggx_eval_reflection(N, L, V, roughness, false).weight;
|
2023-09-03 13:33:38 +02:00
|
|
|
/* Schlick's Fresnel. */
|
2025-04-11 18:28:45 +02:00
|
|
|
float s = saturate(pow5f(1.0f - VH));
|
|
|
|
|
scale += (1.0f - s) * weight;
|
2023-09-03 13:33:38 +02:00
|
|
|
bias += s * weight;
|
2023-09-26 10:16:39 +02:00
|
|
|
/* F82 tint effect. */
|
2025-04-11 18:28:45 +02:00
|
|
|
float b = VH * saturate(pow6f(1.0f - VH));
|
2023-09-26 10:16:39 +02:00
|
|
|
metal_bias += b * weight;
|
2023-09-03 13:33:38 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
scale /= float(sample_count);
|
|
|
|
|
bias /= float(sample_count);
|
2023-09-26 10:16:39 +02:00
|
|
|
metal_bias /= float(sample_count);
|
2023-09-03 13:33:38 +02:00
|
|
|
|
2025-04-14 13:46:41 +02:00
|
|
|
return float4(scale, bias, metal_bias, 0.0f);
|
2023-09-03 13:33:38 +02:00
|
|
|
}
|
|
|
|
|
|
2023-09-18 14:26:19 +02:00
|
|
|
/* Generate BSDF LUT for `IOR < 1` using Schlick's approximation. Returns the transmittance and the
|
|
|
|
|
* scale and bias for reflectance.
|
|
|
|
|
*
|
|
|
|
|
* The result is interpreted as:
|
|
|
|
|
* `reflectance = F0 * scale + F90 * bias`,
|
|
|
|
|
* `transmittance = (1 - F0) * transmission_factor`. */
|
2025-04-14 13:46:41 +02:00
|
|
|
float4 ggx_bsdf_split_sum(float3 lut_coord)
|
2023-09-03 13:33:38 +02:00
|
|
|
{
|
2025-04-11 18:28:45 +02:00
|
|
|
float ior = clamp(sqrt(lut_coord.x), 1e-4f, 0.9999f);
|
2023-09-03 13:33:38 +02:00
|
|
|
/* ior is sin of critical angle. */
|
2025-04-11 18:28:45 +02:00
|
|
|
float critical_cos = sqrt(1.0f - saturate(square(ior)));
|
2023-09-03 13:33:38 +02:00
|
|
|
|
2025-04-11 18:28:45 +02:00
|
|
|
lut_coord.y = lut_coord.y * 2.0f - 1.0f;
|
2023-09-03 13:33:38 +02:00
|
|
|
/* Maximize texture usage on both sides of the critical angle. */
|
2025-04-11 18:28:45 +02:00
|
|
|
lut_coord.y *= (lut_coord.y > 0.0f) ? (1.0f - critical_cos) : critical_cos;
|
2023-09-03 13:33:38 +02:00
|
|
|
/* Center LUT around critical angle to avoid strange interpolation issues when the critical
|
|
|
|
|
* angle is changing. */
|
|
|
|
|
lut_coord.y += critical_cos;
|
2025-04-11 18:28:45 +02:00
|
|
|
float NV = clamp(lut_coord.y, 1e-4f, 0.9999f);
|
2023-09-03 13:33:38 +02:00
|
|
|
|
|
|
|
|
/* Squaring for perceptually linear roughness, see [Physically Based Shading at Disney]
|
|
|
|
|
* (https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf)
|
|
|
|
|
* Section 5.4. */
|
|
|
|
|
float roughness = square(lut_coord.z);
|
|
|
|
|
|
2025-04-14 13:46:41 +02:00
|
|
|
float3 V = float3(sqrt(1.0f - square(NV)), 0.0f, NV);
|
|
|
|
|
float3 N = float3(0.0f, 0.0f, 1.0f);
|
2023-09-03 13:33:38 +02:00
|
|
|
|
|
|
|
|
/* Integrating BSDF */
|
2025-04-11 18:28:45 +02:00
|
|
|
float scale = 0.0f;
|
|
|
|
|
float bias = 0.0f;
|
|
|
|
|
float transmission_factor = 0.0f;
|
2025-04-15 11:36:53 +02:00
|
|
|
constexpr uint sample_count = 512u * 512u;
|
2023-09-03 13:33:38 +02:00
|
|
|
for (uint i = 0u; i < sample_count; i++) {
|
2025-04-14 13:46:41 +02:00
|
|
|
float2 rand = hammersley_2d(i, sample_count);
|
|
|
|
|
float3 Xi = sample_cylinder(rand);
|
2023-09-03 13:33:38 +02:00
|
|
|
|
|
|
|
|
/* Reflection. */
|
2025-04-14 13:46:41 +02:00
|
|
|
float3 R = bxdf_ggx_sample_reflection(Xi, V, roughness, false).direction;
|
2023-09-03 13:33:38 +02:00
|
|
|
float NR = R.z;
|
2025-04-11 18:28:45 +02:00
|
|
|
if (NR > 0.0f) {
|
2025-04-14 13:46:41 +02:00
|
|
|
float3 H = normalize(V + R);
|
|
|
|
|
float3 L = refract(-V, H, 1.0f / ior);
|
2024-10-10 00:07:32 +02:00
|
|
|
float HL = abs(dot(H, L));
|
|
|
|
|
/* Schlick's Fresnel. */
|
2025-04-11 18:28:45 +02:00
|
|
|
float s = saturate(pow5f(1.0f - saturate(HL)));
|
2024-10-10 00:07:32 +02:00
|
|
|
|
|
|
|
|
float weight = bxdf_ggx_eval_reflection(N, R, V, roughness, false).weight;
|
2025-04-11 18:28:45 +02:00
|
|
|
scale += (1.0f - s) * weight;
|
2023-09-18 14:26:19 +02:00
|
|
|
bias += s * weight;
|
2023-09-03 13:33:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Refraction. */
|
2025-04-14 13:46:41 +02:00
|
|
|
float3 T = bxdf_ggx_sample_refraction(Xi, V, roughness, ior, 0.0f, false).direction;
|
2023-09-03 13:33:38 +02:00
|
|
|
float NT = T.z;
|
2025-04-14 13:46:41 +02:00
|
|
|
/* In the case of TIR, `T == float3(0)`. */
|
2025-04-11 18:28:45 +02:00
|
|
|
if (NT < 0.0f) {
|
2025-04-14 13:46:41 +02:00
|
|
|
float3 H = normalize(ior * T + V);
|
2024-10-10 00:07:32 +02:00
|
|
|
float HL = abs(dot(H, T));
|
|
|
|
|
/* Schlick's Fresnel. */
|
2025-04-11 18:28:45 +02:00
|
|
|
float s = saturate(pow5f(1.0f - saturate(HL)));
|
2024-10-10 00:07:32 +02:00
|
|
|
|
2025-04-11 18:28:45 +02:00
|
|
|
float weight = bxdf_ggx_eval_refraction(N, T, V, roughness, ior, 0.0f, false).weight;
|
|
|
|
|
transmission_factor += (1.0f - s) * weight;
|
2023-09-03 13:33:38 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-09-18 14:26:19 +02:00
|
|
|
transmission_factor /= float(sample_count);
|
|
|
|
|
scale /= float(sample_count);
|
|
|
|
|
bias /= float(sample_count);
|
2023-09-03 13:33:38 +02:00
|
|
|
|
2025-04-14 13:46:41 +02:00
|
|
|
return float4(scale, bias, transmission_factor, 0.0f);
|
2023-09-18 14:26:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Generate BTDF LUT for `IOR > 1` using Schlick's approximation. Only the transmittance is needed
|
|
|
|
|
* because the scale and bias does not depend on the IOR and can be obtained from the BRDF LUT.
|
|
|
|
|
*
|
2023-09-19 15:53:51 +10:00
|
|
|
* Parameterize with `x = sqrt((ior - 1) / (ior + 1))` for higher precision in 1 < IOR < 2,
|
2025-04-11 18:28:45 +02:00
|
|
|
* and `y = sqrt(1.0f - cos(theta))`, `z = roughness` similar to BRDF LUT.
|
2023-09-18 14:26:19 +02:00
|
|
|
*
|
|
|
|
|
* The result is interpreted as:
|
|
|
|
|
* `transmittance = (1 - F0) * transmission_factor`. */
|
2025-04-14 13:46:41 +02:00
|
|
|
float4 ggx_btdf_gt_one(float3 lut_coord)
|
2023-09-18 14:26:19 +02:00
|
|
|
{
|
2025-04-11 18:28:45 +02:00
|
|
|
float f0 = clamp(square(lut_coord.x), 1e-4f, 0.9999f);
|
|
|
|
|
float ior = (1.0f + f0) / (1.0f - f0);
|
2023-09-18 14:26:19 +02:00
|
|
|
|
2025-04-11 18:28:45 +02:00
|
|
|
float NV = clamp(1.0f - square(lut_coord.y), 1e-4f, 0.9999f);
|
2025-04-14 13:46:41 +02:00
|
|
|
float3 V = float3(sqrt(1.0f - square(NV)), 0.0f, NV);
|
|
|
|
|
float3 N = float3(0.0f, 0.0f, 1.0f);
|
2023-09-18 14:26:19 +02:00
|
|
|
|
|
|
|
|
/* Squaring for perceptually linear roughness, see [Physically Based Shading at Disney]
|
|
|
|
|
* (https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf)
|
|
|
|
|
* Section 5.4. */
|
|
|
|
|
float roughness = square(lut_coord.z);
|
|
|
|
|
|
|
|
|
|
/* Integrating BTDF. */
|
2025-04-11 18:28:45 +02:00
|
|
|
float transmission_factor = 0.0f;
|
2025-04-15 11:36:53 +02:00
|
|
|
constexpr uint sample_count = 512u * 512u;
|
2023-09-18 14:26:19 +02:00
|
|
|
for (uint i = 0u; i < sample_count; i++) {
|
2025-04-14 13:46:41 +02:00
|
|
|
float2 rand = hammersley_2d(i, sample_count);
|
|
|
|
|
float3 Xi = sample_cylinder(rand);
|
2023-09-18 14:26:19 +02:00
|
|
|
|
|
|
|
|
/* Refraction. */
|
2025-04-14 13:46:41 +02:00
|
|
|
float3 L = bxdf_ggx_sample_refraction(Xi, V, roughness, ior, 0.0f, false).direction;
|
2023-09-18 14:26:19 +02:00
|
|
|
float NL = L.z;
|
|
|
|
|
|
2025-04-11 18:28:45 +02:00
|
|
|
if (NL < 0.0f) {
|
2025-04-14 13:46:41 +02:00
|
|
|
float3 H = normalize(ior * L + V);
|
2024-10-10 00:07:32 +02:00
|
|
|
float HV = abs(dot(H, V));
|
2023-09-18 14:26:19 +02:00
|
|
|
/* Schlick's Fresnel. */
|
2025-04-11 18:28:45 +02:00
|
|
|
float s = saturate(pow5f(1.0f - saturate(HV)));
|
2024-10-10 00:07:32 +02:00
|
|
|
|
2025-04-11 18:28:45 +02:00
|
|
|
float weight = bxdf_ggx_eval_refraction(N, L, V, roughness, ior, 0.0f, false).weight;
|
|
|
|
|
transmission_factor += (1.0f - s) * weight;
|
2023-09-18 14:26:19 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
transmission_factor /= float(sample_count);
|
|
|
|
|
|
2025-04-14 13:46:41 +02:00
|
|
|
return float4(transmission_factor, 0.0f, 0.0f, 0.0f);
|
2023-09-03 13:33:38 +02:00
|
|
|
}
|
|
|
|
|
|
2023-09-20 15:17:27 +02:00
|
|
|
/* Generate SSS translucency profile.
|
|
|
|
|
* We precompute the exit radiance for a slab of homogenous material backface-lit by a directional
|
|
|
|
|
* light. We only integrate for a single color primary since the profile will be applied to each
|
2023-09-22 09:11:57 +10:00
|
|
|
* primary independently.
|
2023-09-20 15:17:27 +02:00
|
|
|
* For each distance `d` we compute the radiance incoming from an hypothetical parallel plane. */
|
2025-04-14 13:46:41 +02:00
|
|
|
float4 burley_sss_translucency(float3 lut_coord)
|
2023-09-20 15:17:27 +02:00
|
|
|
{
|
|
|
|
|
/* Note that we only store the 1st (radius == 1) component.
|
|
|
|
|
* The others are here for debugging overall appearance. */
|
2025-04-14 13:46:41 +02:00
|
|
|
float3 radii = float3(1.0f, 0.2f, 0.1f);
|
2023-09-20 15:17:27 +02:00
|
|
|
float thickness = lut_coord.x * SSS_TRANSMIT_LUT_RADIUS;
|
2025-04-14 13:46:41 +02:00
|
|
|
float3 r = thickness / radii;
|
2023-09-20 15:17:27 +02:00
|
|
|
/* Manual fit based on cycles render of a backlit slab of varying thickness.
|
|
|
|
|
* Mean Error: 0.003
|
|
|
|
|
* Max Error: 0.015 */
|
2025-04-14 13:46:41 +02:00
|
|
|
float3 exponential = exp(-3.6f * pow(r, float3(1.11f)));
|
|
|
|
|
float3 gaussian = exp(-pow(3.4f * r, float3(1.6f)));
|
|
|
|
|
float3 fac = square(saturate(0.5f + r / 0.6f));
|
|
|
|
|
float3 profile = saturate(mix(gaussian, exponential, fac));
|
2023-09-20 15:17:27 +02:00
|
|
|
/* Mask off the end progressively to 0. */
|
2025-04-11 18:28:45 +02:00
|
|
|
profile *= saturate(1.0f - pow5f(lut_coord.x));
|
2023-09-20 15:17:27 +02:00
|
|
|
|
2025-04-14 13:46:41 +02:00
|
|
|
return float4(profile, 0.0f);
|
2023-09-20 15:17:27 +02:00
|
|
|
}
|
|
|
|
|
|
2025-04-14 13:46:41 +02:00
|
|
|
float4 random_walk_sss_translucency(float3 lut_coord)
|
2023-09-20 15:17:27 +02:00
|
|
|
{
|
|
|
|
|
/* Note that we only store the 1st (radius == 1) component.
|
|
|
|
|
* The others are here for debugging overall appearance. */
|
2025-04-14 13:46:41 +02:00
|
|
|
float3 radii = float3(1.0f, 0.2f, 0.1f);
|
2023-09-20 15:17:27 +02:00
|
|
|
float thickness = lut_coord.x * SSS_TRANSMIT_LUT_RADIUS;
|
2025-04-14 13:46:41 +02:00
|
|
|
float3 r = thickness / radii;
|
2023-09-20 15:17:27 +02:00
|
|
|
/* Manual fit based on cycles render of a backlit slab of varying thickness.
|
|
|
|
|
* Mean Error: 0.003
|
|
|
|
|
* Max Error: 0.016 */
|
2025-04-14 13:46:41 +02:00
|
|
|
float3 scale = float3(0.31f, 0.47f, 0.32f);
|
|
|
|
|
float3 exponent = float3(-22.0f, -5.8f, -0.5f);
|
|
|
|
|
float3 profile = float3(dot(scale, exp(exponent * r.r)),
|
|
|
|
|
dot(scale, exp(exponent * r.g)),
|
|
|
|
|
dot(scale, exp(exponent * r.b)));
|
2025-04-11 18:28:45 +02:00
|
|
|
profile = saturate(profile - 0.1f);
|
2023-09-20 15:17:27 +02:00
|
|
|
/* Mask off the end progressively to 0. */
|
2025-04-11 18:28:45 +02:00
|
|
|
profile *= saturate(1.0f - pow5f(lut_coord.x));
|
2023-09-20 15:17:27 +02:00
|
|
|
|
2025-04-14 13:46:41 +02:00
|
|
|
return float4(profile, 0.0f);
|
2023-09-20 15:17:27 +02:00
|
|
|
}
|
|
|
|
|
|
2023-09-03 13:33:38 +02:00
|
|
|
void main()
|
|
|
|
|
{
|
|
|
|
|
/* Make sure coordinates are covering the whole [0..1] range at texel center. */
|
2025-04-14 13:46:41 +02:00
|
|
|
float3 lut_normalized_coordinate = float3(gl_GlobalInvocationID) / float3(table_extent - 1);
|
2023-09-03 13:33:38 +02:00
|
|
|
/* Make sure missing cases are noticeable. */
|
2025-04-14 13:46:41 +02:00
|
|
|
float4 result = float4(-1);
|
2023-09-03 13:33:38 +02:00
|
|
|
switch (uint(table_type)) {
|
|
|
|
|
case LUT_GGX_BRDF_SPLIT_SUM:
|
|
|
|
|
result = ggx_brdf_split_sum(lut_normalized_coordinate);
|
|
|
|
|
break;
|
2023-09-18 14:26:19 +02:00
|
|
|
case LUT_GGX_BTDF_IOR_GT_ONE:
|
|
|
|
|
result = ggx_btdf_gt_one(lut_normalized_coordinate);
|
|
|
|
|
break;
|
2023-09-14 16:04:26 +02:00
|
|
|
case LUT_GGX_BSDF_SPLIT_SUM:
|
|
|
|
|
result = ggx_bsdf_split_sum(lut_normalized_coordinate);
|
2023-09-03 13:33:38 +02:00
|
|
|
break;
|
2023-09-20 15:17:27 +02:00
|
|
|
case LUT_BURLEY_SSS_PROFILE:
|
|
|
|
|
result = burley_sss_translucency(lut_normalized_coordinate);
|
|
|
|
|
break;
|
|
|
|
|
case LUT_RANDOM_WALK_SSS_PROFILE:
|
|
|
|
|
result = random_walk_sss_translucency(lut_normalized_coordinate);
|
|
|
|
|
break;
|
2023-09-03 13:33:38 +02:00
|
|
|
}
|
2025-04-14 13:46:41 +02:00
|
|
|
imageStore(table_img, int3(gl_GlobalInvocationID), result);
|
2023-09-03 13:33:38 +02:00
|
|
|
}
|