Files
test2/source/blender/draw/engines/eevee/shaders/eevee_lut_comp.glsl

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

262 lines
9.7 KiB
Plaintext
Raw Normal View History

/* 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.
*/
#include "infos/eevee_lut_infos.hh"
COMPUTE_SHADER_CREATE_INFO(eevee_lut)
#include "eevee_bxdf_sampling_lib.glsl"
#include "eevee_sampling_lib.glsl"
#include "gpu_shader_math_base_lib.glsl"
/* Generate BRDF LUT following "Real shading in unreal engine 4" by Brian Karis
* https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
* Parametrizing with `x = roughness` and `y = sqrt(1.0f - cos(theta))`.
* The result is interpreted as: `integral = F0 * scale + F90 * bias - F82_tint * metal_bias`.
* with `F82_tint = mix(F0, float3(1.0f), pow5f(6.0f / 7.0f)) * (7.0f / pow6f(6.0f / 7.0f)) * (1.0f
* - F82)`
*/
float4 ggx_brdf_split_sum(float3 lut_coord)
{
/* 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);
float NV = clamp(1.0f - square(lut_coord.y), 1e-4f, 0.9999f);
float3 V = float3(sqrt(1.0f - square(NV)), 0.0f, NV);
float3 N = float3(0.0f, 0.0f, 1.0f);
/* Integrating BRDF. */
float scale = 0.0f;
float bias = 0.0f;
float metal_bias = 0.0f;
constexpr uint sample_count = 512u * 512u;
for (uint i = 0u; i < sample_count; i++) {
float2 rand = hammersley_2d(i, sample_count);
float3 Xi = sample_cylinder(rand);
/* Microfacet normal. */
float3 L = bxdf_ggx_sample_reflection(Xi, V, roughness, false).direction;
float3 H = normalize(V + L);
float NL = L.z;
if (NL > 0.0f) {
float VH = saturate(dot(V, H));
float weight = bxdf_ggx_eval_reflection(N, L, V, roughness, false).weight;
/* Schlick's Fresnel. */
float s = saturate(pow5f(1.0f - VH));
scale += (1.0f - s) * weight;
bias += s * weight;
/* F82 tint effect. */
float b = VH * saturate(pow6f(1.0f - VH));
metal_bias += b * weight;
}
}
scale /= float(sample_count);
bias /= float(sample_count);
metal_bias /= float(sample_count);
return float4(scale, bias, metal_bias, 0.0f);
}
/* 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`. */
float4 ggx_bsdf_split_sum(float3 lut_coord)
{
float ior = clamp(sqrt(lut_coord.x), 1e-4f, 0.9999f);
/* ior is sin of critical angle. */
float critical_cos = sqrt(1.0f - saturate(square(ior)));
lut_coord.y = lut_coord.y * 2.0f - 1.0f;
/* Maximize texture usage on both sides of the critical angle. */
lut_coord.y *= (lut_coord.y > 0.0f) ? (1.0f - critical_cos) : critical_cos;
/* Center LUT around critical angle to avoid strange interpolation issues when the critical
* angle is changing. */
lut_coord.y += critical_cos;
float NV = clamp(lut_coord.y, 1e-4f, 0.9999f);
/* 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);
float3 V = float3(sqrt(1.0f - square(NV)), 0.0f, NV);
float3 N = float3(0.0f, 0.0f, 1.0f);
/* Integrating BSDF */
float scale = 0.0f;
float bias = 0.0f;
float transmission_factor = 0.0f;
constexpr uint sample_count = 512u * 512u;
for (uint i = 0u; i < sample_count; i++) {
float2 rand = hammersley_2d(i, sample_count);
float3 Xi = sample_cylinder(rand);
/* Reflection. */
float3 R = bxdf_ggx_sample_reflection(Xi, V, roughness, false).direction;
float NR = R.z;
if (NR > 0.0f) {
float3 H = normalize(V + R);
float3 L = refract(-V, H, 1.0f / ior);
float HL = abs(dot(H, L));
/* Schlick's Fresnel. */
float s = saturate(pow5f(1.0f - saturate(HL)));
float weight = bxdf_ggx_eval_reflection(N, R, V, roughness, false).weight;
scale += (1.0f - s) * weight;
bias += s * weight;
}
/* Refraction. */
float3 T = bxdf_ggx_sample_refraction(Xi, V, roughness, ior, 0.0f, false).direction;
float NT = T.z;
/* In the case of TIR, `T == float3(0)`. */
if (NT < 0.0f) {
float3 H = normalize(ior * T + V);
float HL = abs(dot(H, T));
/* Schlick's Fresnel. */
float s = saturate(pow5f(1.0f - saturate(HL)));
float weight = bxdf_ggx_eval_refraction(N, T, V, roughness, ior, 0.0f, false).weight;
transmission_factor += (1.0f - s) * weight;
}
}
transmission_factor /= float(sample_count);
scale /= float(sample_count);
bias /= float(sample_count);
return float4(scale, bias, transmission_factor, 0.0f);
}
/* 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.
*
* Parameterize with `x = sqrt((ior - 1) / (ior + 1))` for higher precision in 1 < IOR < 2,
* and `y = sqrt(1.0f - cos(theta))`, `z = roughness` similar to BRDF LUT.
*
* The result is interpreted as:
* `transmittance = (1 - F0) * transmission_factor`. */
float4 ggx_btdf_gt_one(float3 lut_coord)
{
float f0 = clamp(square(lut_coord.x), 1e-4f, 0.9999f);
float ior = (1.0f + f0) / (1.0f - f0);
float NV = clamp(1.0f - square(lut_coord.y), 1e-4f, 0.9999f);
float3 V = float3(sqrt(1.0f - square(NV)), 0.0f, NV);
float3 N = float3(0.0f, 0.0f, 1.0f);
/* 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. */
float transmission_factor = 0.0f;
constexpr uint sample_count = 512u * 512u;
for (uint i = 0u; i < sample_count; i++) {
float2 rand = hammersley_2d(i, sample_count);
float3 Xi = sample_cylinder(rand);
/* Refraction. */
float3 L = bxdf_ggx_sample_refraction(Xi, V, roughness, ior, 0.0f, false).direction;
float NL = L.z;
if (NL < 0.0f) {
float3 H = normalize(ior * L + V);
float HV = abs(dot(H, V));
/* Schlick's Fresnel. */
float s = saturate(pow5f(1.0f - saturate(HV)));
float weight = bxdf_ggx_eval_refraction(N, L, V, roughness, ior, 0.0f, false).weight;
transmission_factor += (1.0f - s) * weight;
}
}
transmission_factor /= float(sample_count);
return float4(transmission_factor, 0.0f, 0.0f, 0.0f);
}
/* 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.
* For each distance `d` we compute the radiance incoming from an hypothetical parallel plane. */
float4 burley_sss_translucency(float3 lut_coord)
{
/* Note that we only store the 1st (radius == 1) component.
* The others are here for debugging overall appearance. */
float3 radii = float3(1.0f, 0.2f, 0.1f);
float thickness = lut_coord.x * SSS_TRANSMIT_LUT_RADIUS;
float3 r = thickness / radii;
/* Manual fit based on cycles render of a backlit slab of varying thickness.
* Mean Error: 0.003
* Max Error: 0.015 */
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));
/* Mask off the end progressively to 0. */
profile *= saturate(1.0f - pow5f(lut_coord.x));
return float4(profile, 0.0f);
}
float4 random_walk_sss_translucency(float3 lut_coord)
{
/* Note that we only store the 1st (radius == 1) component.
* The others are here for debugging overall appearance. */
float3 radii = float3(1.0f, 0.2f, 0.1f);
float thickness = lut_coord.x * SSS_TRANSMIT_LUT_RADIUS;
float3 r = thickness / radii;
/* Manual fit based on cycles render of a backlit slab of varying thickness.
* Mean Error: 0.003
* Max Error: 0.016 */
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)));
profile = saturate(profile - 0.1f);
/* Mask off the end progressively to 0. */
profile *= saturate(1.0f - pow5f(lut_coord.x));
return float4(profile, 0.0f);
}
void main()
{
/* Make sure coordinates are covering the whole [0..1] range at texel center. */
float3 lut_normalized_coordinate = float3(gl_GlobalInvocationID) / float3(table_extent - 1);
/* Make sure missing cases are noticeable. */
float4 result = float4(-1);
switch (uint(table_type)) {
case LUT_GGX_BRDF_SPLIT_SUM:
result = ggx_brdf_split_sum(lut_normalized_coordinate);
break;
case LUT_GGX_BTDF_IOR_GT_ONE:
result = ggx_btdf_gt_one(lut_normalized_coordinate);
break;
case LUT_GGX_BSDF_SPLIT_SUM:
result = ggx_bsdf_split_sum(lut_normalized_coordinate);
break;
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;
}
imageStore(table_img, int3(gl_GlobalInvocationID), result);
}