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

Advanced_Renderman_Book[torrents.ru]

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

294 12 A Gallery of Procedural Shaders

tiledisp *= smoothpulse (0, .075, 0.925, 1, ttile); normal Nf = faceforward (normalize(N), I);

normal Ntile = Displace (P, Nf, 0, groovedepth*(tiledisp-1), "shader");

We notice that in real life, every tile is oriented slightly differently than every other, thus causing reflected images to break up a bit at tile boundaries. We can simulate this effect by adding a very small amount of per-tile noise to the surface normal, using the vector cellnoise function:

float tileindex = swhichtile + 13*twhichtile;

Ntile += 0.05 * (vector cellnoise (tileindex+5) - 0.5); Nf = normalize (mix (Nf, Ntile, intile));

Moving on to the texture within each tile, we can observe that the tiles have a complex and pleasing mottling of color. We can use fBm from Section 10.4 for this purpose:

color C = Cbase;

float dst = max(ds,dt);

point noisep = mottlefreq*point(stile,ttile,tileindex); float mottle = .2+.6*fBm(noisep, mottlefreq*dst, 4, 2, 0.65); C = mix (C, Cmottle, clamp(mottling*mottle,0,1));

We have computed fBm on 3D points constructed from the sti1e and ttile coordinates (scaled by an appropriate frequency) and a per-tile index. This causes the pattern to be looking at a different slice of fBm space for every tile so that each tile will not have the identical mottling pattern.

Frequently there is another, usually darker, color close to the edges of the tile and with a ragged boundary:

float sedgeoffset = .05*fBm(point(stile*10, ttile*10, tileindex+10), 10*dst, 2, 2, 0.5);

float tedgeoffset = .05*fBm(point (stile*10, ttile*10, tileindex-3), 10*dst, 2, 2, 0.5);

float edgy = 1 - ( smoothpulse (.05, .15, .85, .95, stile+sedgeoffset) smoothpulse (.05, .15, .85, .95, ttile+tedgeoffset));

C = mix (C, Cedge, edgy);

Here we have used fBm to offset the stile and ttile values before being passed through smoothpulse. Figure 12.6 shows the variable edgy with and without the noise warping.

To add speckles, we will threshold a medium-frequency noise by running it through a smoothstep that ramps up from 0.55 to 0.7. Most noise "bumps" will be lower than the threshold, thus falling out. Some will be large enough and will show up as a speckle.

float speckfreq = 7;

point noisep = point (stile*speckfreq, ttile*speckfreq, tileindex+8);

295

12.3 Ceramic Tiles

Figure 12.6 Close-up of the edgy variable, which mixes between the base mottled color and the special tile edge color (small values of edgy are shown in white, large values in black). Just using the smoothpulse to define the shape of the edge region (left). Using fBm to warp the domain gives a ragged edge (right).

float specky = filteredsnoise (noisep, speckfreq*dst); specky = smoothstep (0.55, 0.7, specky);

C = mix (C, Cspeck, specky);

We notice that in real life, the colors aren't the same on each tile-they can differ quite a bit in the base color. Therefore, we will add a function that, given the computed color of the tile, will tweak it on a per-tile basis. For flexibility, we will allow user controls over the per-tile variation, separately for each of hue, saturation, and luminance (using the variables varyhue, varysat, and varylum). We do this by transforming Ctile into "hsl" space, where it can more conveniently have variation added to its hue, saturation, and lightness. Before returning, we clamp to reasonable values and convert back to "rgb" space:

color varyEach (color Cin; float index, varyhue, varysat, varylum;)

{

/* Convert to "hsl" space, it's more convenient */ color Chsl = ctransform ("hsl", Cin);

float h = comp(Chs1,0), s = comp(Chsl,l), l = comp(Chs1,2); /* Modify Chsl by adding Cvary scaled by our separate h,s,l

controls */

h += varyhue * (cellnoise(index+3)-0.5);

s *= 1 - varysat * (cellnoise(index-14)-0.5); l *= 1 - varylum * (cellnoise(index+37)-0.5);

Chsl = color (mod(h,l), clamp(s,0,1), clamp(1,0,1)); /* Clamp hsl and transform back to rgb space */

return ctransform ("hsl", "rgb", clamp(Chsl,color 0, color 1));

}

We're nearly done. The mortar can simply be a diffuse fBm pattern:

296 12 A Gallery of Procedural Shaders

color Cmortar = mortarcolor; point Q = 20*point(ss,tt,0); float dQ = filterwidthp (Q); if (intile < 1.0)

Cmortar *= smoothstep (0, 1, (.5 + .4 * fBm (Q, dQ, 3, 2, .6)));

And now the only thing left is the illumination model. We will make an appropriate ceramic reflection model with components for diffuse, the glossy specular of Section 9.5.3, and some Fresnel-based reflection. We multiply kr and ks by intile in order to get rid of highlights and reflections for the mortar parts:

color basecolor = mix (Cmortar, Ctile, intile); float ks = Ks * intile;

float kd = mix (Kdmortar, Kdtile, intile); vector IN = normalize(I), V = -IN;

float fkr, fkt; vector R, T;

fresnel (IN, Nf, 1/eta, fkr, fkt, R, T); fkt = 1-fkr;

float kr = fkr * Kr * intile;

Ci = fkt * basecolor * (Ka*ambient() + kd*diffuse(Nf))

+ks * LocIllumGlossy (Nf, V, roughness/10, specsharpness)

+kr * SampleEnvironment (P, R, blur, ENVPARAMS);

Figure 12.7 shows the final tile shader in action, and Listing 12.2 lists the complete shader source code. Notice how we have modularized the shader, moving each of the major pieces of functionality into a separate function.

There are a number of enhancements that could be made to this shader. We will leave these modifications as exercises for the reader, but here are some suggestions:

Add the ability to use texture maps to control some of the procedural aspects of the shader, including:

the mortar/tile division and in-tile coordinates; one way to do this might be to have a three-channel texture specify the intile with channel 0, and stile and ttile in channels 1 and 2, respectively

the pattern of mottling and specks within each tile

the base color of each tile; this would allow you to lay out tiles in patterns, as is

often done in real life

For any of these features for which texture maps are not specified, the shader can fall back on the purely procedural code.

By increasing the tile and groove sizes, changing the type of illumination model to a rough diffuse appearance rather than a ceramic appearance, and modifying the mottling pattern, you could easily modify this shader into one for those orange terra-cotta tiles that line walkways.

We may reuse the technique of dividing into rectilinear tiles with very different "in-tile" patterns. For example, by replacing the mottling tiletexture function with a marble appearance, we could make a floor of tiled marble slabs.

29

12.3 Ceramic Tiles

Listing 12.2 Final shader for procedural ceramic tiles.

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

*ceramictiles.sl

*

*Description: Ceramic tiles (like you'd find in a bathroom)

*Parameters for pattern placement and size:

*projection, textureprojspace, mx - define the projection used to

*establish a basic 2D coordinate system for the pattern.

*stilespacing, ttilespacing - tile-to-tile spacing (separate controls

*for s and t directions)

*groovewidth, grooveheight - width of the spacing between tiles,

*expressed as a fraction of the tile-to-tile spacing.

*groovedepth - displacement amount for the grooves (expressed in

*shader space units)

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

*Parameters for tile color and pattern:

*Cbase, Cmottle - base color and mottle color of the tile

*mottlefreq - frequency of the mottling between Cbase & Cmottle

*Cedge - separate edge color for the tiles

*Cspeck - color of the occasional specks in the tiles

*edgevary, mottling, speckly - individual scalar controls over

*edge variation, mottling, and speckles. Setting any to zero will

*turn that feature off.

*varyhue, varysat, varylum - individual controls for the per-tile

*color variation (0 means don't vary in that way, larger values

*cause more tile-to-tile variation).

*

*Parameters for illumination model:

*Ka - the usual meaning

*Kdmortar - Kd for the mortar between tiles

*mortarcolor - base color of the mortar

*Kdtile - diffuse component weighting of the tile

*Ks, roughness, specsharpness - glossy specular controls of the tile

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

*envname, envspace, envrad - environment mapping controls

*rayjitter, raysamples - ray tracing controls

*

*************************************************************************

#include "project.h" #include "material.h" #include "noises.h"

#include "displace.h" #include "patterns.h"

298 12 A Gallery of Procedural Shaders

Listing 12.2 (continued)

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

*and the width and height of the grooves between tiles (assuming that

*tile spacing is 1.0), figure out which (integer indexed) tile we are

*on and what coordinates (on [0,1]) within our individual tile we are

*shading.

*/ float

tilepattern (float ss, tt, ds, dt;

float groovewidth, grooveheight; output float swhichtile, twhichtile; output float stile, ttile;)

{

swhichtile = floor (ss); twhichtile = floor (tt); stile = ss - swhichtile; ttile = tt - twhichtile;

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

* filteredpulsetrain (grooveheight, 1, tt+grooveheight/2, dt);

}

/* Given coordinates (stile,ttile) and derivatives (ds,dt) *within* a

*single tile, calculate the color of the tile at that point. Major

*features include (1) mottling of the color; (2) darkening or shifting

*to a different color near the border of the tile (with a ragged edge

*to the color transition); (3) occasional dark specks.

*/ color

tiletexture (float tileindex;

float stile, ttile, ds, dt;

float edgevary, mottling, speckly; float mottlefreq;

color Cbase, Cedge, Cmottle, Cspeck)

{

color C = Cbase;

float dst = max(ds,dt); if (mottling > 0) {

point noisep = mottlefreq*point(stile,ttile,tileindex);

float mottle = .2+.6*fBm(noisep, mottlefreqi*max(ds,dt), 4, 2, 0.65); C = mix (C, Cmottle, clamp(mottling*mottle,0,1));

}

if (edgevary > 0) {

float sedgeoffset = .05*fBm(point(stile*l0, ttile*10, tileindex+l0), 10*dst, 2, 2, 0.5);

2

12.3 Ceramic Tiles

Listing 12.2 (continued)

float tedgeoffset = .05*fBm(point(stile*10, ttile*10, tileindex-3) 10*dst, 2, 2, 0.5);

float edgy = 1 - (smoothpulse (.05, .15, .85, .95, stile+sedgeoffset) *

smoothpulse (.05, .15, .85, .95, ttile+tedgeoffset));

C = mix (C, Cedge, edgevary*edgy);

}

if (speckly > 0) {

float speckfreq = 7;

point noisep = point(stile*speckfreq, ttile*speckfreq, tileindex+8);

float specky = filteredsnoise (noisep, speckfreq*dst); specky = smoothstep (0.55, 0.7, specky);

C = mix (C, Cspeck, speckly*specky);

}

return C;

}

/* Compute the color of a ceramic object. Like plastic, but use a

*"glossy" specular term. We're actually blending between a purely

*diffuse model for the mortar and a ceramic model for the tiles,

*depending on the variable intile. When in the mortar area, we turn

*off highlights and reflections.

*/

color MaterialCeramicTiles (normal Nf; color Cmortar, Ctile; float intile;

float Ka, Kdmortar, Kdtile, Ks;

float roughness, specsharpness, Kr, blur, eta; DECLARE_ENVPARAMS)

{

extern vector I; extern point P;

color basecolor = mix (Cmortar, Ctile, intile); float ks = Ks * intile;

float kd = mix (Kdmortar, Kdtile, intile); vector IN = normalize(I), V = -IN;

float fkr, fkt; vector R, T;

fresnel (IN, Nf, 1/eta, fkr, fkt, R, T); fkt = 1-fkr;

float kr = fkr * Kr * intile;

return fkt * basecolor * (Ka*ambient<) + kd*diffuse<Nf))

+ks * LocIllumGlossy (Nf, V, roughness/10, specsharpness)

+kr * SampleEnvironment (P, R, blur, ENVPARAMS);

}

300 12 A Gallery of Procedural Shaders

Listing 12.2 (continued)

surface

ceramictiles (float Ka = 1, Ks = .75, roughness = 0.1; float Kr = 1, blur = 0, eta = 1.5; float specsharpness = 0.5;

float Kdtile = 0.5; float Kdmortar = 0.8;

color mortarcolor = color (.5, .5, .5); DECLARE_DEFAULTED_ENVPARAMS;

float stilespacing = 10, ttilespacing = 10; float groovewidth = 0.06, grooveheight = 0.06; float groovedepth = 0.2, truedisp = 0;

string projection = "st";

string textureprojspace = "shader";

float mx[16] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1}; float edgevary = 1, mottling = 1, speckly = 1; float mottlefreq = 7;

color Cbase = color(.05, .075, .6); color Cedge = color(.025, .025, .2); color Cmottle = color(.2, .2, .7); color Cspeck = color(.015, .015, .015);

float varyhue = 0.025, varysat = 0.4, varylum = 0.5;

)

{

/*

*Get a 2D texture coordinate for the texturing, then

*Normalize everything so that the tiles are 1 x 1 units

*float ss, tt, dss, dtt;

*/

ProjectTo2D (projection, P, textureprojspace, array_to_mx(mx), ss, tt, dss, dtt);

ss/= stilespacing; dss /= stilespacing;

tt/= ttilespacing; dtt /= ttilespacing;

/*

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

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

float swhichtile, twhichtile, stile, ttile; float intile = tilepattern (ss, tt, dss, dtt,

groovewidth, grooveheight, swhichtile, twhichtile, stile, ttile);

float tileindex = swhichtile + 13*twhichtile;

30

12.3 Ceramic Tiles

List 12.2 (continued)

/*

*Displacement: the edges of the tile displace down a bit, as do

*the grooves between tiles. Also, add just a little bit of

*per-tile normal variation to break up reflections.

*/

float tiledisp = smoothpulse (0, .075, 0.925, 1, stile); tiledisp *= smoothpulse (0, .075, 0.925, 1, ttile); normal Nf = faceforward (normalize(N), I);

normal Ntile = Displace (Nf, "shader", groovedepth*(tiledisp-1), truedisp);

Ntile += 0.05 * (vector cellnoise (tileindex+5) - 0.5);

Nf = normalize (mix (Nf, Ntile, intile));

/*

*Here's the exciting part -- calculate the color of the spot we're

*in within the tile. Then use the tile index to vary its color

*so every tile looks a little different.

*/

color Ctile = tiletexture (tileindex, stile, ttile, dss, dtt, edgevary, mottling, speckly, mottlefreq,

Cbase, Cedge, Cmottle, Cspeck); Ctile = varyEach (Ctile, tileindex, varyhue, varysat, varylum);

/*

*Set the color of the mortar between tiles, make it look good by

*scaling it by some high-frequency fBm.

*/

color Cmortar = mortarcolor; point Q = 20*point(ss,tt,0); float dQ = filterwidthp (Q); if (intile < 1.0)

Cmortar *= smoothstep (0, 1, (.5 + .4 * fBm (Q, dQ, 3, 2, .6)));

/*

*Illumination model */

Ci = MaterialCeramicTiles (Nf, Cmortar, Ctile, intile, Ka, Kdmortar, Kdtile, Ks, roughness, specsharpness,

Kr, blur, eta, ENVPARAMS);

Oi = Os; Ci *= Oi;

}

302 A Gallery of Procedural Shaders

Figure 12.7 The final ceramictiles shader. See also color plate 12.7.

12.4 Wood Grain

Our "theory" of wood is as follows:

The wood is composed of light and dark wood alternating in concentric cylinders surrounding a central axis. We will call the dark bands rings.

The rings are not, of course, perfect cylinders; they are warped in a variety of interesting ways.

Throughout the wood there is a high-frequency "grain" pattern of dark streaks. These grains are darker than the surrounding wood.

The grains are darker and more prevalent in the dark areas of the rings than in the light wood area.

For simplicity, the central axis of the rings will be taken to be the z-axis in "shader" space. To position the ring pattern relative to an object, the user should manipulate the "shader" space coordinate system.

/* culate the radius from the center. */

float r2 = sqr(xcomp(Pshad)) + sqr(ycomp(Pshad));

303

12.4 Wood Grain

float r = sqrt(r2) * ringfreq;

float inring = smoothpulsetrain (.1, .55, .7, .95, 1, r); color Cwood = mix (Clightwood, Cdarkwood, inring);

We have calculated a radius from the z-axis, multiplied by a ringfreq, and then alternated between light and dark wood colors according to a pulse train. We set up the pulse train so that we ramp from light to dark more slowly, then we drop from dark back down to light (this is purely for aesthetic reasons). The smoothpulsetrain function is analogous to the spulsetrain function from Section 11.3.2, but with

smoothstep:

float smoothpulse (float e0, el, e2, e3, x)

{

return smoothstep(e0,el,x) - smoothstep(e2,e3,x);

}

/* A pulsetrain of smoothsteps: a signal that repeats with a given

*period and is 0 when 0 <= mod(x/period,l) < edge, and 1 when

*mod(x/period,l) > edge.

*/

float smoothpulsetrain (float e0, el, e2, e3, period, x)

{

return smoothpulse (e0, el, e2, e3, mod(x,period));

}

This results in a perfectly regular pattern of concentric rings, as shown in Figure 12.8. Because we are philosophically opposed to regular patterns, we will "noise it up" in a variety of ways:

Warp the domain by using vector-based fBm to offset Pshad:

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

point Pring = Pshad + ringnoise*offset;

Add some low-frequency noise( ) of z to simulate the tree trunk not being quite aligned with the z-axis:

Pring += trunkwobble *

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

The final multiply by (1,1,0) squashes the z component of the vsnoise, thus offsetting only the x, y components, based on noise of the z component.

Add some noise( ) of the angle about the z-axis, to simulate rings that are not quite round:

r += angularwobble * smoothstep(0,5,r)

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

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