Anisotropic Reflections and Specular Highlights are widely used in visual effects and animations to simulate surfaces with directional reflectivity like brushed metals or holograms. There are some algorithms for calculating anisotropic distribution models such as Ward or Heidrich–Seide models.The limitation with Renderman shaders is that they need a parametric surface such as NURBS or Subdivisions to calculate the anisotropy of surfaces. Here I have tried to find a solution to use anisotropic shaders for polygon surfaces.
|
Definition |
||
|
Most of surfaces in real world are not perfect circles and they have a degree of anisotropy; the reflectivity of the surface varies in different directions. Greg Ward Larson defines the anisotropic specular highlights as:
where X and Y are two perpendicular tangent vectors and αx and αy are the deviation of surface in X and Y directions respectively or simply the roughness of surface on those directions. So if we find a pair of tangent vectors, we can calculate the specular highlights. In NURBS surfaces dPdu and dPdv are suitable to be used as tangent vectors, because they are the direction of U and V respectively. But what if our surface does not have defined U and V directions, like polygon surfaces? |
|
|
|
|
Now what if we need to rotate the specular highlights? In order to find new U
and V directions, we multiply the U and V vectors (which are
of course normalized vectors with unit lengths) by cosine and sine of and arbitrary
angle and then add these two vectors. One of benefits of this feature is that the
angle could vary from point to point, i.e. a texture map could be used to
manipulate the rotation of the specular highlights.
There is a small glitch in the shader: when the normal are parallel to the up vector,
the cross product becomes zero; therefore no tangent vector could be defined. This
problem leads to artifacts, for instance on a simple poly plane. To solve this problem
I made I made an exception for this case and defined the tangent vector exactly toward
X direction of the object.
The Shader Code:
color wardAniso(normal N;
vector V;
vector xdir;
float xroughness, yroughness;)
{
float sqr (float x) { return x * x; }
float xrough = xroughness, yrough = yroughness;
if (xroughness < 0.0001) { xrough = 0.0001;}
if (yroughness < 0.0001) { yrough = 0.0001;}
float cos_theta_r = clamp(N . V, 0.0001, 1);
vector X = xdir / xrough;
vector Y = (N ^ xdir) / yrough;
color C = 0;
extern point P;
illuminance(P, N, PI/2)
{
extern vector L;
extern color Cl;
float nonspec = 0;
lightsource("__nonspecular", nonspec);
if (nonspec < 1)
{
vector LN = normalize(L);
float cos_theta_i = LN . N;
if (cos_theta_i > 0.0)
{
vector H = normalize(V + LN);
float rho = exp (-2 * (sqr(X . H) + sqr(Y . H)) / (1 + H . N))
/ sqrt (cos_theta_i * cos_theta_r);
C += Cl * ((1 - nonspec) * cos_theta_i * rho);
}
}
}
return C / (4 * xrough * yrough);
}
/////////////////////////////////////////////////////////////////////////////////////////////
include "/home/aseiff20/maya/projects/RMS_slim/wardAniso.h"
surface ward( color surfColor = color(0.5,0.5,0.5);
color specColor = 1;
float Ka = 0.2, Ks = 0.5;
float angle = 360;
string angleMap = "/home/aseiff20/mount/stuhome/vsfx755/cutter/man.tex";
float xrough = 1;
string xroughMap = "";
float yrough = 0.5;
string yroughMap = "";)
{
normal Nf = faceforward(normalize(N), I);
vector V = -normalize(I);
vector upvec = vector "object" (0, 1, 0);
vector xdir;
float xroughness = xrough;
float yroughness = yrough;
if (normalize(N).upvec > 0.999)
{xdir = vector "object" (1, 0, 0);}
else
{xdir = normalize(upvec ^ N);}
vector ydir = normalize(N ^ xdir);
float ang = angle;
if (angleMap != "")
{
float r = texture(angleMap[0]);
float g = texture(angleMap[1]);
float b = texture(angleMap[2]);
ang = (r + g + b) / 3;
}
if (xroughMap != "")
{
float r = texture(xroughMap[0]);
float g = texture(xroughMap[1]);
float b = texture(xroughMap[2]);
xroughness = (r + g + b) / 3;
}
if (yroughMap != "")
{
float r = texture(yroughMap[0]);
float g = texture(yroughMap[1]);
float b = texture(yroughMap[2]);
yroughness = (r + g + b) / 3;
}
vector direction = normalize (xdir * cos(radians(ang)) + ydir * sin(radians(ang)));
Oi = Os;
Ci = Cs * ((Ka * ambient()) + (diffuse(Nf) * surfColor) + (Ks * specColor * wardAniso(Nf, V, direction, xroughness, yroughness)));
Ci *= Oi;
}
|
