Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Advanced_Renderman_Book[torrents.ru]

.pdf
Скачиваний:
1741
Добавлен:
30.05.2015
Размер:
38.84 Mб
Скачать

304 12 A Gallery of Procedural Shaders

Figure 12.8 The pattern of concentric rings of dark and light wood.

Add noise(r) to r to prevent all rings from being equally spaced:

float dr = filterwidth(r);

r += 0.5*filteredsnoise(r,dr);

The resulting ring structure is shown in Figure 12.9. This is getting closer to the large-scale structure, but we still need the fine grain for close-ups. First, we must find a domain for our noise grain. We will use our original Pshad scaled by a high-frequency grainfreq. Furthermore, we want our grains to be long and thin, stretched out in the z-direction, which we achieved by multiplying the domain by vector(1,1,0.05) (remember that multiplication of vectors is done component by component, so this squashes the domain by a factor of 20 in the z-direction):

12.4Wood Grain

Figure 12.9 The ring pattern after several layers of noise() are added.

point Pgrain = Pshad*grainfreq*vector(1,1,.05); float dPgrain = dPshad*grainfreq;

At this point, just calculating noise(Pshad) does not quite give the look wanted. Instead, after a couple hours of trial and error, this author hit on following unrolled fBm loop:

float grain = 0; float i, amp=1;

for (i = 0; i < 2; i += 1) {

float grainlvalid = 1-smoothstep(.2,.6,dPgrain); if (grainlvalid > 0) {

float g = grainlvalid * snoise (Pgrain);

306 12 A Gallery of Procedural Shaders

g *= (0.3 + 0.7*inring);

g = pow(clamp(0.8 - (g),0,1),2); g = smoothstep (0.5, 1, g);

if (i == 0)

inring *= (1-0.4*grainlvalid); grain = max (grain, g);

}

Pgrain *= 2; dPgrain *= 2; amp *= 0.5;

}

return mix (inring, 1, grain);

There is little apparent rhyme or reason to this code. Like much shader pattern generation, it is simply the result of trial and error, and a little intuition about the shape of these functions. However, you should be able to extract some meaning from the loop: it adds two octaves of grain; it attempts to antialias by fading out the grain at the Nyquist limit; it makes the grain stronger in the dark sections of the rings; it uses smoothstep and pow to sharpen the grain appearance; and for the lower frequency of grain, it scales down the inring value. Our reason for scaling inring is that for close-ups, the grain is the dominant feature. For far shots, the grains visually merge to contribute to the ring look.

We combine inring (which is 1 when in the dark portion of the rings) with grain (which is 1 inside a grain) to form the final blending value between the light and dark woods:

return mix (inring*ringy, 1, grain);

The meaning of the mix is that when grain==0, we return the ring value, but when grain==1, we return the dark wood, regardless of the ring. Finally, we present the completed shader (Listing 12.3), which modularizes the wood pattern into a reusable function, uses the result to blend between light and dark wood as well as to displace down slightly in the grains, and uses a basic plastic illumination model (as this is for unfinished wood). The results are shown in Figure 12.10.

Listing 12.3 The oak.sl shader implements a simple, unfinished oak wood grain.

/*************************************************************************

*oak.sl

*

* Description: makes procedural solid texture that looks very much

like

*wood grain. The rings surround the z-axis, so to position the

*pattern, one should translate the shadingspace (which defaults

to

*"shader"). This makes a fairly plain, unfinished wood that looks

*very much like oak.

12.4Wood Grain

Listing 12-3 (confnued)

*Parameters for the coordinate mapping:

*shadingspace - space in which the pattern is laid out

*shadingfreq - overall scaling factor for the pattern

*Pref - if supplied, gives the reference pose

*Parameters for the color and pattern:

*Clightwood - the light, "background" wood color

*Cdarkwood - the darker color in the ring/grain

*ringfreq - mean frequency of ring spacing

*ringunevenness - 0=equally spaced rings, larger is unequally spaced

*grainfreq - frequency of the fine grain

*ringnoise, ringnoisefreq - general warping of the domain

*trunkwobble, trunkwobblefreq - controls noise that wobbles the axis

*of the trunk so that it's not perfectly on the z-axis.

*angularwobble, angularwobblefreq - warping indexed by angle about

*the z-axis.

*ringy, grainy - overall scale on the degree to which rings and

*grain are weighted. 0 turns one off; 1 makes full effect.

*divotdepth - depth (in shader units) of the displacement due to

*ring or grain.

*truedisp - 1 for true displacement; 0 for bump mapping

*

*Parameters for illumination model:

*Ka, Kd, Ks, roughness - the usual meaning

***************************************************************************/

#include "project.h" #include "pshad.h" #include "material.h" #include "displace.h"

float

oaktexture (point Pshad;

float dPshad; float ringfreq, ringunevenness, grainfreq; float ringnoise, ringnoisefreq;

float trunkwobble, trunkwobblefreq; float angularwobble, angularwobblefreq; float ringy, grainy;)

{

/* We shade based on Pshad, but we add several layers of warping: */ /* Some general warping of the domain */

vector offset = vfBm(Pshad*ringnoisefreq,dPshad*ringnoisefreq, 2, 4, 0.5);

point Pring = Pshad + ringnoise*offset;

/* The trunk isn't totally steady xy as you go up in z */ Pring += trunkwobble *

vsnoise(zcomp(Pshad)*trunkwobblefreq) * vector(1,1,0);

/* Calculate the radius from the center. */ float r2 = sqr(xcomp(Pring)) + sqr(ycomp(Pring));

308 12 A Gallery of Procedural Shaders

Listing 12,3 (continued)

float r = sqrt(r2) * ringfreq;

/* Add some noise around the trunk */ r += angularwobble * smoothstep(0,5,r)

* snoise (angularwobblefreq*(Pring)*vector(1,1,0.1));

/* Now add some noise so all rings are not equal width */ extern float du, dv;

float dr = filterwidth(r);

r += 0.5*filteredsnoise(r,dr);

float inring = smoothpulsetrain (.1, .55, .7, .95, 1, r);

point Pgrain = Pshad*grainfreq*vector(1,1,.05); float dPgrain = filterwidthp(Pgrain);

float grain = 0; float i, amp=1;

for (i = 0; i < 2; i += 1) {

float grainlvalid = 1-smoothstep(.2,.6,dPgrain); if (grainlvalid > 0) {

float g = grainlvalid * snoise (Pgrain); g *= (0.3 + 0.7*inring);

g = pow(clamp(0.8 - (g),0,1),2);

g = grainy * smoothstep (0.5, 1, g); if (i == 0)

inring *= (1-0.4*grainlvalid); grain = max (grain, g);

}

Pgrain *= 2; dPgrain *= 2; amp *= 0.5;

}

 

return mix (inring*ringy, 1, grain);

 

}

 

surface

 

oak ( float Ka = 1, Kd = 1, Ks = .25, roughness = 0.2;

 

DEFAULT_PSHAD_PARAMS;

 

float ringfreq = 8, ringunevenness = 0.5;

 

float ringnoise = 0.02, ringnoisefreq = 1;

 

float grainfreq = 25;

 

float trunkwobble = 0.15, trunkwobblefreq = 0.025;

 

float angularwobble = 1, angularwobblefreq = 1.5;

 

float divotdepth = 0.05;

 

color Clightwood = color (.5, .2, .067);

/* light wood color */

color Cdarkwood = color(0.15, 0.077, 0.028);

 

float ringy = 1, grainy = 1;

 

float truedisp = 0;

 

)

 

30

12.4Wood Grain

Listing 12.3 (continued)

{

GET-PSHAD;

normal Nf = faceforward(normalize(N),I);

float wood;

wood = oaktexture (Pshad, dPshad, ringfreq, ringunevenness, grainfreq ringnoise, ringnoisefreq, trunkwobble, trunkwobblefreq, angularwobble,

angularwobblefreq, ringy, grainy); color Cwood = mix (Clightwood, Cdarkwood, wood);

Nf = faceforward(Displace (Nf, "shader", -wood*divotdepth, truedisp), I);

/* Illumination model - just use plastic */

Ci = MaterialPlastic (Nf, Cwood, Ka, Kd, Ks*(1-0.5'*wood), roughness); Oi = Os; Ci *= Oi;

}

Figure 12.10 Example uses of the oak shader. Each object uses slightly different parameters to the shader, resulting in different looks and feels for the wood. (Trivia: the elephant is called "Gumbo" and was modeled by Ed Catmull.) See also color plate 12.10.

31012 A Gallery of Procedural Shaders

12.5Wood Planks

The shader from the previous section is adequate to shade an object that is presumed to be carved from a single block of wood. An obvious extension is to make finished planks, as you would see on a floor. One strategy would be to divide up the 2D domain into individual planks, then simply reference the oak pattern using a different "per-plank" offset into the texture function. We will divide ss and tt space into planks using a method reminiscent of the tilepattern function that we used in Section 12.3. First, we find which "plank column" we're in:

swhichplank = floor (ss/plankwidth); /* integer selector */

splank = ss - swhichplank*plankwidth; /* relative place within the strip */

Instead of doing exactly the same thing for t, we shift the t coordinate by a different amount for every plank column, prior to taking the combination of perpendicular pulse trains:

float newt = tt + planklength*cellnoise(swhichplank); twhichplank = floor (newt/planklength);

tplank = newt - twhichplank*planklength;

return filteredpulsetrain (groovewidth, plankwidth, ss+groovewidth/2, ds)

* filteredpulsetrain (grooveheight, planklength, newt+grooveheight/2, dt);

Knowing which plank we are on, we can call oaktexture with a different offset for every plank (again using cellnoise). We can shift the color of each plank independently, also reusing the varyEach function from Section 12.3. (Are you noticing the trend? Once we hit on a good idea, we put it in our library and reuse it as frequently as we can get away with it.)

point Ppat = point(splank-0.5,height-0.01*tplank,tplank) + vector(1,5,10)* (vector cellnoise(swhichplank,

twhichplank) - 0.5);

float wood = oaktexture (Ppat, dPshad, ringfreq, ringunevenness, grainfreq, ringnoise, ringnoisefreq, trunkwobble, trunkwobblefreq, angularwobble, angularwobblefreq, ringy, grainy);

color Cwood = mix (Clightwood, Cdarkwood, wood);

Cwood = varyEach (Cwood, plankindex, varyhue, varysat, varylum);

Also reused from earlier sections are the techniques for displacing the grooves and rounding the corners of the planks. We throw in some low-amplitude noise to simulate lumpy varnish and use the MaterialShinyPlastic illumination model (not an entirely bad approximation to varnished wood). See Figure 12.11 and Listing 12.4.

12.5Wood Planks

Figure 12.11 Example use of the oakplank shader. See also color plate 12.11.

Listing 12.4 The oakplank.sl shader implements varnished oak planks.

/****************************************************************************

*oakplank.sl

*Description: makes procedural varnished wood planks. The planks are projected

*onto the x-y plane, with the length aligned with the y-axis. The

*subpattern within each individual plank is just a shifted version of the

*oaktexture function from oak.h.

*

*Parameters for the coordinate mapping:

*shadingspace - space in which the pattern is laid out

*shadingfreq - overall scaling factor for the pattern

*Pref - if supplied, gives the reference pose

*Parameters for the pattern of the plank structure:

*plankwidth, planklength – size of the planks

*groovewidth, grooveheight - -width of the grooves between planks

*Cgroove – color of the grooves between the planks

*groovedepth – how far down do the grooves desplace?

*edgewidth – how close to the plank border does the wood start to curl?

*varyhue, varysat, varylum – control plank-to-plank color varation

312 12 A Gallery of Procedural Shaders

Listing 12.4 (continued)

*Parameters for the color and pattern of the wood grain: Clightwood - the

*light, "background" wood color

*Cdarkwood - the darker color in the ring/grain

*ringfreq - mean frequency of ring spacing

*ringunevenness - 0=equally spaced rings, larger is unequally spaced

*grainfreq - frequency of the fine grain

*ringnoise, ringnoisefreq - general warping of the domain trunkwobble,

*trunkwobblefreq - controls noise that wobbles the

*axis of the trunk so that it's not perfectly on the z-axis.

*angularwobble, angularwobblefreq - warping indexed by angle about the z-axis.

*ringy, grainy - overall scale on the degree to which rings and

*grain are weighted. 0 turns one off; 1 makes full effect.

*divotdepth - depth (in shader units) of the displacement due to ring or grain.

*truedisp - 1 for true displacement; 0 for bump mapping

*

*Parameters for illumination model:

*Ka, Kd, Ks, roughness - the usual meaning

*Kr, blur, eta - reflection parameters for the tile

*envname, envspace, envrad - environment mapping controls rayjitter,

*raysamples - ray tracing controls

*varnishlump, varnishlumpfreq - amp & freq of lumpiness in the varnish

************************************************************************************/

#include "project.h" #include "pshad.h" #include "material.h" #include "noises.h" #include "displace.h" #include "patterns.h"

#include "oak.h"

/* Given 2D texture coordinates ss,tt, filter widths ds, dt, and the

*width and height of the grooves between tiles, figure out which

*(integer indexed) plank we are on and what coordinates within our

*individual plank we are shading.

*/

float

plankpattern (float ss, tt, ds, dt;

float plankwidth, planklength; float groovewidth, grooveheight;

output float swhichplank, twhichplank; output float splank, tplank;)

{

/* Find which s plank we're on and our s coordinate within it */ swhichplank = floor (ss/plankwidth);

splank = ss - swhichplank*plankwidth;

313

12.5Wood Planks

Listing 12,4 (continued)

/* Shift in t a random amount for each plank column */ float newt = tt + planklength*cellnoise(swhichplank);

/* Find which t plank we're on and our t coordinate within it twhichplank = floor (newt/planklength);

tplank = newt - twhichplank*planklength;

/* Calculate our "in-plank" value by multiplying two perpendicular * filtered pulsetrain functions.

*/

return filteredpulsetrain (groovewidth, plankwidth, ss+groovewidth/2, ds)

* filteredpulsetrain (grooveheight, planklength, newt+grooveheight/2, dt);

}

surface

oakplank ( float Ka = 1, Kd = 1, Ks = .75, roughness = 0.1; float Kr = 1, blur = 0, eta = 1.5; DECLARE_DEFAULTED_ENVPARAMS; DEFAULT_PSHAD_PARAMS;

float ringfreq = 8, ringunevenness = 0.5; float ringnoise = 0.02, ringnoisefreq = 1; float grainfreq = 25;

float trunkwobble = 0.15, trunkwobblefreq = 0.025; float angularwobble = 1, angularwobblefreq = 1.5; float divotdepth = 0.012, truedisp = 0;

color Clightwood = color (.5, .2, .067); /* light wood color */

color Cdarkwood = color(0.15, 0.077, 0.028); color Cgroove = color(0.02, 0.02, 0.02); float ringy = 1, grainy = 1;

float plankwidth = 2, planklength = 30;

float groovewidth = 0.05, grooveheight = 0.05; float varyhue = 0.015, varysat = 0.1, varylum = 0.5; float groovedepth = 0.03, edgewidth = 0.1;

float varnishlump = 0.01, varnishlumpfreq = 0.5;

)

{

GET-PSHAD;

float ss = xcomp(Pshad), tt = ycomp(Pshad), height = zcomp(Pshad); float dss = filterwidth(ss), dtt = filterwidth(tt);

/*

*Find out where in the pattern we are: which plank we're on, and

*the (splank,tplank) coordinates (both on [0,1]) within our tile. */

float swhichplank, twhichplank, splank, tplank;

float inplank = plankpattern (ss, tt, dss, dtt, plankwidth, planklength, groovewidth, grooveheight, swhichplank, twhichplank, splank, tplank);

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]