Full BSDF

This commit is contained in:
2026-08-07 02:04:01 -07:00
parent 7aeebf2008
commit fe017dd874
55 changed files with 2306 additions and 1215 deletions

View File

@ -1,10 +1,30 @@
#ifndef _BRDF_GLSL_
#define _BRDF_GLSL_
#ifndef PI
#define PI 3.1415926535
#endif
#ifndef INV_PI
#define INV_PI 0.3183098861
#endif
#ifndef INV_TWO_PI
#define INV_TWO_PI 0.1591549430
#endif
#ifndef SCHLICK_A
#define SCHLICK_A -5.55473
#endif
#ifndef SCHLICK_B
#define SCHLICK_B -6.98316
#endif
#ifndef SRGB_GAMMA
#define SRGB_GAMMA 2.2
#endif
#define srgbToLinear(x) pow(x, vec3(SRGB_GAMMA))
// http://xlgames-inc.github.io/posts/improvedibl/
// http://blog.selfshadow.com/publications/s2013-shading-course/
vec3 f_schlick(const vec3 f0, const float vh) {
return f0 + (1.0 - f0) * exp2((-5.55473 * vh - 6.98316) * vh);
return f0 + (1.0 - f0) * exp2((SCHLICK_A * vh + SCHLICK_B) * vh);
}
float v_smithschlick(const float nl, const float nv, const float a) {
@ -31,7 +51,7 @@ float d_ggx(const float nh, const float a) {
float a2 = a * a;
float denom = nh * nh * (a2 - 1.0) + 1.0;
denom = max(denom * denom, 0.00006103515625 /* 2^-14 = smallest possible half float value, prevent div by zero */);
return a2 * (1.0 / 3.1415926535) / denom;
return a2 * INV_PI / denom;
}
vec3 specularBRDF(const vec3 f0, const float roughness, const float nl, const float nh, const float nv, const float vh) {
@ -44,11 +64,10 @@ vec3 specularBRDF(const vec3 f0, const float roughness, const float nl, const fl
// http://filmicworlds.com/blog/optimizing-ggx-shaders-with-dotlh/
vec3 specularBRDFb(const vec3 f0, const float roughness, const float dotNL, const float dotNH, const float dotLH) {
// D
const float pi = 3.1415926535;
float alpha = roughness * roughness;
float alphaSqr = alpha * alpha;
float denom = dotNH * dotNH * (alphaSqr - 1.0) + 1.0;
float D = alphaSqr / (pi * denom * denom);
float D = alphaSqr / (PI * denom * denom);
// F
const float F_a = 1.0;
float F_b = pow(1.0 - dotLH, 5.0);
@ -65,21 +84,8 @@ vec3 specularBRDFb(const vec3 f0, const float roughness, const float dotNL, cons
return specular / 4.0; // TODO: get rid of / 4.0
}
vec3 orenNayarDiffuseBRDF(const vec3 albedo, const float roughness, const float nv, const float nl, const float vh) {
float a = roughness * roughness;
float s = a;
float s2 = s * s;
float vl = 2.0 * vh * vh - 1.0; // Double angle identity
float Cosri = vl - nv * nl;
float C1 = 1.0 - 0.5 * s2 / (s2 + 0.33);
float test = 1.0;
if (Cosri >= 0.0) test = (1.0 / (max(nl, nv)));
float C2 = 0.45 * s2 / (s2 + 0.09) * Cosri * test;
return albedo * max(0.0, nl) * (C1 + C2) * (1.0 + roughness * 0.5);
}
vec3 lambertDiffuseBRDF(const vec3 albedo, const float nl) {
return albedo * (1.0 / 3.1415926535) * nl;
return albedo * INV_PI * nl;
}
vec3 surfaceAlbedo(const vec3 baseColor, const float metalness) {
@ -95,24 +101,6 @@ float getMipFromRoughness(const float roughness, const float numMipmaps) {
return roughness * numMipmaps;
}
float wardSpecular(vec3 N, vec3 H, float dotNL, float dotNV, float dotNH, vec3 fiberDirection, float shinyParallel, float shinyPerpendicular) {
if(dotNL < 0.0 || dotNV < 0.0) {
return 0.0;
}
// fiberDirection - parse from rotation
// shinyParallel - roughness
// shinyPerpendicular - anisotropy
vec3 fiberParallel = normalize(fiberDirection);
vec3 fiberPerpendicular = normalize(cross(N, fiberDirection));
float dotXH = dot(fiberParallel, H);
float dotYH = dot(fiberPerpendicular, H);
const float PI = 3.1415926535;
float coeff = sqrt(dotNL/dotNV) / (4.0 * PI * shinyParallel * shinyPerpendicular);
float theta = (pow(dotXH/shinyParallel, 2.0) + pow(dotYH/shinyPerpendicular, 2.0)) / (1.0 + dotNH);
return clamp(coeff * exp(-2.0 * theta), 0.0, 1.0);
}
// https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile
// vec3 EnvBRDFApprox(vec3 SpecularColor, float Roughness, float NoV) {
// const vec4 c0 = { -1, -0.0275, -0.572, 0.022 };
@ -139,38 +127,39 @@ float D_Approx(const float Roughness, const float RoL) {
}
#ifdef _ClearCoat
float brdf_coatF0;
vec3 clearcoatBRDF(const float clearcoat, const float clearcoat_rough,
const float coat_ior,
const float dotNL, const float dotNH, const float dotNV, const float dotVH) {
const float coat_ior, const vec3 coatN, const vec3 l, const vec3 v, const vec3 h) {
if (clearcoat <= 0.0) return vec3(0.0);
float cdotNL = max(0.0, dot(coatN, l));
float cdotNH = max(0.0, dot(coatN, h));
float cdotNV = max(0.0, dot(coatN, v));
float cdotVH = max(0.0, dot(v, h));
float a = clearcoat_rough * clearcoat_rough;
// F0 from Fresnel equation for dielectric
float ccF0 = (coat_ior - 1.0) / (coat_ior + 1.0);
ccF0 = ccF0 * ccF0;
float F = ccF0 + (1.0 - ccF0) * pow(1.0 - dotVH, 5.0);
float D = d_ggx(dotNH, a);
float G = g2_approx(dotNL, dotNV, a);
return vec3(clearcoat * D * G * F / max(4.0 * dotNV, 1e-5));
float F = brdf_coatF0 + (1.0 - brdf_coatF0) * exp2((SCHLICK_A * cdotVH + SCHLICK_B) * cdotVH);
float D = d_ggx(cdotNH, a);
float G = g2_approx(cdotNL, cdotNV, a);
return vec3(clearcoat * D * G * F / max(4.0 * cdotNV, 1e-5));
}
float coatAttenuation(const float clearcoat,
const float coat_ior, const float dotNV) {
const float coat_ior, const vec3 coatN, const vec3 v) {
if (clearcoat <= 0.0) return 1.0;
float ccF0 = (coat_ior - 1.0) / (coat_ior + 1.0);
ccF0 = ccF0 * ccF0;
// Schlick with pow(.,5) - cheaper than exp2
float F = ccF0 + (1.0 - ccF0) * pow(1.0 - dotNV, 5.0);
float cdotNV = max(0.0, dot(coatN, v));
float F = brdf_coatF0 + (1.0 - brdf_coatF0) * exp2((SCHLICK_A * cdotNV + SCHLICK_B) * cdotNV);
return max(1.0 - F * clearcoat, 0.0);
}
vec3 coatTintAttenuation(const float clearcoat, const vec3 coat_tint, const float dotNV) {
vec3 coatTintAttenuation(const float clearcoat, const vec3 coat_tint, const vec3 coatN, const vec3 v) {
if (clearcoat <= 0.0) return vec3(1.0);
float absorption = 1.0 / max(dotNV, 0.3);
return mix(vec3(1.0), coat_tint, clamp(absorption * 0.2, 0.0, 1.0));
float cdotNV = max(0.0, dot(coatN, v));
float absorption = 1.0 / max(cdotNV, 0.3);
return mix(vec3(1.0), clamp(coat_tint, 0.0, 1.0), clamp(absorption * 0.2, 0.0, 1.0));
}
#endif
#ifdef _Sheen
float brdf_sheenAlbedo;
// based on Blender sheen model/Frostbite PBR
vec3 sheenBRDF(const float sheen, const float sheen_rough,
const vec3 sheen_tint, const float dotNL, const float dotNH, const float dotNV) {
@ -179,18 +168,16 @@ vec3 sheenBRDF(const float sheen, const float sheen_rough,
float a = rough * rough;
float sinNH2 = 1.0 - dotNH * dotNH;
float a2 = a * a;
float D = (2.0 + a2) * sinNH2 / (2.0 * 3.1415926535 * pow(1.0 + a2 * sinNH2, 2.0));
float denom = 1.0 + a2 * sinNH2;
float D = (2.0 + a2) * sinNH2 * INV_TWO_PI / (denom * denom);
float V = 1.0 / (4.0 * dotNL * dotNV + 1e-5);
float sheenAlbedo = (1.0 - 0.5 * rough) * mix(1.0, dotNV, 0.5);
return sheen_tint * sheen * D * V * dotNL * sheenAlbedo;
return sheen_tint * sheen * D * V * dotNL * brdf_sheenAlbedo;
}
float sheenAttenuation(const float sheen, const float sheen_rough,
const vec3 sheen_tint, const float dotNV) {
if (sheen <= 0.0) return 1.0;
float rough = clamp(sheen_rough, 1e-3, 1.0);
float sheenAlbedo = (1.0 - 0.5 * rough) * mix(1.0, dotNV, 0.5);
float maxComp = sheen * max(max(sheen_tint.r, sheen_tint.g), sheen_tint.b) * sheenAlbedo;
float maxComp = sheen * max(max(sheen_tint.r, sheen_tint.g), sheen_tint.b) * brdf_sheenAlbedo;
return max(1.0 - maxComp, 0.0);
}
#endif
@ -202,7 +189,7 @@ vec3 anisotropicBRDF(const vec3 f0, const float roughness, const float anisotrop
const vec3 n, const vec3 l, const vec3 v,
const float dotNL, const float dotNV) {
if (abs(anisotropy) <= 0.001) return vec3(0.0);
float rot = aniso_rot * 3.1415926535 * 2.0;
float rot = aniso_rot * PI * 2.0;
float cr = cos(rot);
float sr = sin(rot);
vec3 t = normalize(tangent * cr + bitangent * sr);
@ -221,7 +208,7 @@ vec3 anisotropicBRDF(const vec3 f0, const float roughness, const float anisotrop
float dotTL = dot(t, l);
float dotBL = dot(b, l);
float denom = max(dotTH * dotTH / at2 + dotBH * dotBH / ab2, 1e-7);
float D = 1.0 / (3.1415926535 * at * ab * denom * denom);
float D = INV_PI / (at * ab * denom * denom);
float V = 1.0 / max(dotNL * (dotTL / at + dotBL / ab) * (dotTV / at + dotBV / ab), 1e-5);
float dotVH = max(dot(v, h), 0.0);
vec3 F = f_schlick(f0, dotVH);
@ -229,46 +216,117 @@ vec3 anisotropicBRDF(const vec3 f0, const float roughness, const float anisotrop
}
#endif
#ifdef _Subsurface
// Blenders bssrdf_burley implementation
vec3 subsurfaceBRDF(const vec3 albedo, const vec3 sss_color,
const vec3 sss_radius, const float subsurface, const float sss_anisotropy,
const float dotNL) {
if (subsurface <= 0.0) return vec3(0.0);
vec3 mfp = sss_radius * (0.25 / 3.1415926535);
vec3 A = clamp(albedo, 0.0, 1.0);
vec3 d = 1.9 - A + 3.5 * (A - 0.8) * (A - 0.8);
d = mfp / max(d, 1e-5);
float aniso = clamp(sss_anisotropy, 0.0, 0.9);
float scatter = subsurface * (1.0 / 3.1415926535);
vec3 sssDiffuse = sss_color * scatter * dotNL;
float backScatter = max(0.0, 1.0 - dotNL) * (1.0 - aniso) * 0.5;
vec3 sssBack = sss_color * subsurface * backScatter;
float dist = max(0.0, 1.0 - dotNL);
vec3 rcp_d = 1.0 / max(d, vec3(1e-5));
vec3 x = vec3(dist) * rcp_d;
vec3 extinction = 1.0 / (1.0 + x + 0.5 * x * x);
return sssDiffuse * extinction + sssBack * (vec3(1.0) - extinction);
}
#endif
#ifdef _Transmission
float brdf_transmissionF0;
// Blenders microfacet glass/refraction model
vec3 transmissionBRDF(const vec3 albedo, const float transmission,
const float trans_rough, const float ior, const float thin_wall,
const float dotNL, const float dotNV, const float dotVH) {
if (transmission <= 0.0) return vec3(0.0);
float F0 = (ior - 1.0) / (ior + 1.0);
F0 = F0 * F0;
float F = F0 + (1.0 - F0) * pow(1.0 - dotVH, 5.0);
float F = brdf_transmissionF0 + (1.0 - brdf_transmissionF0) * exp2((SCHLICK_A * dotVH + SCHLICK_B) * dotVH);
float transmittance = 1.0 - F;
if (thin_wall > 0.5) {
return albedo * transmission * transmittance * dotNL;
}
float a = trans_rough * trans_rough;
float rough_atten = mix(1.0, 1.0 / max(dotNV, 0.1), a);
float rough_atten = min(mix(1.0, 1.0 / max(dotNV, 0.1), a), 4.0);
return albedo * transmission * transmittance * rough_atten * dotNL;
}
#endif
#ifdef _ExtBRDF
float brdf_sheenWeight = 1.0;
float brdf_coatWeight = 1.0;
vec3 brdf_coatTintAbsorb = vec3(1.0);
vec3 applyExtBRDFLayers(
const vec3 direct,
const vec3 albedo,
const vec3 f0,
const float roughness,
const float dotNL, const float dotNV, const float dotNH, const float dotVH,
const vec3 n, const vec3 l, const vec3 v, const vec3 h,
#ifdef _ClearCoat
const float clearcoat, const float clearcoatRough, const float coatIOR,
const vec3 coatTint, const vec3 coatN,
#endif
#ifdef _Sheen
const float sheen, const float sheenRough, const vec3 sheenTint,
#endif
#ifdef _Transmission
const float transmission, const float transRough, const float ior, const float thinWall,
#endif
out float layerWeight
) {
float sheenWeight = brdf_sheenWeight;
float coatWeight = brdf_coatWeight;
#ifdef _Sheen
vec3 sheenContrib = sheenBRDF(sheen, sheenRough, sheenTint, dotNL, dotNH, dotNV);
#endif
#ifdef _ClearCoat
vec3 coatContrib = clearcoatBRDF(clearcoat, clearcoatRough, coatIOR, coatN, l, v, h);
#endif
layerWeight = sheenWeight * coatWeight;
vec3 result = direct * layerWeight;
#ifdef _Transmission
result += transmissionBRDF(albedo, transmission, transRough, ior, thinWall, dotNL, dotNV, dotVH) * layerWeight;
#endif
#ifdef _ClearCoat
result *= brdf_coatTintAbsorb;
result += coatContrib * sheenWeight;
#endif
#ifdef _Sheen
result += sheenContrib;
#endif
return result;
}
#endif
#ifdef _ClearCoat
float coatIBLFresnel(const float clearcoat, const float coat_ior,
const float dotNV_coat) {
if (clearcoat <= 0.0) return 0.0;
float F = brdf_coatF0 + (1.0 - brdf_coatF0) * exp2((SCHLICK_A * dotNV_coat + SCHLICK_B) * dotNV_coat);
return F * clearcoat;
}
#endif
#ifdef _Sheen
float sheenIBLAlbedo(const float sheen, const float sheen_rough,
const float dotNV) {
if (sheen <= 0.0) return 0.0;
float rough = clamp(sheen_rough, 1e-3, 1.0);
return sheen * (1.0 - 0.5 * rough) * mix(1.0, dotNV, 0.5);
}
#endif
#ifdef _Anisotropy
vec3 anisotropicIBLDirection(const vec3 n, const vec3 v, const vec3 tangent,
const float anisotropy, const float roughness) {
if (abs(anisotropy) <= 0.001 || dot(tangent, tangent) < 0.001)
return reflect(-v, n);
vec3 bitangent = normalize(cross(n, tangent));
vec3 r = reflect(-v, n);
float aniso_abs = abs(anisotropy);
vec3 stretchDir = anisotropy > 0.0 ? tangent : bitangent;
float stretchAmt = aniso_abs * roughness;
return normalize(r + stretchDir * stretchAmt * dot(r, stretchDir) * 0.5);
}
#endif
#ifdef _Transmission
float transmissionIBLFresnel(const float ior, const float dotNV) {
return brdf_transmissionF0 + (1.0 - brdf_transmissionF0) * exp2((SCHLICK_A * dotNV + SCHLICK_B) * dotNV);
}
vec3 transmissionIBLDirection(const vec3 n, const vec3 v, const float ior) {
float eta = 1.0 / ior;
vec3 refrDir = refract(-v, n, eta);
if (dot(refrDir, refrDir) < 0.001) {
refrDir = reflect(-v, n);
}
return refrDir;
}
#endif
#endif