/***************************************************************
s_fancysurf.sl - fancy surface shader
DESCRIPTION:
A fancy surface shader that incorporates the
Oren-Nayar diffuse lighting model with rim lighting
and translucency. Also has one color texture and one bump texture.
Nothing really new here, but it puts it all in one place for
convenience.
Oren-Nayar diffuse model originally written by Larry Gritz.
I just took his shader and made a function out of it.
The rim lighting model was found through experimentation. I'm sure
I could explain it if I really thought about it, but for now
I just use it cause it seems to work. Has problems if you try
shadowing the rim light.
PARAMETERS:
Ka ambient contribution
Kd diffuse contribution
Ks specular contribution
Krim rimlight contribution
Ktranslucent translucent backlight contribution
specularcolor color of specular highlight
diffuserough roughness parameter for Oren-Nayar diffuse
specularrough roughness parameter for specular() function
rimscatter controls rim "wraparound"
0 for small rim, 1 for whole surface
colormap name of color texture map
bumpmap name of greyscale bump map
bumpdepth amplitude of bump
bumpmidval bias value for bumpmap values
s_offset texturemap s offset
t_offset texturemap t offset
s_scale texturemap s scaling factor
t_scale texturemap t scaling factor
AUTHOR: Mike King
REVISION HISTORY:
11/09/1999: Removed conditional compilation that worked around
an old BMRT bug. Added clamping to the alpha and
beta angles to avoid instability around 90 degree
grazing angles. This is not a correct solution, but
it should at least avoid bright speckles most of
the time.
***************************************************/
#define thetaMin ((-PI/2)+.01)
#define thetaMax ((PI/2)-.01)
/* grazing specular function controls rim lighting from
lights that face towards the camera. backscatter controls
the size and intensity of the rim lighting. */
color grazing_specular(normal Nn; vector V; uniform float backscatter;)
{
extern point P;
extern vector L;
extern color Cs;
extern color Cl;
color lightC = 0;
uniform color black = 0;
float blendval;
float ldotn;
float idotn;
vector Ln;
vector In = -V;
vector NN = normalize(Nn);
/* roughfactor controls brightness of rimlight */
uniform float roughfactor;
/* scatter controls how big an area the rimlight covers */
uniform float scatter;
uniform float rimlight;
/* these values were found empirically. can adjust to get
get different results. */
roughfactor = 0.5-0.5*backscatter;
scatter = (1-backscatter) * 0.1;
illuminance(P,Nn,PI) {
if( lightsource("__rimlight",rimlight) == 0)
{
rimlight = 0;
}
if(rimlight>0)
{
Ln = normalize(L);
/* idotn tells us angle between normal and view direction */
idotn = In.Nn;
/* ldotn tells us angle between normal and light direction */
ldotn = Ln.Nn;
/* put both dot products in range [0,1] and multiply. */
blendval = ((ldotn+1)/2) * ((idotn+1)/2);
/* only want to use values in [0, 0.5] to cause rim lighting.
changing the range will alter the effect */
blendval = smoothstep(scatter,roughfactor,blendval);
lightC += mix(black,Cl,blendval);
}
}
return lightC;
}
/* orennayart diffuse function contains code originally
written by Larry Gritz. I just added the check for
non diffuse lights */
color orennayar_diffuse( normal Nn; vector V; float sigma;)
{
extern point P;
extern vector L;
extern color Cs;
extern color Cl;
vector LN;
color lightC = 0;
color L1, L2;
float C1, C2, C3;
float theta_r, theta_i, cos_theta_i;
float alpha, beta, sigma2, cos_phi_diff;
theta_r = acos( V . Nn);
sigma2 = sigma * sigma;
uniform float nondiffuse = 0;
illuminance (P, Nn, PI/2) {
if( lightsource("__nondiffuse",nondiffuse) == 0)
{
nondiffuse = 0;
}
if(nondiffuse==0)
{
LN = normalize(L);
cos_theta_i = LN . Nn;
cos_phi_diff = normalize(V-Nn*(V.Nn)) . normalize(LN - Nn*(LN.Nn));
theta_i = acos (cos_theta_i);
alpha = max (theta_i, theta_r);
beta = min (theta_i, theta_r);
alpha = clamp(alpha, thetaMin, thetaMax);
beta = clamp (beta, thetaMin, thetaMax);
C1 = 1 - 0.5 * sigma2/(sigma2+0.33);
C2 = 0.45 * sigma2 / (sigma2 + 0.09);
if (cos_phi_diff >= 0)
C2 *= sin(alpha);
else C2 *= (sin(alpha) - pow(2*beta/PI,3));
C3 = 0.125 * sigma2 / (sigma2+0.09) * pow ((4*alpha*beta)/(PI*PI),2);
L1 = Cs * (cos_theta_i * (C1 + cos_phi_diff * C2 * tan(beta) +
(1 - abs(cos_phi_diff)) * C3 * tan((alpha+beta)/2)));
L2 = (Cs * Cs) * (0.17 * cos_theta_i * sigma2/(sigma2+0.13) *
(1 - cos_phi_diff*(4*beta*beta)/(PI*PI)));
lightC += (L1 + L2) * Cl;
}
}
return lightC;
}
surface
MKfancysurf (float Ka = 1;
float Kd = .6;
float Ks = .4;
float Krim = 1;
float Ktranslucent = 0;
color specularcolor = 1;
float diffuserough = 0.0;
float specularrough = .2;
float rimscatter = 0.5;
string colormap="";
string bumpmap="";
float bumpdepth = 0.0;
float bumpmidval = 0.5;
float s_offset = 0;
float t_offset = 0;
float s_scale = 1;
float t_scale = 1;
)
{
color rimcolor;
color Ct;
float bumpMagnitude;
point PP = P;
normal Nn = normalize(N);
normal Nf = Nn;
vector V = -normalize(I);
color Cback = 0;
if(colormap != "")
{
Ct =
color texture(colormap,(s_scale*s)+s_offset, (t_scale*t)+t_offset);
} else
{
Ct = 1.0;
}
if(bumpmap != "")
{
bumpMagnitude =
texture(bumpmap,(s_scale*s)+s_offset,(t_scale*t)+t_offset);
PP -= Nn * bumpdepth * (bumpMagnitude-bumpmidval);
Nf = normalize(calculatenormal(PP));
}
Nf = faceforward(Nf,I);
if(Ktranslucent > 0)
{
Cback = Cs * Ct * diffuse(-Nf);
}
rimcolor = grazing_specular(Nf,V,rimscatter);
Oi = Os;
Ci = Os * (Cs * Ct * (Ka*ambient())
+ Kd*orennayar_diffuse(Nf,V,diffuserough)
+ Ks*specular(Nf,V,specularrough)*specularcolor
+ Krim*rimcolor
+ Ktranslucent*Cback);
}