float D_GGX_Anisotropic(float at, float ab, float ToH, float BoH, float NoH) { // Burley 2012, "Physically-Based Shading at Disney" float a2 = at * ab; vec3 d = vec3(ab * ToH, at * BoH, a2 * NoH); return saturateMediump(a2 * sq(a2 / dot(d, d)) * (1.0 / PI)); } float V_SmithGGXCorrelated_Anisotropic(float at, float ab, float ToV, float BoV, float ToL, float BoL, float NoV, float NoL) { // Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs" // TODO: lambdaV can be pre-computed for all the lights, it should be moved out of this function float lambdaV = NoL * length(vec3(at * ToV, ab * BoV, NoL)); float lambdaL = NoV * length(vec3(at * ToL, ab * BoL, NoV)); float v = 0.5 / (lambdaV + lambdaL); return saturateMediump(v); } vec3 F_Schlick(const vec3 f0, float f90, float VoH) { // Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering" float f = pow5(1.0 - VoH); return f + f0 * (f90 - f); } float distributionAnisotropic(float at, float ab, float ToH, float BoH, float NoH) { return D_GGX_Anisotropic(at, ab, ToH, BoH, NoH); } float visibilityAnisotropic(float linearRoughness, float at, float ab, float ToV, float BoV, float ToL, float BoL, float NoV, float NoL) { return V_SmithGGXCorrelated_Anisotropic(at, ab, ToV, BoV, ToL, BoL, NoV, NoL); } vec3 fresnel(const vec3 f0, float LoH) { return F_Schlick(f0, 1.0, LoH); } vec3 anisotropicLobe(const PixelParams pixel, const Light light, const vec3 h, float NoV, float NoL, float NoH, float LoH) { vec3 l = light.l; vec3 t = pixel.anisotropicT; vec3 b = pixel.anisotropicB; vec3 v = shading_view; float ToV = dot(t, v); float BoV = dot(b, v); float ToL = dot(t, l); float BoL = dot(b, l); float ToH = dot(t, h); float BoH = dot(b, h); // Anisotropic parameters: at and ab are the roughness along the tangent and bitangent // to simplify materials, we derive them from a single roughness parameter // Kulla 2017, "Revisiting Physically Based Shading at Imageworks" float at = max(pixel.linearRoughness * (1.0 + pixel.anisotropy), MIN_LINEAR_ROUGHNESS); float ab = max(pixel.linearRoughness * (1.0 - pixel.anisotropy), MIN_LINEAR_ROUGHNESS); // specular anisotropic BRDF float D = distributionAnisotropic(at, ab, ToH, BoH, NoH); float V = visibilityAnisotropic(pixel.linearRoughness, at, ab, ToV, BoV, ToL, BoL, NoV, NoL); vec3 F = fresnel(pixel.f0, LoH); return (D * V) * F; }