Ajout du projet Depths sur Git

This commit is contained in:
2026-04-30 12:24:52 +02:00
commit a143ea22c7
6651 changed files with 77423 additions and 0 deletions
@@ -0,0 +1,69 @@
#include "mx_microfacet_specular.glsl"
vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distribution, FresnelData fd)
{
// Generate tangent frame.
X = normalize(X - dot(X, N) * N);
vec3 Y = cross(N, X);
mat3 tangentToWorld = mat3(X, Y, N);
// Transform the view vector to tangent space.
V = vec3(dot(V, X), dot(V, Y), dot(V, N));
// Compute derived properties.
float NdotV = clamp(V.z, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(alpha);
float G1V = mx_ggx_smith_G1(NdotV, avgAlpha);
// Integrate outgoing radiance using filtered importance sampling.
// http://cgg.mff.cuni.cz/~jaroslav/papers/2008-egsr-fis/2008-egsr-fis-final-embedded.pdf
vec3 radiance = vec3(0.0);
int envRadianceSamples = $envRadianceSamples;
for (int i = 0; i < envRadianceSamples; i++)
{
vec2 Xi = mx_spherical_fibonacci(i, envRadianceSamples);
// Compute the half vector and incoming light direction.
vec3 H = mx_ggx_importance_sample_VNDF(Xi, V, alpha);
vec3 L = fd.refraction ? mx_refraction_solid_sphere(-V, H, fd.ior.x) : -reflect(V, H);
// Compute dot products for this sample.
float NdotL = clamp(L.z, M_FLOAT_EPS, 1.0);
float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
// Sample the environment light from the given direction.
vec3 Lw = tangentToWorld * L;
float pdf = mx_ggx_NDF(H, alpha) * G1V / (4.0 * NdotV);
float lod = mx_latlong_compute_lod(Lw, pdf, float($envRadianceMips - 1), envRadianceSamples);
vec3 sampleColor = mx_latlong_map_lookup(Lw, $envMatrix, lod, $envRadiance);
// Compute the Fresnel term.
vec3 F = mx_compute_fresnel(VdotH, fd);
// Compute the geometric term.
float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
// Compute the combined FG term, which is inverted for refraction.
vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : F * G;
// Add the radiance contribution of this sample.
// From https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
// incidentLight = sampleColor * NdotL
// microfacetSpecular = D * F * G / (4 * NdotL * NdotV)
// pdf = D * G1V / (4 * NdotV);
// radiance = incidentLight * microfacetSpecular / pdf
radiance += sampleColor * FG;
}
// Apply the global component of the geometric term and normalize.
radiance /= G1V * float(envRadianceSamples);
// Return the final radiance.
return radiance * $envLightIntensity;
}
vec3 mx_environment_irradiance(vec3 N)
{
vec3 Li = mx_latlong_map_lookup(N, $envMatrix, 0.0, $envIrradiance);
return Li * $envLightIntensity;
}
@@ -0,0 +1,11 @@
#include "mx_microfacet_specular.glsl"
vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 roughness, int distribution, FresnelData fd)
{
return vec3(0.0);
}
vec3 mx_environment_irradiance(vec3 N)
{
return vec3(0.0);
}
@@ -0,0 +1,30 @@
#include "mx_microfacet_specular.glsl"
// Return the mip level associated with the given alpha in a prefiltered environment.
float mx_latlong_alpha_to_lod(float alpha)
{
float lodBias = (alpha < 0.25) ? sqrt(alpha) : 0.5 * alpha + 0.375;
return lodBias * float($envRadianceMips - 1);
}
vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distribution, FresnelData fd)
{
N = mx_forward_facing_normal(N, V);
vec3 L = fd.refraction ? mx_refraction_solid_sphere(-V, N, fd.ior.x) : -reflect(V, N);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(alpha);
vec3 F = mx_compute_fresnel(NdotV, fd);
float G = mx_ggx_smith_G2(NdotV, NdotV, avgAlpha);
vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : F * G;
vec3 Li = mx_latlong_map_lookup(L, $envMatrix, mx_latlong_alpha_to_lod(avgAlpha), $envRadiance);
return Li * FG * $envLightIntensity;
}
vec3 mx_environment_irradiance(vec3 N)
{
vec3 Li = mx_latlong_map_lookup(N, $envMatrix, 0.0, $envIrradiance);
return Li * $envLightIntensity;
}
@@ -0,0 +1,10 @@
#include "mx_microfacet_sheen.glsl"
#include "mx_microfacet_specular.glsl"
vec3 mx_generate_dir_albedo_table()
{
vec2 uv = gl_FragCoord.xy / $albedoTableSize;
vec2 ggxDirAlbedo = mx_ggx_dir_albedo(uv.x, uv.y, vec3(1, 0, 0), vec3(0, 1, 0)).xy;
float sheenDirAlbedo = mx_imageworks_sheen_dir_albedo(uv.x, uv.y);
return vec3(ggxDirAlbedo, sheenDirAlbedo);
}
@@ -0,0 +1,78 @@
#include "mx_microfacet_specular.glsl"
// Construct an orthonormal basis from a unit vector.
// https://graphics.pixar.com/library/OrthonormalB/paper.pdf
mat3 mx_orthonormal_basis(vec3 N)
{
float sign = (N.z < 0.0) ? -1.0 : 1.0;
float a = -1.0 / (sign + N.z);
float b = N.x * N.y * a;
vec3 X = vec3(1.0 + sign * N.x * N.x * a, sign * b, -sign * N.x);
vec3 Y = vec3(b, sign + N.y * N.y * a, -N.y);
return mat3(X, Y, N);
}
// Return the alpha associated with the given mip level in a prefiltered environment.
float mx_latlong_lod_to_alpha(float lod)
{
float lodBias = lod / float($envRadianceMips - 1);
return (lodBias < 0.5) ? mx_square(lodBias) : 2.0 * (lodBias - 0.375);
}
// The inverse of mx_latlong_projection.
vec3 mx_latlong_map_projection_inverse(vec2 uv)
{
float latitude = (uv.y - 0.5) * M_PI;
float longitude = (uv.x - 0.5) * M_PI * 2.0;
float x = -cos(latitude) * sin(longitude);
float y = -sin(latitude);
float z = cos(latitude) * cos(longitude);
return vec3(x, y, z);
}
vec3 mx_generate_prefilter_env()
{
// The tangent view vector is aligned with the normal.
vec3 V = vec3(0.0, 0.0, 1.0);
float NdotV = 1.0;
// Compute derived properties.
vec2 uv = gl_FragCoord.xy * pow(2.0, $envPrefilterMip) / vec2(textureSize($envRadiance, 0));
vec3 worldN = mx_latlong_map_projection_inverse(uv);
mat3 tangentToWorld = mx_orthonormal_basis(worldN);
float alpha = mx_latlong_lod_to_alpha(float($envPrefilterMip));
float G1V = mx_ggx_smith_G1(NdotV, alpha);
// Integrate the LD term for the given environment and alpha.
vec3 radiance = vec3(0.0, 0.0, 0.0);
float weight = 0.0;
int envRadianceSamples = 1024;
for (int i = 0; i < envRadianceSamples; i++)
{
vec2 Xi = mx_spherical_fibonacci(i, envRadianceSamples);
// Compute the half vector and incoming light direction.
vec3 H = mx_ggx_importance_sample_VNDF(Xi, V, vec2(alpha));
vec3 L = -V + 2.0 * H.z * H;
// Compute dot products for this sample.
float NdotL = clamp(L.z, M_FLOAT_EPS, 1.0);
// Compute the geometric term.
float G = mx_ggx_smith_G2(NdotL, NdotV, alpha);
// Sample the environment light from the given direction.
vec3 Lw = tangentToWorld * L;
float pdf = mx_ggx_NDF(H, vec2(alpha)) * G1V / (4.0 * NdotV);
float lod = mx_latlong_compute_lod(Lw, pdf, float($envRadianceMips - 1), envRadianceSamples);
vec3 sampleColor = mx_latlong_map_lookup(Lw, $envMatrix, lod, $envRadiance);
// Add the radiance contribution of this sample.
radiance += G * sampleColor;
weight += G;
}
return radiance / weight;
}
@@ -0,0 +1,77 @@
#define M_PI 3.1415926535897932
#define M_PI_INV (1.0 / M_PI)
float mx_pow5(float x)
{
return mx_square(mx_square(x)) * x;
}
// Standard Schlick Fresnel
float mx_fresnel_schlick(float cosTheta, float F0)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return F0 + (1.0 - F0) * x5;
}
vec3 mx_fresnel_schlick(float cosTheta, vec3 F0)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return F0 + (1.0 - F0) * x5;
}
// Generalized Schlick Fresnel
float mx_fresnel_schlick(float cosTheta, float F0, float F90)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return mix(F0, F90, x5);
}
vec3 mx_fresnel_schlick(float cosTheta, vec3 F0, vec3 F90)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return mix(F0, F90, x5);
}
// Generalized Schlick Fresnel with a variable exponent
float mx_fresnel_schlick(float cosTheta, float F0, float F90, float exponent)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
return mix(F0, F90, pow(x, exponent));
}
vec3 mx_fresnel_schlick(float cosTheta, vec3 F0, vec3 F90, float exponent)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
return mix(F0, F90, pow(x, exponent));
}
// Enforce that the given normal is forward-facing from the specified view direction.
vec3 mx_forward_facing_normal(vec3 N, vec3 V)
{
return (dot(N, V) < 0.0) ? -N : N;
}
// https://www.graphics.rwth-aachen.de/publication/2/jgt.pdf
float mx_golden_ratio_sequence(int i)
{
const float GOLDEN_RATIO = 1.6180339887498948;
return fract((float(i) + 1.0) * GOLDEN_RATIO);
}
// https://people.irisa.fr/Ricardo.Marques/articles/2013/SF_CGF.pdf
vec2 mx_spherical_fibonacci(int i, int numSamples)
{
return vec2((float(i) + 0.5) / float(numSamples), mx_golden_ratio_sequence(i));
}
// Generate a uniform-weighted sample in the unit hemisphere.
vec3 mx_uniform_sample_hemisphere(vec2 Xi)
{
float phi = 2.0 * M_PI * Xi.x;
float cosTheta = 1.0 - Xi.y;
float sinTheta = sqrt(1.0 - mx_square(cosTheta));
return vec3(cos(phi) * sinTheta,
sin(phi) * sinTheta,
cosTheta);
}
@@ -0,0 +1,84 @@
#include "mx_microfacet.glsl"
// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
// based on https://mimosa-pudica.net/improved-oren-nayar.html.
float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
{
float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
float s = LdotV - NdotL * NdotV;
float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
float sigma2 = mx_square(roughness * M_PI);
float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
float B = 0.45 * sigma2 / (sigma2 + 0.09);
return A + B * stinv;
}
// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
// Section 5.3
float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
{
vec3 H = normalize(L + V);
float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
float F90 = 0.5 + (2.0 * roughness * mx_square(LdotH));
float refL = mx_fresnel_schlick(NdotL, 1.0, F90);
float refV = mx_fresnel_schlick(NdotV, 1.0, F90);
return refL * refV;
}
// Compute the directional albedo component of Burley diffuse for the given
// view angle and roughness. Curve fit provided by Stephen Hill.
float mx_burley_diffuse_dir_albedo(float NdotV, float roughness)
{
float x = NdotV;
float fit0 = 0.97619 - 0.488095 * mx_pow5(1.0 - x);
float fit1 = 1.55754 + (-2.02221 + (2.56283 - 1.06244 * x) * x) * x;
return mix(fit0, fit1, roughness);
}
// Evaluate the Burley diffusion profile for the given distance and diffusion shape.
// Based on https://graphics.pixar.com/library/ApproxBSSRDF/
vec3 mx_burley_diffusion_profile(float dist, vec3 shape)
{
vec3 num1 = exp(-shape * dist);
vec3 num2 = exp(-shape * dist / 3.0);
float denom = max(dist, M_FLOAT_EPS);
return (num1 + num2) / denom;
}
// Integrate the Burley diffusion profile over a sphere of the given radius.
// Inspired by Eric Penner's presentation in http://advances.realtimerendering.com/s2011/
vec3 mx_integrate_burley_diffusion(vec3 N, vec3 L, float radius, vec3 mfp)
{
float theta = acos(dot(N, L));
// Estimate the Burley diffusion shape from mean free path.
vec3 shape = vec3(1.0) / max(mfp, 0.1);
// Integrate the profile over the sphere.
vec3 sumD = vec3(0.0);
vec3 sumR = vec3(0.0);
const int SAMPLE_COUNT = 32;
const float SAMPLE_WIDTH = (2.0 * M_PI) / float(SAMPLE_COUNT);
for (int i = 0; i < SAMPLE_COUNT; i++)
{
float x = -M_PI + (float(i) + 0.5) * SAMPLE_WIDTH;
float dist = radius * abs(2.0 * sin(x * 0.5));
vec3 R = mx_burley_diffusion_profile(dist, shape);
sumD += R * max(cos(theta + x), 0.0);
sumR += R;
}
return sumD / sumR;
}
vec3 mx_subsurface_scattering_approx(vec3 N, vec3 L, vec3 P, vec3 albedo, vec3 mfp)
{
float curvature = length(fwidth(N)) / length(fwidth(P));
float radius = 1.0 / max(curvature, 0.01);
return albedo * mx_integrate_burley_diffusion(N, L, radius, mfp) / vec3(M_PI);
}
@@ -0,0 +1,92 @@
#include "mx_microfacet.glsl"
// http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
// Equation 2
float mx_imageworks_sheen_NDF(float NdotH, float roughness)
{
float invRoughness = 1.0 / max(roughness, 0.005);
float cos2 = NdotH * NdotH;
float sin2 = 1.0 - cos2;
return (2.0 + invRoughness) * pow(sin2, invRoughness * 0.5) / (2.0 * M_PI);
}
float mx_imageworks_sheen_brdf(float NdotL, float NdotV, float NdotH, float roughness)
{
// Microfacet distribution.
float D = mx_imageworks_sheen_NDF(NdotH, roughness);
// Fresnel and geometry terms are ignored.
float F = 1.0;
float G = 1.0;
// We use a smoother denominator, as in:
// https://blog.selfshadow.com/publications/s2013-shading-course/rad/s2013_pbs_rad_notes.pdf
return D * F * G / (4.0 * (NdotL + NdotV - NdotL*NdotV));
}
// Rational quadratic fit to Monte Carlo data for Imageworks sheen directional albedo.
float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
{
vec2 r = vec2(13.67300, 1.0) +
vec2(-68.78018, 61.57746) * NdotV +
vec2(799.08825, 442.78211) * roughness +
vec2(-905.00061, 2597.49308) * NdotV * roughness +
vec2(60.28956, 121.81241) * mx_square(NdotV) +
vec2(1086.96473, 3045.55075) * mx_square(roughness);
return r.x / r.y;
}
float mx_imageworks_sheen_dir_albedo_table_lookup(float NdotV, float roughness)
{
#if DIRECTIONAL_ALBEDO_METHOD == 1
if (textureSize($albedoTable, 0).x > 1)
{
return texture($albedoTable, vec2(NdotV, roughness)).b;
}
#endif
return 0.0;
}
float mx_imageworks_sheen_dir_albedo_monte_carlo(float NdotV, float roughness)
{
NdotV = clamp(NdotV, M_FLOAT_EPS, 1.0);
vec3 V = vec3(sqrt(1.0f - mx_square(NdotV)), 0, NdotV);
float radiance = 0.0;
const int SAMPLE_COUNT = 64;
for (int i = 0; i < SAMPLE_COUNT; i++)
{
vec2 Xi = mx_spherical_fibonacci(i, SAMPLE_COUNT);
// Compute the incoming light direction and half vector.
vec3 L = mx_uniform_sample_hemisphere(Xi);
vec3 H = normalize(L + V);
// Compute dot products for this sample.
float NdotL = clamp(L.z, M_FLOAT_EPS, 1.0);
float NdotH = clamp(H.z, M_FLOAT_EPS, 1.0);
// Compute sheen reflectance.
float reflectance = mx_imageworks_sheen_brdf(NdotL, NdotV, NdotH, roughness);
// Add the radiance contribution of this sample.
// uniform_pdf = 1 / (2 * PI)
// radiance = reflectance * NdotL / uniform_pdf;
radiance += reflectance * NdotL * 2.0 * M_PI;
}
// Return the final directional albedo.
return radiance / float(SAMPLE_COUNT);
}
float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
{
#if DIRECTIONAL_ALBEDO_METHOD == 0
float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
#elif DIRECTIONAL_ALBEDO_METHOD == 1
float dirAlbedo = mx_imageworks_sheen_dir_albedo_table_lookup(NdotV, roughness);
#else
float dirAlbedo = mx_imageworks_sheen_dir_albedo_monte_carlo(NdotV, roughness);
#endif
return clamp(dirAlbedo, 0.0, 1.0);
}
@@ -0,0 +1,606 @@
#include "mx_microfacet.glsl"
// Fresnel model options.
const int FRESNEL_MODEL_DIELECTRIC = 0;
const int FRESNEL_MODEL_CONDUCTOR = 1;
const int FRESNEL_MODEL_SCHLICK = 2;
const int FRESNEL_MODEL_AIRY = 3;
const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
// XYZ to CIE 1931 RGB color space (using neutral E illuminant)
const mat3 XYZ_TO_RGB = mat3(2.3706743, -0.5138850, 0.0052982, -0.9000405, 1.4253036, -0.0146949, -0.4706338, 0.0885814, 1.0093968);
// Parameters for Fresnel calculations.
struct FresnelData
{
int model;
// Physical Fresnel
vec3 ior;
vec3 extinction;
// Generalized Schlick Fresnel
vec3 F0;
vec3 F90;
float exponent;
// Thin film
float tf_thickness;
float tf_ior;
// Refraction
bool refraction;
#ifdef __METAL__
FresnelData(int _model = 0,
vec3 _ior = vec3(0.0f),
vec3 _extinction = vec3(0.0f),
vec3 _F0 = vec3(0.0f),
vec3 _F90 = vec3(0.0f),
float _exponent = 0.0f,
float _tf_thickness = 0.0f,
float _tf_ior = 0.0f,
bool _refraction = false) :
model(_model),
ior(_ior),
extinction(_extinction),
F0(_F0), F90(_F90), exponent(_exponent),
tf_thickness(_tf_thickness),
tf_ior(_tf_ior),
refraction(_refraction) {}
#endif
};
// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
// Appendix B.2 Equation 13
float mx_ggx_NDF(vec3 H, vec2 alpha)
{
vec2 He = H.xy / alpha;
float denom = dot(He, He) + mx_square(H.z);
return 1.0 / (M_PI * alpha.x * alpha.y * mx_square(denom));
}
// https://ggx-research.github.io/publication/2023/06/09/publication-ggx.html
vec3 mx_ggx_importance_sample_VNDF(vec2 Xi, vec3 V, vec2 alpha)
{
// Transform the view direction to the hemisphere configuration.
V = normalize(vec3(V.xy * alpha, V.z));
// Sample a spherical cap in (-V.z, 1].
float phi = 2.0 * M_PI * Xi.x;
float z = (1.0 - Xi.y) * (1.0 + V.z) - V.z;
float sinTheta = sqrt(clamp(1.0 - z * z, 0.0, 1.0));
float x = sinTheta * cos(phi);
float y = sinTheta * sin(phi);
vec3 c = vec3(x, y, z);
// Compute the microfacet normal.
vec3 H = c + V;
// Transform the microfacet normal back to the ellipsoid configuration.
H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
return H;
}
// https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
// Equation 34
float mx_ggx_smith_G1(float cosTheta, float alpha)
{
float cosTheta2 = mx_square(cosTheta);
float tanTheta2 = (1.0 - cosTheta2) / cosTheta2;
return 2.0 / (1.0 + sqrt(1.0 + mx_square(alpha) * tanTheta2));
}
// Height-correlated Smith masking-shadowing
// http://jcgt.org/published/0003/02/03/paper.pdf
// Equations 72 and 99
float mx_ggx_smith_G2(float NdotL, float NdotV, float alpha)
{
float alpha2 = mx_square(alpha);
float lambdaL = sqrt(alpha2 + (1.0 - alpha2) * mx_square(NdotL));
float lambdaV = sqrt(alpha2 + (1.0 - alpha2) * mx_square(NdotV));
return 2.0 / (lambdaL / NdotL + lambdaV / NdotV);
}
// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
vec3 mx_ggx_dir_albedo_analytic(float NdotV, float alpha, vec3 F0, vec3 F90)
{
float x = NdotV;
float y = alpha;
float x2 = mx_square(x);
float y2 = mx_square(y);
vec4 r = vec4(0.1003, 0.9345, 1.0, 1.0) +
vec4(-0.6303, -2.323, -1.765, 0.2281) * x +
vec4(9.748, 2.229, 8.263, 15.94) * y +
vec4(-2.038, -3.748, 11.53, -55.83) * x * y +
vec4(29.34, 1.424, 28.96, 13.08) * x2 +
vec4(-8.245, -0.7684, -7.507, 41.26) * y2 +
vec4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
vec4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
vec4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
vec2 AB = clamp(r.xy / r.zw, 0.0, 1.0);
return F0 * AB.x + F90 * AB.y;
}
vec3 mx_ggx_dir_albedo_table_lookup(float NdotV, float alpha, vec3 F0, vec3 F90)
{
#if DIRECTIONAL_ALBEDO_METHOD == 1
if (textureSize($albedoTable, 0).x > 1)
{
vec2 AB = texture($albedoTable, vec2(NdotV, alpha)).rg;
return F0 * AB.x + F90 * AB.y;
}
#endif
return vec3(0.0);
}
// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
vec3 mx_ggx_dir_albedo_monte_carlo(float NdotV, float alpha, vec3 F0, vec3 F90)
{
NdotV = clamp(NdotV, M_FLOAT_EPS, 1.0);
vec3 V = vec3(sqrt(1.0 - mx_square(NdotV)), 0, NdotV);
vec2 AB = vec2(0.0);
const int SAMPLE_COUNT = 64;
for (int i = 0; i < SAMPLE_COUNT; i++)
{
vec2 Xi = mx_spherical_fibonacci(i, SAMPLE_COUNT);
// Compute the half vector and incoming light direction.
vec3 H = mx_ggx_importance_sample_VNDF(Xi, V, vec2(alpha));
vec3 L = -reflect(V, H);
// Compute dot products for this sample.
float NdotL = clamp(L.z, M_FLOAT_EPS, 1.0);
float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
// Compute the Fresnel term.
float Fc = mx_fresnel_schlick(VdotH, 0.0, 1.0);
// Compute the per-sample geometric term.
// https://hal.inria.fr/hal-00996995v2/document, Algorithm 2
float G2 = mx_ggx_smith_G2(NdotL, NdotV, alpha);
// Add the contribution of this sample.
AB += vec2(G2 * (1.0 - Fc), G2 * Fc);
}
// Apply the global component of the geometric term and normalize.
AB /= mx_ggx_smith_G1(NdotV, alpha) * float(SAMPLE_COUNT);
// Return the final directional albedo.
return F0 * AB.x + F90 * AB.y;
}
vec3 mx_ggx_dir_albedo(float NdotV, float alpha, vec3 F0, vec3 F90)
{
#if DIRECTIONAL_ALBEDO_METHOD == 0
return mx_ggx_dir_albedo_analytic(NdotV, alpha, F0, F90);
#elif DIRECTIONAL_ALBEDO_METHOD == 1
return mx_ggx_dir_albedo_table_lookup(NdotV, alpha, F0, F90);
#else
return mx_ggx_dir_albedo_monte_carlo(NdotV, alpha, F0, F90);
#endif
}
float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
{
return mx_ggx_dir_albedo(NdotV, alpha, vec3(F0), vec3(F90)).x;
}
// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
// Equations 14 and 16
vec3 mx_ggx_energy_compensation(float NdotV, float alpha, vec3 Fss)
{
float Ess = mx_ggx_dir_albedo(NdotV, alpha, 1.0, 1.0);
return 1.0 + Fss * (1.0 - Ess) / Ess;
}
float mx_ggx_energy_compensation(float NdotV, float alpha, float Fss)
{
return mx_ggx_energy_compensation(NdotV, alpha, vec3(Fss)).x;
}
// Compute the average of an anisotropic alpha pair.
float mx_average_alpha(vec2 alpha)
{
return sqrt(alpha.x * alpha.y);
}
// Convert a real-valued index of refraction to normal-incidence reflectivity.
float mx_ior_to_f0(float ior)
{
return mx_square((ior - 1.0) / (ior + 1.0));
}
// Convert normal-incidence reflectivity to real-valued index of refraction.
float mx_f0_to_ior(float F0)
{
float sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
return (1.0 + sqrtF0) / (1.0 - sqrtF0);
}
vec3 mx_f0_to_ior_colored(vec3 F0)
{
vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
}
// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
float mx_fresnel_dielectric(float cosTheta, float ior)
{
if (cosTheta < 0.0)
return 1.0;
float g = ior*ior + cosTheta*cosTheta - 1.0;
// Check for total internal reflection
if (g < 0.0)
return 1.0;
g = sqrt(g);
float gmc = g - cosTheta;
float gpc = g + cosTheta;
float x = gmc / gpc;
float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
return 0.5 * x * x * (1.0 + y * y);
}
void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
{
if (cosTheta < 0.0) {
Rp = 1.0;
Rs = 1.0;
return;
}
float cosTheta2 = cosTheta * cosTheta;
float sinTheta2 = 1.0 - cosTheta2;
float n2 = n * n;
float t0 = n2 - sinTheta2;
float a2plusb2 = sqrt(t0 * t0);
float t1 = a2plusb2 + cosTheta2;
float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
float t2 = 2.0 * a * cosTheta;
Rs = (t1 - t2) / (t1 + t2);
float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
float t4 = t2 * sinTheta2;
Rp = Rs * (t3 - t4) / (t3 + t4);
}
void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
{
float n = eta2 / eta1;
mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
}
void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
{
cosTheta = clamp(cosTheta, 0.0, 1.0);
float cosTheta2 = cosTheta * cosTheta;
float sinTheta2 = 1.0 - cosTheta2;
vec3 n2 = n * n;
vec3 k2 = k * k;
vec3 t0 = n2 - k2 - vec3(sinTheta2);
vec3 a2plusb2 = sqrt(t0 * t0 + 4.0 * n2 * k2);
vec3 t1 = a2plusb2 + vec3(cosTheta2);
vec3 a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
vec3 t2 = 2.0 * a * cosTheta;
Rs = (t1 - t2) / (t1 + t2);
vec3 t3 = cosTheta2 * a2plusb2 + vec3(sinTheta2 * sinTheta2);
vec3 t4 = t2 * sinTheta2;
Rp = Rs * (t3 - t4) / (t3 + t4);
}
void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
{
vec3 n = eta2 / eta1;
vec3 k = kappa2 / eta1;
mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
}
vec3 mx_fresnel_conductor(float cosTheta, vec3 n, vec3 k)
{
vec3 Rp, Rs;
mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
return 0.5 * (Rp + Rs);
}
// Phase shift due to a dielectric material
void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
{
float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
if (eta2 > eta1) {
phiP = cosTheta < cosB ? M_PI : 0.0f;
phiS = 0.0f;
} else {
phiP = cosTheta < cosB ? 0.0f : M_PI;
phiS = M_PI;
}
}
// Phase shift due to a conducting material
void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
{
if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
// Use dielectric formula to increase performance
float phiPx, phiSx;
mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
phiP = vec3(phiPx, phiPx, phiPx);
phiS = vec3(phiSx, phiSx, phiSx);
return;
}
vec3 k2 = kappa2 / eta2;
vec3 sinThetaSqr = vec3(1.0) - cosTheta * cosTheta;
vec3 A = eta2*eta2*(vec3(1.0)-k2*k2) - eta1*eta1*sinThetaSqr;
vec3 B = sqrt(A*A + mx_square(2.0*eta2*eta2*k2));
vec3 U = sqrt((A+B)/2.0);
vec3 V = max(vec3(0.0), sqrt((B-A)/2.0));
phiS = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
phiP = atan(2.0*eta1*eta2*eta2*cosTheta * (2.0*k2*U - (vec3(1.0)-k2*k2) * V),
mx_square(eta2*eta2*(vec3(1.0)+k2*k2)*cosTheta) - eta1*eta1*(U*U+V*V));
}
// Evaluation XYZ sensitivity curves in Fourier space
vec3 mx_eval_sensitivity(float opd, vec3 shift)
{
// Use Gaussian fits, given by 3 parameters: val, pos and var
float phase = 2.0*M_PI * opd;
vec3 val = vec3(5.4856e-13, 4.4201e-13, 5.2481e-13);
vec3 pos = vec3(1.6810e+06, 1.7953e+06, 2.2084e+06);
vec3 var = vec3(4.3278e+09, 9.3046e+09, 6.6121e+09);
vec3 xyz = val * sqrt(2.0*M_PI * var) * cos(pos * phase + shift) * exp(- var * phase*phase);
xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * cos(2.2399e+06 * phase + shift[0]) * exp(- 4.5282e+09 * phase*phase);
return xyz / 1.0685e-7;
}
// A Practical Extension to Microfacet Theory for the Modeling of Varying Iridescence
// https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html
vec3 mx_fresnel_airy(float cosTheta, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
vec3 f0, vec3 f90, float exponent, bool use_schlick)
{
// Convert nm -> m
float d = tf_thickness * 1.0e-9;
// Assume vacuum on the outside
float eta1 = 1.0;
float eta2 = max(tf_ior, eta1);
vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
// Compute the Spectral versions of the Fresnel reflectance and
// transmitance for each interface.
float R12p, T121p, R12s, T121s;
vec3 R23p, R23s;
// Reflected and transmitted parts in the thin film
mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
// Reflected part by the base
float scale = eta1 / eta2;
float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
float cosTheta2 = sqrt(cosThetaTSqr);
if (use_schlick)
{
vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
R23p = 0.5 * f;
R23s = 0.5 * f;
}
else
{
mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
}
// Check for total internal reflection
if (cosThetaTSqr <= 0.0f)
{
R12s = 1.0;
R12p = 1.0;
}
// Compute the transmission coefficients
T121p = 1.0 - R12p;
T121s = 1.0 - R12s;
// Optical path difference
float D = 2.0 * eta2 * d * cosTheta2;
float phi21p, phi21s;
vec3 phi23p, phi23s, r123s, r123p;
// Evaluate the phase shift
mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
if (use_schlick)
{
phi23p = vec3(
(eta3[0] < eta2) ? M_PI : 0.0,
(eta3[1] < eta2) ? M_PI : 0.0,
(eta3[2] < eta2) ? M_PI : 0.0);
phi23s = phi23p;
}
else
{
mx_fresnel_conductor_phase_polarized(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
}
phi21p = M_PI - phi21p;
phi21s = M_PI - phi21s;
r123p = max(vec3(0.0), sqrt(R12p*R23p));
r123s = max(vec3(0.0), sqrt(R12s*R23s));
// Evaluate iridescence term
vec3 I = vec3(0.0);
vec3 C0, Cm, Sm;
// Iridescence term using spectral antialiasing for Parallel polarization
vec3 S0 = vec3(1.0);
// Reflectance term for m=0 (DC term amplitude)
vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
C0 = R12p + Rs;
I += C0 * S0;
// Reflectance term for m>0 (pairs of diracs)
Cm = Rs - T121p;
for (int m=1; m<=2; ++m)
{
Cm *= r123p;
Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
I += Cm*Sm;
}
// Iridescence term using spectral antialiasing for Perpendicular polarization
// Reflectance term for m=0 (DC term amplitude)
vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
C0 = R12s + Rp;
I += C0 * S0;
// Reflectance term for m>0 (pairs of diracs)
Cm = Rp - T121s ;
for (int m=1; m<=2; ++m)
{
Cm *= r123s;
Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
I += Cm*Sm;
}
// Average parallel and perpendicular polarization
I *= 0.5;
// Convert back to RGB reflectance
I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
return I;
}
FresnelData mx_init_fresnel_data(int model)
{
return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
}
FresnelData mx_init_fresnel_dielectric(float ior)
{
FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
fd.ior = vec3(ior);
return fd;
}
FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
{
FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
fd.ior = ior;
fd.extinction = extinction;
return fd;
}
FresnelData mx_init_fresnel_schlick(vec3 F0)
{
FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
fd.F0 = F0;
fd.F90 = vec3(1.0);
fd.exponent = 5.0f;
return fd;
}
FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
{
FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
fd.F0 = F0;
fd.F90 = F90;
fd.exponent = exponent;
return fd;
}
FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
{
FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
fd.F0 = F0;
fd.F90 = F90;
fd.exponent = exponent;
fd.tf_thickness = tf_thickness;
fd.tf_ior = tf_ior;
return fd;
}
FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
{
FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
fd.ior = vec3(ior);
fd.tf_thickness = tf_thickness;
fd.tf_ior = tf_ior;
return fd;
}
FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
{
FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
fd.ior = ior;
fd.extinction = extinction;
fd.tf_thickness = tf_thickness;
fd.tf_ior = tf_ior;
return fd;
}
vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
{
if (fd.model == FRESNEL_MODEL_DIELECTRIC)
{
return vec3(mx_fresnel_dielectric(cosTheta, fd.ior.x));
}
else if (fd.model == FRESNEL_MODEL_CONDUCTOR)
{
return mx_fresnel_conductor(cosTheta, fd.ior, fd.extinction);
}
else if (fd.model == FRESNEL_MODEL_SCHLICK)
{
return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
}
else
{
return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
fd.F0, fd.F90, fd.exponent,
fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
}
}
// Compute the refraction of a ray through a solid sphere.
vec3 mx_refraction_solid_sphere(vec3 R, vec3 N, float ior)
{
R = refract(R, N, 1.0 / ior);
vec3 N1 = normalize(R * dot(R, N) - N * 0.5);
return refract(R, N1, ior);
}
vec2 mx_latlong_projection(vec3 dir)
{
float latitude = -asin(dir.y) * M_PI_INV + 0.5;
float longitude = atan(dir.x, -dir.z) * M_PI_INV * 0.5 + 0.5;
return vec2(longitude, latitude);
}
vec3 mx_latlong_map_lookup(vec3 dir, mat4 transform, float lod, sampler2D envSampler)
{
vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
vec2 uv = mx_latlong_projection(envDir);
return textureLod(envSampler, uv, lod).rgb;
}
// Return the mip level with the appropriate coverage for a filtered importance sample.
// https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch20.html
// Section 20.4 Equation 13
float mx_latlong_compute_lod(vec3 dir, float pdf, float maxMipLevel, int envSamples)
{
const float MIP_LEVEL_OFFSET = 1.5;
float effectiveMaxMipLevel = maxMipLevel - MIP_LEVEL_OFFSET;
float distortion = sqrt(1.0 - mx_square(dir.y));
return max(effectiveMaxMipLevel - 0.5 * log2(float(envSamples) * pdf * distortion), 0.0);
}
@@ -0,0 +1,23 @@
// https://developer.nvidia.com/gpugems/gpugems3/part-ii-light-and-shadows/chapter-8-summed-area-variance-shadow-maps
float mx_variance_shadow_occlusion(vec2 moments, float fragmentDepth)
{
const float MIN_VARIANCE = 0.00001;
// One-tailed inequality valid if fragmentDepth > moments.x.
float p = (fragmentDepth <= moments.x) ? 1.0 : 0.0;
// Compute variance.
float variance = moments.y - mx_square(moments.x);
variance = max(variance, MIN_VARIANCE);
// Compute probabilistic upper bound.
float d = fragmentDepth - moments.x;
float pMax = variance / (variance + mx_square(d));
return max(p, pMax);
}
vec2 mx_compute_depth_moments()
{
float depth = gl_FragCoord.z;
return vec2(depth, mx_square(depth));
}
@@ -0,0 +1,6 @@
#include "mx_microfacet_specular.glsl"
vec3 mx_surface_transmission(vec3 N, vec3 V, vec3 X, vec2 alpha, int distribution, FresnelData fd, vec3 tint)
{
return tint;
}
@@ -0,0 +1,14 @@
#include "mx_microfacet_specular.glsl"
vec3 mx_surface_transmission(vec3 N, vec3 V, vec3 X, vec2 alpha, int distribution, FresnelData fd, vec3 tint)
{
// Approximate the appearance of surface transmission as glossy
// environment map refraction, ignoring any scene geometry that might
// be visible through the surface.
fd.refraction = true;
if ($refractionTwoSided)
{
tint = mx_square(tint);
}
return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
}
@@ -0,0 +1,4 @@
void mx_add_edf(vec3 N, vec3 L, EDF in1, EDF in2, out EDF result)
{
result = in1 + in2;
}
@@ -0,0 +1,4 @@
void mx_anisotropic_vdf(vec3 absorption, vec3 scattering, float anisotropy, inout BSDF bsdf)
{
// TODO: Add some approximation for volumetric light absorption.
}
@@ -0,0 +1,17 @@
void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, out vec3 ior, out vec3 extinction)
{
// "Artist Friendly Metallic Fresnel", Ole Gulbrandsen, 2014
// http://jcgt.org/published/0003/04/03/paper.pdf
vec3 r = clamp(reflectivity, 0.0, 0.99);
vec3 r_sqrt = sqrt(r);
vec3 n_min = (1.0 - r) / (1.0 + r);
vec3 n_max = (1.0 + r_sqrt) / (1.0 - r_sqrt);
ior = mix(n_max, n_min, edge_color);
vec3 np1 = ior + 1.0;
vec3 nm1 = ior - 1.0;
vec3 k2 = (np1*np1 * r - nm1*nm1) / (1.0 - r);
k2 = max(k2, 0.0);
extinction = sqrt(k2);
}
@@ -0,0 +1,48 @@
/// XYZ to Rec.709 RGB colorspace conversion
const mat3 XYZ_to_RGB = mat3( 3.2406, -0.9689, 0.0557,
-1.5372, 1.8758, -0.2040,
-0.4986, 0.0415, 1.0570);
void mx_blackbody(float temperatureKelvin, out vec3 colorValue)
{
float xc, yc;
float t, t2, t3, xc2, xc3;
// if value outside valid range of approximation clamp to accepted temperature range
temperatureKelvin = clamp(temperatureKelvin, 1667.0, 25000.0);
t = 1000.0 / temperatureKelvin;
t2 = t * t;
t3 = t * t * t;
// Cubic spline approximation for Kelvin temperature to sRGB conversion
// (https://en.wikipedia.org/wiki/Planckian_locus#Approximation)
if (temperatureKelvin < 4000.0) { // 1667K <= temperatureKelvin < 4000K
xc = -0.2661239 * t3 - 0.2343580 * t2 + 0.8776956 * t + 0.179910;
}
else { // 4000K <= temperatureKelvin <= 25000K
xc = -3.0258469 * t3 + 2.1070379 * t2 + 0.2226347 * t + 0.240390;
}
xc2 = xc * xc;
xc3 = xc * xc * xc;
if (temperatureKelvin < 2222.0) { // 1667K <= temperatureKelvin < 2222K
yc = -1.1063814 * xc3 - 1.34811020 * xc2 + 2.18555832 * xc - 0.20219683;
}
else if (temperatureKelvin < 4000.0) { // 2222K <= temperatureKelvin < 4000K
yc = -0.9549476 * xc3 - 1.37418593 * xc2 + 2.09137015 * xc - 0.16748867;
}
else { // 4000K <= temperatureKelvin <= 25000K
yc = 3.0817580 * xc3 - 5.87338670 * xc2 + 3.75112997 * xc - 0.37001483;
}
if (yc <= 0.0) { // avoid division by zero
colorValue = vec3(1.0);
return;
}
vec3 XYZ = vec3(xc / yc, 1.0, (1.0 - xc - yc) / yc);
colorValue = XYZ_to_RGB * XYZ;
colorValue = max(colorValue, vec3(0.0));
}
@@ -0,0 +1,36 @@
#include "lib/mx_microfacet_diffuse.glsl"
void mx_burley_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
normal = mx_forward_facing_normal(normal, V);
float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
bsdf.response *= mx_burley_diffuse(L, V, normal, NdotL, roughness);
}
void mx_burley_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
normal = mx_forward_facing_normal(normal, V);
float NdotV = clamp(dot(normal, V), M_FLOAT_EPS, 1.0);
vec3 Li = mx_environment_irradiance(normal) *
mx_burley_diffuse_dir_albedo(NdotV, roughness);
bsdf.response = Li * color * weight;
}
@@ -0,0 +1,70 @@
#include "lib/mx_microfacet_specular.glsl"
void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
X = normalize(X - dot(X, N) * N);
vec3 Y = cross(N, X);
vec3 H = normalize(L + V);
float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);
vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
FresnelData fd;
if (bsdf.thickness > 0.0)
fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
else
fd = mx_init_fresnel_conductor(ior_n, ior_k);
vec3 F = mx_compute_fresnel(VdotH, fd);
float D = mx_ggx_NDF(Ht, safeAlpha);
float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
// Note: NdotL is cancelled out
bsdf.response = D * F * G * comp * occlusion * weight / (4.0 * NdotV);
}
void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
FresnelData fd;
if (bsdf.thickness > 0.0)
fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
else
fd = mx_init_fresnel_conductor(ior_n, ior_k);
vec3 F = mx_compute_fresnel(NdotV, fd);
vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
bsdf.response = Li * comp * weight;
}
@@ -0,0 +1,116 @@
#include "lib/mx_microfacet_specular.glsl"
void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
X = normalize(X - dot(X, N) * N);
vec3 Y = cross(N, X);
vec3 H = normalize(L + V);
float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);
vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
FresnelData fd;
vec3 safeTint = max(tint, 0.0);
if (bsdf.thickness > 0.0)
{
fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
}
else
{
fd = mx_init_fresnel_dielectric(ior);
}
vec3 F = mx_compute_fresnel(VdotH, fd);
float D = mx_ggx_NDF(Ht, safeAlpha);
float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
float F0 = mx_ior_to_f0(ior);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, F0, 1.0) * comp;
bsdf.throughput = 1.0 - dirAlbedo * weight;
// Note: NdotL is cancelled out
bsdf.response = D * F * G * comp * safeTint * occlusion * weight / (4.0 * NdotV);
}
void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
FresnelData fd;
vec3 safeTint = max(tint, 0.0);
if (bsdf.thickness > 0.0)
{
fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
}
else
{
fd = mx_init_fresnel_dielectric(ior);
}
vec3 F = mx_compute_fresnel(NdotV, fd);
vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);
float F0 = mx_ior_to_f0(ior);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, F0, 1.0) * comp;
bsdf.throughput = 1.0 - dirAlbedo * weight;
if (scatter_mode != 0)
{
bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, safeTint) * weight;
}
}
void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
FresnelData fd;
vec3 safeTint = max(tint, 0.0);
if (bsdf.thickness > 0.0)
{
fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
}
else
{
fd = mx_init_fresnel_dielectric(ior);
}
vec3 F = mx_compute_fresnel(NdotV, fd);
vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);
float F0 = mx_ior_to_f0(ior);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, F0, 1.0) * comp;
bsdf.throughput = 1.0 - dirAlbedo * weight;
vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
bsdf.response = Li * safeTint * comp * weight;
}
@@ -0,0 +1,5 @@
void mx_displacement_float(float disp, float scale, out displacementshader result)
{
result.offset = vec3(disp);
result.scale = scale;
}
@@ -0,0 +1,5 @@
void mx_displacement_vector3(vec3 disp, float scale, out displacementshader result)
{
result.offset = disp;
result.scale = scale;
}
@@ -0,0 +1,119 @@
#include "lib/mx_microfacet_specular.glsl"
void mx_generalized_schlick_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color0, vec3 color90, float exponent, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
X = normalize(X - dot(X, N) * N);
vec3 Y = cross(N, X);
vec3 H = normalize(L + V);
float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);
vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
FresnelData fd;
vec3 safeColor0 = max(color0, 0.0);
vec3 safeColor90 = max(color90, 0.0);
if (bsdf.thickness > 0.0)
{
fd = mx_init_fresnel_schlick_airy(safeColor0, safeColor90, exponent, bsdf.thickness, bsdf.ior);
}
else
{
fd = mx_init_fresnel_schlick(safeColor0, safeColor90, exponent);
}
vec3 F = mx_compute_fresnel(VdotH, fd);
float D = mx_ggx_NDF(Ht, safeAlpha);
float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, safeColor0, safeColor90) * comp;
float avgDirAlbedo = dot(dirAlbedo, vec3(1.0 / 3.0));
bsdf.throughput = vec3(1.0 - avgDirAlbedo * weight);
// Note: NdotL is cancelled out
bsdf.response = D * F * G * comp * occlusion * weight / (4.0 * NdotV);
}
void mx_generalized_schlick_bsdf_transmission(vec3 V, float weight, vec3 color0, vec3 color90, float exponent, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
FresnelData fd;
vec3 safeColor0 = max(color0, 0.0);
vec3 safeColor90 = max(color90, 0.0);
if (bsdf.thickness > 0.0)
{
fd = mx_init_fresnel_schlick_airy(safeColor0, safeColor90, exponent, bsdf.thickness, bsdf.ior);
}
else
{
fd = mx_init_fresnel_schlick(safeColor0, safeColor90, exponent);
}
vec3 F = mx_compute_fresnel(NdotV, fd);
vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, safeColor0, safeColor90) * comp;
float avgDirAlbedo = dot(dirAlbedo, vec3(1.0 / 3.0));
bsdf.throughput = vec3(1.0 - avgDirAlbedo * weight);
if (scatter_mode != 0)
{
float avgF0 = dot(safeColor0, vec3(1.0 / 3.0));
fd.ior = vec3(mx_f0_to_ior(avgF0));
bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, safeColor0) * weight;
}
}
void mx_generalized_schlick_bsdf_indirect(vec3 V, float weight, vec3 color0, vec3 color90, float exponent, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
FresnelData fd;
vec3 safeColor0 = max(color0, 0.0);
vec3 safeColor90 = max(color90, 0.0);
if (bsdf.thickness > 0.0)
{
fd = mx_init_fresnel_schlick_airy(safeColor0, safeColor90, exponent, bsdf.thickness, bsdf.ior);
}
else
{
fd = mx_init_fresnel_schlick(safeColor0, safeColor90, exponent);
}
vec3 F = mx_compute_fresnel(NdotV, fd);
vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, safeColor0, safeColor90) * comp;
float avgDirAlbedo = dot(dirAlbedo, vec3(1.0 / 3.0));
bsdf.throughput = vec3(1.0 - avgDirAlbedo * weight);
vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
bsdf.response = Li * comp * weight;
}
@@ -0,0 +1,9 @@
#include "lib/mx_microfacet.glsl"
void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
{
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
result = base * f;
}
@@ -0,0 +1,36 @@
#include "lib/mx_microfacet_diffuse.glsl"
void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
normal = mx_forward_facing_normal(normal, V);
float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
if (roughness > 0.0)
{
bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
}
}
void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
normal = mx_forward_facing_normal(normal, V);
vec3 Li = mx_environment_irradiance(normal);
bsdf.response = Li * color * weight;
}
@@ -0,0 +1,15 @@
void mx_roughness_anisotropy(float roughness, float anisotropy, out vec2 result)
{
float roughness_sqr = clamp(roughness*roughness, M_FLOAT_EPS, 1.0);
if (anisotropy > 0.0)
{
float aspect = sqrt(1.0 - clamp(anisotropy, 0.0, 0.98));
result.x = min(roughness_sqr / aspect, 1.0);
result.y = roughness_sqr * aspect;
}
else
{
result.x = roughness_sqr;
result.y = roughness_sqr;
}
}
@@ -0,0 +1,9 @@
void mx_roughness_dual(vec2 roughness, out vec2 result)
{
if (roughness.y < 0.0)
{
roughness.y = roughness.x;
}
result.x = clamp(roughness.x * roughness.x, M_FLOAT_EPS, 1.0);
result.y = clamp(roughness.y * roughness.y, M_FLOAT_EPS, 1.0);
}
@@ -0,0 +1,43 @@
#include "lib/mx_microfacet_sheen.glsl"
void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
vec3 H = normalize(L + V);
float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
float NdotH = clamp(dot(N, H), M_FLOAT_EPS, 1.0);
vec3 fr = color * mx_imageworks_sheen_brdf(NdotL, NdotV, NdotH, roughness);
float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
// We need to include NdotL from the light integral here
// as in this case it's not cancelled out by the BRDF denominator.
bsdf.response = fr * NdotL * occlusion * weight;
}
void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
vec3 Li = mx_environment_irradiance(N);
bsdf.response = Li * color * dirAlbedo * weight;
}
@@ -0,0 +1,34 @@
#include "lib/mx_microfacet_diffuse.glsl"
void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
normal = mx_forward_facing_normal(normal, V);
vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
bsdf.response = sss * visibleOcclusion * weight;
}
void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
normal = mx_forward_facing_normal(normal, V);
// For now, we render indirect subsurface as simple indirect diffuse.
vec3 Li = mx_environment_irradiance(normal);
bsdf.response = Li * color * weight;
}
@@ -0,0 +1,29 @@
// We fake diffuse transmission by using diffuse reflection from the opposite side.
// So this BTDF is really a BRDF.
void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
// Invert normal since we're transmitting light from the other side
float NdotL = dot(L, -normal);
if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
{
return;
}
bsdf.response = color * weight * NdotL * M_PI_INV;
}
void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
// Invert normal since we're transmitting light from the other side
vec3 Li = mx_environment_irradiance(-normal);
bsdf.response = Li * color * weight;
}
@@ -0,0 +1,4 @@
void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
{
result = color;
}
@@ -0,0 +1,80 @@
<?xml version="1.0"?>
<materialx version="1.38">
<!-- <oren_nayar_diffuse_bsdf> -->
<implementation name="IM_oren_nayar_diffuse_bsdf_genglsl" nodedef="ND_oren_nayar_diffuse_bsdf" file="mx_oren_nayar_diffuse_bsdf.glsl" function="mx_oren_nayar_diffuse_bsdf" target="genglsl" />
<!-- <burley_diffuse_bsdf> -->
<implementation name="IM_burley_diffuse_bsdf_genglsl" nodedef="ND_burley_diffuse_bsdf" file="mx_burley_diffuse_bsdf.glsl" function="mx_burley_diffuse_bsdf" target="genglsl" />
<!-- <translucent_bsdf> -->
<implementation name="IM_translucent_bsdf_genglsl" nodedef="ND_translucent_bsdf" file="mx_translucent_bsdf.glsl" function="mx_translucent_bsdf" target="genglsl" />
<!-- <dielectric_bsdf> -->
<implementation name="IM_dielectric_bsdf_genglsl" nodedef="ND_dielectric_bsdf" file="mx_dielectric_bsdf.glsl" function="mx_dielectric_bsdf" target="genglsl" />
<!-- <conductor_bsdf> -->
<implementation name="IM_conductor_bsdf_genglsl" nodedef="ND_conductor_bsdf" file="mx_conductor_bsdf.glsl" function="mx_conductor_bsdf" target="genglsl" />
<!-- <generalized_schlick_bsdf> -->
<implementation name="IM_generalized_schlick_bsdf_genglsl" nodedef="ND_generalized_schlick_bsdf" file="mx_generalized_schlick_bsdf.glsl" function="mx_generalized_schlick_bsdf" target="genglsl" />
<!-- <subsurface_bsdf> -->
<implementation name="IM_subsurface_bsdf_genglsl" nodedef="ND_subsurface_bsdf" file="mx_subsurface_bsdf.glsl" function="mx_subsurface_bsdf" target="genglsl" />
<!-- <sheen_bsdf> -->
<implementation name="IM_sheen_bsdf_genglsl" nodedef="ND_sheen_bsdf" file="mx_sheen_bsdf.glsl" function="mx_sheen_bsdf" target="genglsl" />
<!-- <anisotropic_vdf> -->
<implementation name="IM_anisotropic_vdf_genglsl" nodedef="ND_anisotropic_vdf" file="mx_anisotropic_vdf.glsl" function="mx_anisotropic_vdf" target="genglsl" />
<!-- <thin_film_bsdf> -->
<implementation name="IM_thin_film_bsdf_genglsl" nodedef="ND_thin_film_bsdf" target="genglsl" />
<!-- <layer> -->
<implementation name="IM_layer_bsdf_genglsl" nodedef="ND_layer_bsdf" target="genglsl" />
<implementation name="IM_layer_vdf_genglsl" nodedef="ND_layer_vdf" target="genglsl" />
<!-- <mix> -->
<implementation name="IM_mix_bsdf_genglsl" nodedef="ND_mix_bsdf" target="genglsl" />
<implementation name="IM_mix_edf_genglsl" nodedef="ND_mix_edf" target="genglsl" />
<!-- <add> -->
<implementation name="IM_add_bsdf_genglsl" nodedef="ND_add_bsdf" target="genglsl" />
<implementation name="IM_add_edf_genglsl" nodedef="ND_add_edf" target="genglsl" />
<!-- <multiply> -->
<implementation name="IM_multiply_bsdfC_genglsl" nodedef="ND_multiply_bsdfC" target="genglsl" />
<implementation name="IM_multiply_bsdfF_genglsl" nodedef="ND_multiply_bsdfF" target="genglsl" />
<implementation name="IM_multiply_edfC_genglsl" nodedef="ND_multiply_edfC" target="genglsl" />
<implementation name="IM_multiply_edfF_genglsl" nodedef="ND_multiply_edfF" target="genglsl" />
<!-- <uniform_edf> -->
<implementation name="IM_uniform_edf_genglsl" nodedef="ND_uniform_edf" file="mx_uniform_edf.glsl" function="mx_uniform_edf" target="genglsl" />
<!-- <generalized_schlick_edf> -->
<implementation name="IM_generalized_schlick_edf_genglsl" nodedef="ND_generalized_schlick_edf" file="mx_generalized_schlick_edf.glsl" function="mx_generalized_schlick_edf" target="genglsl" />
<!-- <surface> -->
<implementation name="IM_surface_genglsl" nodedef="ND_surface" target="genglsl" />
<!-- <displacement> -->
<implementation name="IM_displacement_float_genglsl" nodedef="ND_displacement_float" file="mx_displacement_float.glsl" function="mx_displacement_float" target="genglsl" />
<implementation name="IM_displacement_vector3_genglsl" nodedef="ND_displacement_vector3" file="mx_displacement_vector3.glsl" function="mx_displacement_vector3" target="genglsl" />
<!-- <light> -->
<implementation name="IM_light_genglsl" nodedef="ND_light" target="genglsl" />
<!-- <roughness_anisotropy> -->
<implementation name="IM_roughness_anisotropy_genglsl" nodedef="ND_roughness_anisotropy" file="mx_roughness_anisotropy.glsl" function="mx_roughness_anisotropy" target="genglsl" />
<!-- <roughness_dual> -->
<implementation name="IM_roughness_dual_genglsl" nodedef="ND_roughness_dual" file="mx_roughness_dual.glsl" function="mx_roughness_dual" target="genglsl" />
<!-- <artistic_ior> -->
<implementation name="IM_artistic_ior_genglsl" nodedef="ND_artistic_ior" file="mx_artistic_ior.glsl" function="mx_artistic_ior" target="genglsl" />
<!-- <blackbody> -->
<implementation name="IM_blackbody_genglsl" nodedef="ND_blackbody" file="mx_blackbody.glsl" function="mx_blackbody" target="genglsl" />
</materialx>