Ajout du projet Depths sur Git
This commit is contained in:
+69
@@ -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;
|
||||
}
|
||||
+11
@@ -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);
|
||||
}
|
||||
+30
@@ -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;
|
||||
}
|
||||
+10
@@ -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);
|
||||
}
|
||||
+78
@@ -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;
|
||||
}
|
||||
+77
@@ -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);
|
||||
}
|
||||
+84
@@ -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);
|
||||
}
|
||||
+92
@@ -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);
|
||||
}
|
||||
+606
@@ -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);
|
||||
}
|
||||
Vendored
+23
@@ -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));
|
||||
}
|
||||
+6
@@ -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;
|
||||
}
|
||||
+14
@@ -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;
|
||||
}
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
void mx_add_edf(vec3 N, vec3 L, EDF in1, EDF in2, out EDF result)
|
||||
{
|
||||
result = in1 + in2;
|
||||
}
|
||||
+4
@@ -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.
|
||||
}
|
||||
Vendored
+17
@@ -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);
|
||||
}
|
||||
Vendored
+48
@@ -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));
|
||||
}
|
||||
+36
@@ -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;
|
||||
}
|
||||
+70
@@ -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;
|
||||
}
|
||||
+116
@@ -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;
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
void mx_displacement_float(float disp, float scale, out displacementshader result)
|
||||
{
|
||||
result.offset = vec3(disp);
|
||||
result.scale = scale;
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
void mx_displacement_vector3(vec3 disp, float scale, out displacementshader result)
|
||||
{
|
||||
result.offset = disp;
|
||||
result.scale = scale;
|
||||
}
|
||||
+119
@@ -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;
|
||||
}
|
||||
+9
@@ -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;
|
||||
}
|
||||
+36
@@ -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;
|
||||
}
|
||||
+15
@@ -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;
|
||||
}
|
||||
}
|
||||
+9
@@ -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);
|
||||
}
|
||||
Vendored
+43
@@ -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;
|
||||
}
|
||||
+34
@@ -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;
|
||||
}
|
||||
+29
@@ -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;
|
||||
}
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
|
||||
{
|
||||
result = color;
|
||||
}
|
||||
+80
@@ -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>
|
||||
Reference in New Issue
Block a user