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

Advanced_Renderman_Book[torrents.ru]

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

253

10.4 Fractional Brownian Motion and Turbulence

Listing 10.1 fBm: shader code for fractional Brownian motion.

float fBm (point p; uniform float octaves, lacunarity, gain)

{

varying float sum = 0, amp = 1; varying point pp = p;

uniform float i;

for (i = 0; i < octaves; i += 1) sum += amp * snoise(pp);

amp *= gain; pp *= lacunarity;

}

return sum;

}

vector vfBm (point p; uniform float octaves, lacunarity, gain)

{

uniform float amp = 1; varying point pp = p; varying vector sum = 0; uniform float i;

for (i = 0; i < octaves; i += 1) { sum += amp * vsnoise (pp);

amp *= gain; pp *= lacunarity;

}

return sum;

}

float turbulence (point P; uniform float octaves, lacunarity, gain)

{

varying float sum = 0, amp = 1; varying point pp = p;

uniform float i;

for (i = 0; i < octaves; i += 1) { sum += amp * abs (snoise(pp)); amp *= gain; pp *= lacunarity;

}

return sum;

}

254 10 Pattern Generation

Changing the frequency. Going from left to right, each successive image doubles the base frequency of the previous one.

Maximum numbers of octaves, left to right: 1, 2, 3, 4, 5.

Adjusting the lacunarity, left to right: 0.5, 1, 2, 4, 8.

Adjusting the gain, left to right: 0.2, 0.5, 0.75, 1, 1.5. The gain parameter changes the mix of frequencies, with larger gain emphasizing higher frequencies.

Figure 10.6 Exploring the parameter space of the fBm function. For all examples, parameters that did not vary were assigned oct = 6, lac = 2, gain = 0.5.

255

10.5 Cellular Patterns

Figure 10.7 fBm (left); turbulence (right). Notice that the turbulence function appears more "billowy."

may be particularly tricky to antialias.) Figure 10.7 shows a comparison of fBm and turbulence in 2D.

10.5Cellular Patterns

Section 10.4 explains fractional Brownian motion and "turbulence" in detail. These functions are useful because they may be used as basis functions for procedural texture synthesis. In other words, they are not in and of themselves final patterns but are very useful as building blocks for procedural textures. Steve Worley introduced another basis function in Worley (1996). Worley's texture basis functions can be used to generate patterns that resemble cellular divisions-cobblestone patterns, lizard scales, and so on. The gist of Worley's technique is to picture space as containing a random distribution of "feature points." Knowing which of these feature points is closest to your shading position (and possibly knowing about the 2ndor 3rd-closest points) yields information that turns out to be surprisingly useful for generating natural-looking patterns. Let us then develop a Shading Language implementation of such a function.

First, let's suppose that space is divided up into 1 x 1 x 1 cells, with a "feature point" somewhere in each cell. Thus, for any point P, the center of the cell P occupies is at

point cellcenter = point ( floor(xcomp(P))+0.5, floor(ycomp(P))+0.5, floor(zcomp(P))+0.5);

256 10 Pattern Generation

To randomly place a feature point inside the cell, we will add an offset to the center

point using cellnoise( ):

point featurepos = cellcenter +

(vector cellnoise(cellcenter) - 0.5);

Remember that the vector cellnoise( ) function will return a vector that is constant over a single cell (whose boundaries are the integers) but will differ from cell to cell. The range of cellnoise() is such that each component is on (0, 1); we subtract 0.5 so that the offset is in the range (-0.5, 0.5).

The following figure illustrates the jittering of feature positions. The unjittered grid is shown on the left; the features in their jittered positions are shown on the right.

Now that we can compute the feature position within any individual cell, we want to figure out which feature is nearest to us. Since the jittered points stay within their original cells (i.e., the jitter amounts are in the range (-0.5, 0.5)), we can simply loop over the neighboring cells, computing the feature position for each of those cells and comparing each one's distance to P:

point thiscell = point ( floor(xcomp(P))+0.5, floor(ycomp(P))+0.5, floor(zcomp(P))+0.5);

dist to_nearest = 1000; /* initialize to too large */ for (i = -1; i <= 1; i += 1) {

for (j = -1; j <= 1; j += 1) {

for (k = -1; k <= 1; k += 1) {

point testcell = thiscell + vector(i,j,k); point pos = testcell +

vector cellnoise (testcell) - 0.5; float dist = distance(pos,P);

if (dist < dist to_nearest) {

257

10.5 Cellular Patterns

dist to_nearest = dist; nearestfeature = pos;

}

}

}

}

This code fragment stores the position of the feature nearest to P in nearestfeature and the distance to that feature in dist_to_nearest.

Listing 10.2 formalizes this code, giving the complete shader function that returns both the nearest and second-nearest features. It also contains a "jitter" parameter that scales the amount of possible jitter in the feature positions within their cells. Outputs include the distances to the closest and second-closest features (fl and f2) and the positions of those two features (pos1 and pos2). Note that you can also create a version that is optimized for the 2D case.

Now that we have this magic function, what can we do with it? Let us summarize some potential uses:

Because each feature exists in a unique cell, a cellnoise( ) lookup for the feature position will also be unique. For example, consider the following code fragment:

voronoi_f1f2_3d (P, 1, fl, posl, f2, pos2); Ci = color cellnoise (pos1 + vector(10,0,0));

This will assign a surface color to P that depends on the closest feature. The effect is shown in Figure 10.8(a) and is the basis for forming cellular patterns. This figure alone should provide adequate explanation for why we call this function "Voronoi." (Examining the code reveals why we don't call it "Worley"-we differ

Lasting 10.2 voronoi_f1f2_3d function computes the distances and positions of the two nearest features in 3D space.

/* Voronoi cellnoise (a.k.a. Worley noise) -- 3D, 1- and 2-feature version.

*Inputs:

*P - the domain point

*fitter - how much (in the range 0-1) to fitter feature positions

*Outputs:

*f1 - distance between P and the closest feature point

*f2 - distance between P and the second-closest feature point

*posl, post - positions of the closest and 2nd-closest feature points

*/

void

voronoi_f1_3d (point P; float fitter;

output float f1; output point pos1;)

{

point thiscell = point (floor(xcomp(P))+0.5, floor(ycomp(P))+0.5, floor(zcomp(P))+0.5);

258 10 Pattern Generation

Listing 10. 2 (continued)

f1 = 1000;

uniform float i, j, k;

for (i = -1; i <= 1; i += 1) {

for (j = -1; j <= 1; j += 1) {

for (k = -1; k <= 1; k += 1) {

point testcell = thiscell + vector(i,j,k); point pos = testcell +

jitter * (vector cellnoise (testcell) - 0.5); vector offset = pos - P;

float dist = offset . offset; /* actually dist^2 */ if (dist < fl) {

f1 = dist; pos1 = pos;

}

}

}

fl = sqrt(f1);

void

voronoi_f1f2_3d (point P; float fitter;

output float f1; output point pos1; output float f2; output point pos2;)

{

point thiscell = point ( floor(xcomp(P))+0.5, floor(ycomp(P))+0.5, floor(zcomp(P))+0.5);

f1 = f2 = 1000; uniform float i, j, k;

for (i = -1; i <= 1; i += 1) {

for (j = -1; j <= 1; j += 1) {

for (k = -1; k <= 1; k += 1) {

point testcell = thiscell + vector(i,j,k); point pos = testcell +

jitter * (vector cellnoise (testcell) - 0.5); vector offset = pos - P;

float disc =offset . offset; /* actually dist^2 */ if (dist < fl) {

f2 = fl; pos2 = pos1;

f1 = dist; pos1 = pos; } else if (dist < f2) {

f2 = dist; pos2 = pos;

}

}

}

}

f1 = sqrt(fl); f2 = sqrt(f2);

}

259

10.5Cellular Patterns

Figure 10.8 Basis functions of the Voronoi cellular function. (a) assigning a color based on the position of the nearest feature (top left); (b) the value of f1, the distance to the nearest feature (top right); (c) the value of f2, the distance to the second-closest feature (bottom left); (d) the difference of f2 - f1 (bottom right).

in implementation from Worley's description and take several shortcuts.) We add an integer offset to our cellnoise lookup because the feature position within the cell was determined by a cellnoise lookup based on that point. We do not want any correlations between the color and feature position, so it's wise to use a different offset for each offect that is being keyed off of the feature position.

The values of fl and f2 themselves make interesting patterns. Figure 10.8b and c show these raw outputs, respectively.

The difference fl - f2, as shown in Figure 10.8d.

260 10 Pattern Generation

Figure 10.9 Thresholding Voronoi f2 – f1 to find cell boundaries. From left to right: (a) naive thresholding of f2 – f1; (b) more careful thresholding of f2 – f1 to produce evenly wide strips; (c) using fBm to make the borders jagged.

Notice that fl - f2 is zero exactly on the boundaries of two cells because on the boundaries we are equally distant from two features. We can apply a threshold to this value, as shown in Figure 10.9a and embodied by the following code fragment:

voronoi_f1f2_3d (P, 1, f1, pos1, f2, pos2); Ci = step (0.05, f2-f1);

But as you can see from Figure 10.9a, this naive thresholding approach has the unfortunate property that the separator lines are unequal in width. This is because the features themselves are unevenly spaced, and therefore a particular threshold will represent a different width for each feature pair. We can correct for this, as shown in Figure 10.9b and in the code fragment below (deducing exactly why this correction factor works is left as an exercise for the reader).

voronoi_flf2_3d (P, 1, f1, pos1, f2, pos2); float scalefactor = distance(posl,pos2) /

(distance(pos1,P)+distance(P,pos2)); Ct = step (0.05*scalefactor, f2-f1);

We can make the borders more ragged by adding fBm to the input position, as shown in Figure 10.9c:

point PP = P + 0.15 * vfBm(2*P, 4, 2, 0.5); voronoi_f1f2_3d (PP, 1, f1, pos1, f2, pos2); float scalefactor = distance(pos1,pos2) /

(distance(pos1,P)+distance(P,pos2)); Ct = step (0.05*scalefactor, f2-f1);

261

Further Reading

Further Reading

Other sources for algorithms and applications of noise can be found in Lewis (1989) and Perlin and Hoffert (1989). Readers interested in computation and uses of noise are especially encouraged to read Ebert, Musgrave, Peachey, Perlin, and Worley (1998).

Shader Antialiasing

Everybody has experienced it: sharp jaggies, pixellation artifacts, creepy-crawlies as the camera moves, horrible twinkling, or just plain weirdness when you pull back from your favorite procedural texture. It's aliasing, and it's a fact of life when writing shaders. This chapter is designed to make the problem seem less scary and to give you the conceptual tools to attack the problem on your own. It's not the complete compendium of antialiasing techniques, but it should get you started.

11.1 Sources of Aliasing in Shading

What is aliasing, really? The heart of the problem lies in the domain of signal processing. Fundamentally, our conceptual model is that there is a continuous image function in front of the camera (or alternatively, impinging on the film plane). But we need to represent this function using a raster of discrete pixels in our image,

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