/*************************************************************** 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); }