/**************************************************************************** * superplank.sl -- another surface shader for wood planks. * * Description: * Makes texture of wooden planks in s-t space. This wood looks rather * like varnished oak planks, with staggered planks, rings and grain, * reflections (either traced or via reflection map), and bumps. * It can make the plank pattern as either straight staggered planks * or in a parquet pattern. * * Parameters: * Ka, Kd, Ks, specularcolor, roughness - work just like the plastic shader * Kr, eta - reflection amount and index of refraction (for fresnel) * Ktrace, Krefl, reflmap - const for trace, for refl map, filename * txtscale - overall scaling factor for the texture * Km - overall scaling factor for bumpiness. * lightwood - wood-like color from which the various shades are derived. * plankwidth - width of each plank (in terms of s/t) * planklength - length of each plank (in terms of s/t) * groovewidth - width of the grooves between the planks (in terms of s/t) * groovedepth - depth of the groove indentations (in shader space units) * groovecolor - the color of the "grooves" between the planks * plankpattern - plank orientation pattern 1=straight, 2=parquet * plankspertile - for parquet, number of sub-planks * plankstagger - for staggered, how much are the rows of planks staggered * plankvary - controls how much wood color varies from plank to plank * ringscale - scaling for the ring spacing * ringwidth - relative width of the dark ring parts * wavy - relative wavyness of the ring pattern * grainy - relative graininess (0 = no fine grain) * grainscale - scaling for the fine grain * graindepth - depth of grain and ring grooves * varnishbumpfreq - frequency of bumps in the varnish coating * varnishbumpamp - height of bumps in the varnish coating * * Antialiasing: this shader does a pretty good job of antialiasing itself, * even with low sampling densities. * * Author: written by Larry Gritz * current contact address: gritzl@acm.org * * $Revision: 1.6 $ $Date: 1997-11-23 17:36:25-08 $ * ****************************************************************************/ #include "noises.h" #include "patterns.h" #define MINFILTERWIDTH 1.0e-7 /* Estimate the filter width -- i.e. the change from pixel to pixel -- of * float and point values. */ #define filterwidth(x) (abs(Du(x)*du) + abs(Dv(x)*dv)) surface superplank (/* Parameters: */ /* BRDF */ float Ka = 1, Kd = 1; /* Overall ambient & diffuse response */ float Ks = .75, roughness = .02; /* Spec highlight control */ color specularcolor = 1; /* Spec reflection color */ float Kr = 1, eta = 1.5; /* Mirror refl & index of refr */ float Ktrace = 1, Krefl = 0; /* trace and reflection map */ string reflmap = ""; /* Overall texturing control */ float txtscale = 1; /* Overall scaling factor */ float Km = 1; /* Overall bumpiness factor */ color lightwood = color (.5, .2, .067); /* light wood color */ /* Plank layout */ float plankpattern = 1; /* 1=straight, 2=parquet */ float plankspertile = 4; /* For parquet */ float plankwidth = .05; /* Width of a plank */ float planklength = .75; /* Length of a plank */ float groovewidth = 0.001; /* Width of the grooves between planks */ float groovedepth = .0004; /* Depth of the groove */ color groovecolor = color (.01, .003, .001); float plankstagger = 1; /* How much should rows be staggered */ float plankvary = 0.8; /* Wood color variation plank-to-plank */ /* Wood appearance */ float ringscale = 25; /* Larger makes more, thinner rings */ float ringwidth = 1; /* Relative ring width */ float wavy = 0.08; /* Larger makes rings more wavy */ float grainy = 1; /* Relative graininess */ float grainscale = 60; /* Larger makes smaller "grains" */ float graindepth = 0.0001; /* Depth of divots where grains are */ float varnishbumpfreq = 30; /* Bump freq of the varnish */ float varnishbumpamp = 0.0003; /* How big are the varnish bumps? */ ) { /* mapping coordinates */ float ss, tt; float grain_s, grain_t, ring_s, ring_t, plank_s, plank_t; /* antialiasing */ float swidth, twidth, fwidth; float sw, tw, overallscale; /* Planks & grooves */ uniform float PGWIDTH, PGHEIGHT, GWF, GHF; float whichrow, whichplank; /* Index for each row & plank within row */ float w, h; /* temporaries */ float groovy; /* 0 in groove, 1 in woody part */ /* Wood appearance */ color Ct, woodcolor; float r2; float fade, ttt; float ring; /* 1 in a ring darkening, 0 where not */ float grain; /* 1 inside a grain bit, 0 elsewhere */ /* Illumination model */ float adjustedKs; vector IN; /* normalized I vector */ normal NN; /* normalized N for displacing */ normal Nf; /* forward facing, normalized normal */ vector R, T_dummy; /* Refl (and tummy transmit) from fresnel */ float fresnelKr, fresnelKt; /* Fresnel reflection coefficients */ float shadlen; /* length of a unit of shader space */ float disp; /* accumulate displacement here */ point Pndc; /* NDC coordinate of P */ vector V; color Cspec; float nonspec; float tmp; /* * 1. Setup. * * Determine the basic mapping, filter sizes for antialiasing, other * values used throughout the shader. */ /* First, determine the basic mapping */ ss = s * txtscale; tt = t * txtscale; /* Compute the basic filter size for antialiasing */ swidth = max (filterwidth(ss), MINFILTERWIDTH); twidth = max (filterwidth(tt), MINFILTERWIDTH); fwidth = max (swidth,twidth); /* How much current space corresponds to a unit of s or t? * We will use this later to help scale the displacement, this making * the bumps scale relative to the overall pattern, rather than being * strictly tied to the scale of the object. */ overallscale = (length(Deriv(P,ss))); /* * 2. Plank pattern. * * Determine which row and plank we're on, and come up with an * antialiased term for whether we're in or out of a groove. */ if (plankpattern == 1) { /* Straight, staggered planks */ PGWIDTH = plankwidth + groovewidth; PGHEIGHT = planklength + groovewidth; plank_s = ss / PGWIDTH; whichrow = floor (plank_s); /* Jiggle each row */ plank_t = tt / PGHEIGHT + 20 * plankstagger * cellnoise(whichrow); whichplank = floor(plank_t); } else { /* Parquet pattern */ PGWIDTH = plankwidth + groovewidth; PGHEIGHT = PGWIDTH * plankspertile; plank_s = ss / PGWIDTH; whichrow = floor (plank_s); plank_t = tt / PGHEIGHT; whichplank = floor(plank_t); if (mod ((whichrow/plankspertile) + whichplank, 2) >= 1) { plank_s = tt / PGWIDTH; plank_t = ss / PGHEIGHT; whichrow = floor (plank_s); whichplank = floor(plank_t); tmp = ss; ss = tt; tt = tmp; tmp = swidth; swidth = twidth; twidth = tmp; } } /* Now whichplank is a unique integer index for each plank */ /* Figure out where the grooves are. The value groovy is 0 where there * are grooves, 1 where the wood grain is visible. Do some simple * antialiasing by trying to box filter the edges of the grooves. */ /* compute half width & length of groove as fraction of plank size */ GWF = groovewidth*0.5/PGWIDTH; GHF = groovewidth*0.5/PGHEIGHT; sw = swidth / PGWIDTH; tw = twidth / PGHEIGHT; if (sw >= 1) w = 1-2*GWF; /* Filter width is wider than the plank itself */ else w = filteredpulse (whichrow+GWF, whichrow+1-GWF, plank_s, sw); if (tw >= 1) h = 1 - 2*GHF; /* Filter width is longer than the plank itself */ else h = filteredpulse (whichplank+GHF, whichplank+1-GHF, plank_t, tw); groovy = w*h; /* * 3. Ring and grain patterns, color and specularity adjustment. * * The wood has rings at one scale, grain at a finer scale. They * interact subtly. */ /* Compute the filter width first, so we don't bother with more if * the rings are too small to see. */ fwidth = max (swidth*ringscale, twidth*ringscale); fade = smoothstep (.75, 4, fwidth); if (fade < 0.999) { ring_s = ss * ringscale; ring_t = tt * ringscale; ttt = ring_t + whichplank*28.38 + wavy * noise (8*ring_s, ring_t); ring = ringscale * noise (ring_s-whichplank*4.18, ttt/20); ring -= floor (ring); ring = 0.3 + 0.7 * smoothstep(.55-.35*ringwidth, 0.55, ring) * (1 - smoothstep(0.75, 0.8, ring)); ring = (1-fade)*ring + 0.65*fade; /* Grain pattern */ fwidth = max (swidth*grainscale, twidth*grainscale); fade = smoothstep (.75, 4, fwidth); if (fade < 0.999) { grain_s = ss * grainscale; grain_t = tt * grainscale; r2 = 1.3 - noise (12*grain_s, grain_t); r2 = grainy * r2*r2 + (1-grainy); grain = (1-fade)*r2 + (0.75*fade); } else grain = 0.75; } else { ring = 0.4875; grain = 0.75; } grain *= (.85 + .15*ring); /* Start with the light wood color */ woodcolor = lightwood; /* Add some plank-to-plank variation in overall color */ woodcolor *= 1 - plankvary/2 + plankvary * float cellnoise (whichplank,whichrow); /* Darken the wood according to the ring and grain patterns */ woodcolor *= (1 - 0.25*ring) * (1-.5*grain); /* Combine the rings, grain, plank variation into one surface color */ Ct = mix (groovecolor, woodcolor, groovy); /* Less specular in the grooves, more specular in the dark wood. */ adjustedKs = Ks * (1 + .2*ring) * (1 + .3*grain) * groovy; /* * 4. Bumps * * We do some bump mapping to make the grooves and grain depressed, * and add some general lumpiness to the varnish layer. */ if (Km > 0) { /* Grooves */ disp = -groovedepth * (1-groovy); /* Random bumps on the varnish */ if (varnishbumpamp > 0) disp += varnishbumpamp * (2*noise(varnishbumpfreq*ss,varnishbumpfreq*tt) - 1); /* Depressions due to grain & rings */ disp -= graindepth * (.75*grain + ring); /* Find out how long a unit of shader space is, in current units */ NN = normalize(N); shadlen = overallscale/length (ntransform("shader",NN)); /* Recalculate N */ N = calculatenormal (P + (Km * shadlen * disp) * NN); } /* * 5. BRDF * * The wood itself behaves like plastic, the varnish overtop reflects * using the fresnel formula (grazing angles reflect like mirrors). * Have some subtle interaction between grain and specularity. */ IN = normalize(I); V = -IN; Nf = faceforward (normalize(N),I); fresnel (IN, Nf, 1/eta, fresnelKr, fresnelKt, R, T_dummy); fresnelKr *= Kr; Cspec = 0; illuminance (P, Nf, PI/2) { nonspec = 0; lightsource ("__nonspecular", nonspec); if (nonspec < 1) { vector H = normalize(normalize(L)+V); Cspec += Cl * ((1-nonspec) * smoothstep (.6, .85, pow(max(0,Nf.H), 8/roughness))); } } Ci = (1-fresnelKr) * (Ct * (Ka*ambient() + Kd*diffuse(Nf))) + adjustedKs * (specularcolor * Cspec); /* Only compute reflections if Kr > 0 */ if (fresnelKr > 0.001) { if (Krefl > 0 && reflmap != "") { Pndc = transform ("NDC", P); Ci += fresnelKr * specularcolor * texture (reflmap, xcomp(Pndc), 1-ycomp(Pndc)); } if (Ktrace > 0) Ci += Ktrace * fresnelKr * specularcolor * trace (P, R); } /* Scale by opacity */ Oi = Os; Ci *= Oi; }