/* * Ashkihmin-Shirley BRDF * Peter Stuart * $Id: ash-shir.sl,v 1.9 2003/05/05 05:22:41 ophi Exp $ * description: Ashikmin and Shirley BRDF lighting model as described * in "An Anisotropic Phong Light Reflection Model." A local illumination * version and an environment map version are both included. The environment * map version uses the importance sampling as described in the paper to reduce * variance. * * ---- local illumination surface parameters ---------- * N: normal direction * V: view direction * xdir: direction of anisotropy - can be primary or secondary * rd: diffuse reflectance * rs: specular reflectance * nu: specular exponent along xdir * nv: specular exponent perpendicular to xdir * * ---- environment illumination surface parameters ---------- * N: normal direction * V: view direction * xdir: direction of anisotropy - can be primary or secondary * rd: diffuse reflectance * rs: specular reflectance * nu: specular exponent along xdir * nv: specular exponent perpendicular to xdir * samples: number of samples to take of the environment map * envmap: name of environment map */ /* ---- fast fresnel -------- * H: vector halfway between V and L ((V+L) / 2) * V: view vector (-I/|I|) * f0: reflectance at normal incidence * * Schlick's approximation to the fresnel function */ float fastFresnel(vector H, V; float f0;) { return f0 + (1-f0)*pow(1 - (H.V), 5); } /******** local illumination model **********/ color LocIllumAshShir (normal N; vector k1; vector xdir; float rd, rs; float nu, nv;) { float sqr(float x) { return x*x; } vector u_dir = xdir; vector v_dir = N ^ xdir; vector k2; vector h; float rho_d, rho_s; /* calculations that can be done one time ... */ color diff = color(0,0,0), spec = color(0,0,0); float diffcont = ((28*rd) / (23)) * (1-rs)*(1-pow(1-(N.k1)/2, 5)); float speccont = (sqrt((nu + 1)*(nv + 1)) / (8)); color C = 0; extern point P; extern color Cs; illuminance(P, N, PI/2) { extern vector L; extern color Cl; k2 = normalize(L); h = normalize(k1 + k2); rho_d = diffcont * (1-pow(1-(N.k1)/2, 5)); rho_s = speccont * (pow(N.h, (nu*sqr(h.u_dir) + nv*sqr(h.v_dir)) / (1-sqr(h.N)))) / ((h.k1)*max(N.k1, N.k2)) * fastFresnel(h, k1, rs); C += (rho_d*Cs + rho_s) * Cl * max(0, (k2.N)); } return C; } /******* "global" illumination model ********/ /* ---- function to sample environment ---- * P: location of sample * R: reflection vector * envname: name of environment map - use raytracing if "" */ color SampleEnvironment (point P; vector R; uniform string envname;) { color C = color(0,0,0); if (envname != "") { C = color environment(envname, R); } else { C = trace(P, R); } return C; } /* ---- helper function ----- * nu, nv: anisotropic specular exponents * e1: random variable [0,1] */ float calcPhi(float nu, nv; float e1) { return atan(sqrt((nu+1)/(nv+1)) * tan(PI*e1/2)); } color EnvIllumAshShir (normal N; vector V; vector xdir; float rd, rs; float nu, nv; uniform float samples; uniform string envmap;) { uniform float i; color C = color(0,0,0); for (i=0; i= 0 && e1 < 0.25) ph = calcPhi(nu, nv, 1 - 4*(0.25 - e1)); else if (e1 >= 0.25 && e1 < 0.5) ph = calcPhi(nu, nv, 1 - 4*(0.5 - e1)) + PI/2; else if (e1 >= 0.5 && e1 < 0.75) ph = calcPhi(nu, nv, 1 - 4*(0.75 - e1)) + PI; else ph = calcPhi(nu, nv, 1 - 4*(1 - e1)) - PI/2; float cosph = cos(ph); float sinph = sin(ph); float th = acos(pow(1-e2, 1/(nu*cosph*cosph+nv*sinph*sinph+1))); if (th <= PI/2) { /* we don't want thetas below hemisphere */ /* transform H (halfway vector) into space where frame is */ vector ydir = N ^ xdir; vector H = cosph*sin(th)*xdir + sinph*sin(th)*ydir + cos(th)*N; extern point P; C += rs * SampleEnvironment(P, 2*H - V, envmap) * fastFresnel(H,V,rs); /* now generate the diffuse part using cos-weighted distribution. */ e1 = random(); e2 = random(); H = cos(2*PI*e1)*sqrt(1-e2)*xdir + sin(2*PI*e1)*sqrt(1-e2)*ydir + sqrt(e2)*N; C += rd * SampleEnvironment(P, H, envmap); /* FIXME - instead of sampling over the hemisphere, get the cos-weighted average value, which can be done with a cos-weighted filtered call to environment. The problem is that such a filter doesn't seem to exist :( */ /* C += rd * color environment(envmap, xdir, ydir, -xdir, -ydir, */ /* "filter", "gaussian"); */ } } if (samples > 0) C /= samples; return C; }