Full BSDF
This commit is contained in:
@ -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
|
||||
|
||||
Reference in New Issue
Block a user