- Jim Eckerlein, Norbert Nopper, UX3D
Suggestion
Written against the glTF 2.0 spec.
This extension defines the anisotropic property of a material as observable with brushed metals for instance. An asymetric specular lobe model is introduced to allow for such phenomena. The visually distinct feature of that lobe is the elongated appearance of the specular reflection. For a single punctual light source, the specular reflection will eventually degenerate into a zero width line in the limit, that is where the material is fully anisotropic, as opposed to be fully isotropic in which case the specular reflecation is radially symmetric.
Sample values:
{
"materials": [
{
"extensions": {
"KHR_materials_anisotropy": {
"anisotropy": 0.6,
"anisotropyDirection": [0.0, 1.0, 0.0]
}
}
}
]
}
Type | Description | Required | |
---|---|---|---|
anisotropy | number |
The anisotropy. | No, default: 0.0 |
anisotropyTexture | textureInfo |
The anisotropy texture. | No |
anisotropyDirection | array |
The anisotropy direction. | No, default: [1.0, 0.0, 0.0] |
anisotropyDirectionTexture | textureInfo |
The anisotropy direction texture. | No |
Two new material properties are introduced: an explicit anisotropy parameter and the direction in which the specular reflection elongates relative to the surface tangents.
The anisotropy parameter is a dimensionaless number in [-1, 1]
and forms an injective relation to the roughness distribution along two orthogonal directions, one of which is the direction parameter and the other the result of crossing the direction and the geometric normal.
An anisotropy of 1
means that the specular reflection will elongate along the given direction,
while a value of -1
will elongate it along the computed orthogonal direction.
0.5 | 0.0 | -0.5 | |
---|---|---|---|
[1.0, 0.0, 0.0] |
|||
[1.0, 1.0, 0.0] |
To achieve certain surface finishes, it is possible to define the anisotropy and its direction using a texture.
Anisotropy | Direction |
---|---|
While uniform and textured anisotropy are multiplied, uniform and textured direction defintions are mutual exclusive and the latter overrides the former. (While it is surely possible to add both directions in terms of their complex argument, the computional overhead may not justify the additional editorial convenience.)
float anisotropy = u_Anisotropy;
#if HAS_ANISOTROPY_MAP
anisotropy *= texture(uv, u_AnisotropySampler).r * 2.0 - 1.0;
#endif
#if HAS_ANISOTROPY_DIRECTION_MAP
vec3 direction = texture(uv, u_AnisotropyDirectionSampler).xyz * 2.0 - vec3(1.0);
#else
vec3 direction = u_AnisotropyDirection;
#endif
vec3 anisotropicT = normalize(TBN * direction);
vec3 anisotropicB = normalize(cross(normal_geometric, anisotropicT));
float TdotL = dot(anisotropicT, l);
float BdotL = dot(anisotropicB, l);
float TdotH = dot(anisotropicT, h);
float BdotH = dot(anisotropicB, h);
f_specular += intensity * NdotL * BRDF_specularAnisotropicGGX(f0, f90, alphaRoughness,
VdotH, NdotL, NdotV, NdotH,
BdotV, TdotV, TdotL, BdotL, TdotH, BdotH, anisotropy);
The anisotropic GGX is defined as follows:
vec3 BRDF_specularAnisotropicGGX(vec3 f0, vec3 f90, float alphaRoughness,
float VdotH, float NdotL, float NdotV, float NdotH, float BdotV, float TdotV,
float TdotL, float BdotL, float TdotH, float BdotH, float anisotropy)
{
float at = max(alphaRoughness * (1.0 + anisotropy), 0.00001);
float ab = max(alphaRoughness * (1.0 - anisotropy), 0.00001);
vec3 F = F_Schlick(f0, f90, VdotH);
float V = V_GGX_anisotropic(NdotL, NdotV, BdotV, TdotV, TdotL, BdotL, anisotropy, at, ab);
float D = D_GGX_anisotropic(NdotH, TdotH, BdotH, anisotropy, at, ab);
return F * V * D;
}
float D_GGX_anisotropic(float NdotH, float TdotH, float BdotH, float anisotropy, float at, float ab)
{
float a2 = at * ab;
vec3 f = vec3(ab * TdotH, at * BdotH, a2 * NdotH);
float w2 = a2 / dot(f, f);
return a2 * w2 * w2 / M_PI;
}
float V_GGX_anisotropic(float NdotL, float NdotV, float BdotV, float TdotV, float TdotL, float BdotL,
float anisotropy, float at, float ab)
{
float GGXV = NdotL * length(vec3(at * TdotV, ab * BdotV, NdotV));
float GGXL = NdotV * length(vec3(at * TdotL, ab * BdotL, NdotL));
float v = 0.5 / (GGXV + GGXL);
return clamp(v, 0.0, 1.0);
}
The parametrization of at
and ab
, denoting roughness values along both anisotropic directions respectively, is taken from [4].