Ashikhmin-Shirley Anisotropic BRDF in GLSL

This article details the mathematical formulation of the Ashikhmin-Shirley anisotropic specular Bidirectional Reflectance Distribution Function (BRDF) and demonstrates how to implement it directly in OpenGL Shading Language (GLSL). Designed to simulate anisotropic surfaces such as brushed metal, woven fabrics, and hair, this microfacet-based model provides realistic specular highlights shaped by distinct directional roughness parameters.

Mathematical Formulation

The Ashikhmin-Shirley model defines the specular component of a BRDF using surface normal \(\mathbf{n}\), tangent \(\mathbf{u}\), bitangent \(\mathbf{v}\), incoming light direction \(\mathbf{l}\), and outgoing view direction \(\mathbf{v}_o\).

Given the half-vector \(\mathbf{h} = \frac{\mathbf{l} + \mathbf{v}_o}{\|\mathbf{l} + \mathbf{v}_o\|}\), the specular BRDF term \(\rho_s(\mathbf{l}, \mathbf{v}_o)\) is defined as:

\[\rho_s(\mathbf{l}, \mathbf{v}_o) = \frac{\sqrt{(n_u + 1)(n_v + 1)}}{8\pi} \cdot \frac{(\mathbf{n} \cdot \mathbf{h})^{\frac{n_u (\mathbf{h} \cdot \mathbf{u})^2 + n_v (\mathbf{h} \cdot \mathbf{v})^2}{1 - (\mathbf{h} \cdot \mathbf{n})^2}}}{(\mathbf{h} \cdot \mathbf{l}) \max(\mathbf{n} \cdot \mathbf{l}, \mathbf{n} \cdot \mathbf{v}_o)} \cdot F(\mathbf{h} \cdot \mathbf{l})\]

Components:

  • Specular Exponents (\(n_u, n_v\)): Control surface roughness along the tangent \(\mathbf{u}\) and bitangent \(\mathbf{v}\) directions.
  • Normalization Factor: \(\frac{\sqrt{(n_u + 1)(n_v + 1)}}{8\pi}\) ensures the BRDF conserves energy.
  • Anisotropic Exponent: \(\frac{n_u (\mathbf{h} \cdot \mathbf{u})^2 + n_v (\mathbf{h} \cdot \mathbf{v})^2}{1 - (\mathbf{h} \cdot \mathbf{n})^2}\) stretches the highlight along the tangent axes. When \(\mathbf{h} = \mathbf{n}\), \(1 - (\mathbf{h} \cdot \mathbf{n})^2 = 0\); taking the limit resolves this singularity to zero.
  • Denominator: Attenuates the reflection based on the geometric interaction of view and light angles, avoiding standard microfacet shadowing approximations.
  • Fresnel Term \(F(\mathbf{h} \cdot \mathbf{l})\): Evaluated using Schlick's approximation: \[F(\mathbf{h} \cdot \mathbf{l}) = R_0 + (1 - R_0)(1 - (\mathbf{h} \cdot \mathbf{l}))^5\] where \(R_0\) is the specular reflectance at normal incidence.

GLSL Implementation

The following GLSL function computes the anisotropic specular reflection. It assumes input vectors are normalized and incorporates numerical safeguards against division by zero.

#define PI 3.14159265359

vec3 ashikhminShirleySpecular(
    vec3 N,            // Surface normal
    vec3 T,            // Tangent
    vec3 B,            // Bitangent
    vec3 V,            // View direction (pointing toward camera)
    vec3 L,            // Light direction (pointing toward light)
    float nu,          // Specular exponent along tangent T
    float nv,          // Specular exponent along bitangent B
    vec3 R0            // Specular reflectance at normal incidence
) {
    vec3 H = normalize(L + V);

    float NdotL = max(dot(N, L), 0.0);
    float NdotV = max(dot(N, V), 0.0);
    float NdotH = max(dot(N, H), 0.0);
    float HdotL = max(dot(H, L), 0.0);
    float HdotT = dot(H, T);
    float HdotB = dot(H, B);

    // Prevent evaluation below horizon
    if (NdotL <= 0.0 || NdotV <= 0.0 || HdotL <= 0.0) {
        return vec3(0.0);
    }

    // Normalization constant
    float normalization = sqrt((nu + 1.0) * (nv + 1.0)) / (8.0 * PI);

    // Anisotropic exponent computation
    float denominatorExp = 1.0 - (NdotH * NdotH);
    float exponent = 0.0;
    if (denominatorExp > 1e-5) {
        exponent = (nu * HdotT * HdotT + nv * HdotB * HdotB) / denominatorExp;
    }

    // Specular distribution term
    float specularDist = pow(NdotH, exponent);

    // Denominator with geometric attenuation factor
    float denom = HdotL * max(NdotL, NdotV);

    // Schlick's Fresnel approximation
    vec3 F = R0 + (vec3(1.0) - R0) * pow(1.0 - HdotL, 5.0);

    // Final specular BRDF
    return normalization * (specularDist / max(denom, 1e-5)) * F;
}

Integration into the Rendering Equation

To evaluate the reflected radiance \(L_o\), multiply the resulting BRDF by the incoming light intensity \(L_i\) and the cosine attenuation factor \((\mathbf{n} \cdot \mathbf{l})\):

vec3 specularLight = ashikhminShirleySpecular(N, T, B, V, L, nu, nv, R0) * lightColor * NdotL;