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

Advanced_Renderman_Book[torrents.ru]

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

15.2Ray Marching Techniques

We already know that the point we are shading, P, is an intersection of the sphere and the ray with origin E and direction I. From there we can calculate the other end point by using the raysphere function presented in Chapter 9.

15.2.1Hypertexture Clouds

Once we have determined the basic segment to trace through, we need to think about generating the pattern we want inside the sphere-that is, our density function and color. For the cloud shader example we will use the fBm function described in Chapter 10. This seems to produce a very soft and detailed 3D fractal pattern. The cost is high for this function, as it uses noise() many times to calculate the hypertexture. For simplicity, we will use a constant color for the cloud.

While stepping through the ray, we sample the density and color functions. As mentioned in Perlin and Hoffert (1989), we base the strength of the density on the step size, so that when one changes the step size of the ray marcher, the image retains the same look.

We will also randomize the starting sample point of the ray march. The benefit of doing this is that your eye tends to notice the noise created by this less than the alternative quantization artifacts resulting from large discrete steps. This addition and the preceding formula allow you to produce quick test renders of what the final image will look like by rendering test images with large step sizes. The resulting test images will be noisy but will retain the same overall character as if you had used a small step size.

As the shader steps along the ray, it calculates the current running opacity total and the current color total. We use basic compositing and Blinn's model of lowalbedo atmospheric scattering. If the clouds produced are thin and do not selfshadow significantly, then we can stop here, merely varying the color function to get a wide variety of interesting images.

However, more realistic-looking clouds need self-shadowing. The expense of this is great, but if you have the means, the results can be much more interesting. Selfshadowing of the volume is achieved by casting a shadow ray back to the light for each sample. Each shadow ray itself marches from the sample point to the light position, calculating an opacity total along the shadow ray and using this as a multiplier for the total light reaching the sample point. This is illustrated by Listing 15.3, which is simply the smoke shader presented in Section 12.6 modified to work within a sphere rather than as an atmosphere, and to have the volume self-shadow as we described. An enhanced version of this technique was used to produce the images in Figure 15.3.

Figure 15.3 also includes some images of very dense clouds. To get backlighting effects, we add a light directly behind the clouds, and to get explosion-type effects, we place a light inside the cloud volume. You'll notice in Figure 15.4 that the denser areas of the cloud block light more, producing a nice effect that would be hard to get otherwise.

418 15 Volumetric Shaders for Visual Effects

Listing 15.3 shadowedclouds.sl a self-shadowing volume cloud shader.

/* shadowedclouds - self-shadowing volumetric clouds

*

*Params:

*Ka, Kd - the usual meaning

*radius - object space radius of the sphere in which the volume resides

*opacdensity - overall smoke density control as it affects its ability

*to block light from behind it.

*lightdensity - smoke density control as it affects light scattering

*toward the viewer.

*shadowdensity - smoke density control as it affects its ability to

*shadow iteself.

*stepsize - step size for integration

*shadstepsize - step size for self-shadowing ray marching

*noisefreq - frequency of the noise field that makes the volume

*/

#include "noises.h"

#include "filterwidth.h" #include "raysphere.h"

float volumedensity (point Pobj; float radius, noisefreq, stepsize;)

{

float density = 0.5 + 0.5 * fBm(Pobj*noisefreq, stepsize*noisefreq, 7, 2, 0.65);

/* Increase Contrast */

density = pow(clamp(density,0,1), 7.28); /* Fade At Edges Of Sphere */

density *= 1 - smoothstep (0.8, 1, length(Pobj)/radius); return density;

}

color volumecolor (point Pobj)

{

return color l; /* Trivial function - white clouds

}

float volumeshadow (point Pobj; vector Lobj;

float radius, density, noisefreq, stepsize)

{

float Oi = 0;

float Llen = length(Lobj); vector Iobj = normalize(Lobj); float t0, ti;

float hits = raysphere (Pobj, Lobj, radius, 1.Oe-4, t0, tl); float end = (hits > 0) ? t0 : 0; /* distance to march */ end = min (end, Llen);

float d = 0;

15 Volumetric Shaders for Visual Effects

419

 

15.2Ray Marching Techniques

Listing 15.3 (continued)

float ss = min (stepsize, end-d);

float last dtau = volumedensity (Pobj, radius, noisefreq, stepsize); while (d <= end) {

/* Take a step and get the scattered light and density */ ss = clamp (ss, 0.005, end-d);

d += ss;

float dtau = volumedensity (Pobj + d*Iobj, radius, noisefreq, stepsize);

float tau = density * ss/2 * (dtau + last dtau); Oi += (1-Oi) * (1 - exp(-tau));

last dtau = dtau;

}

return Oi;

}

color volumelight (point Pcur, Pobj;

float radius, density, noisefreq, stepsize)

{

color Lscatter = 0; illuminance (Pcur) { extern color Cl; extern vector L; color Cscat = Cl; if (density > 0)

Cscat *= 1 - volumeshadow (Pobj, vtransform("object",L), radius, density, noisefreq, stepsize);

Lscatter += Cscat;

}

return Lscatter * volumecolor(Pobj);

}

surface

shadowedclouds ( float Ka = 0.127, Kd = 1; float radius = 10.0;

float opacdensity = 1, lightdensity = 1, shadowdensity = 1; float stepsize = 0.1, shadstepsize = 0.5;

float noisefreq = 2.0;)

{

Ci = Oi = 0 ;

/* Do not shade the front of the sphere -- only the back! */ if (N.I > 0) {

/* Find the segment to trace through. The far endpoint is simply

*The other endpoint can be found by ray tracing against the

*sphere (in the opposite direction).

*/

point Pobj = transform ("object", P);

vector Iobj = normalize (vtransform ("object", I)); float t0, t1;

voronoi_f1_3d
density_func

420 15 Volumetric Shaders for Visual Effects

Listing 15.3 (continued)

float hits = raysphere (Pobj, -Iobj, radius, 1.Oe-4, t0, ti); float end = (hits > 0) ? t0 : 0; /* distance to march */ point origin = Pobj -t0*Iobj;

point Worigin = transform ("object", "current", origin);

/* Integrate forwards from the start point */ float d = random()*stepsize;

/* Calculate a reasonable step size */ float ss = min (stepsize, end-d);

point Psamp = origin + d*Iobj;

float last dtau = volumedensity (Psamp, radius, noisefreq, stepsize); color last li = volumelight (transform ("object", "current",

Psamp), Psamp, radius, shadowdensity, noisefreq, shadstepsize);

while (d <= end) {

/* Take a step and get the scattered light and density */ ss = clamp (ss, 0.005, end-d);

d += ss;

/* Get the scattered light and density */

Psamp = origin + d*Iobj;

float dtau = volumedensity (Psamp, radius, noisefreq, stepsize); color li = volumelight (transform ("object", "current", Psamp),

Psamp, radius, shadowdensity, noisefreq, shadstepsize);

float tau = opacdensity * ss/2 * (dtau + last_dtau);

color lighttau = lightdensity * ss/2 * (li*dtau + last li*last dtau);

/* Composite with exponential extinction of background light */ Ci += (1-0i) * lighttau; Oi += (1-0i) * (1 - exp(-tau));

last dtau = dtau; last_li = li;

}

}

}

Finally, there is no limit to what type of density functions you can place in the cloud shader. As an example, Listing 15.4 gives alternative implementations of

and color_func. Placing the voronoi_fl_3d of Section 10.5 into the density function allows us to make images like those shown in Figure 15.5. Neat effects can be created by modifying the space of the sample point before passing it into the function.

15.2Ray Marching Techniques

Basic unshadowed clouds.

Same as above, but shadowed.

Dense clouds.

By placing lights behind the volume you can acheive very nice backlighting effects.

Figure 15.3 Basic cloud effects.

422 15 Volumetric Shaders for Visual Effects

Figure 15.4 Explosion cloud. See also color plate 15.4.

Figure 15.5 Cell clouds. See also color plate 15.5.

423

15.2Ray Marching Techniques

Listing 15.4 Alternative density and color functions resulting in "cell clouds."

float volumedensity (point Pobj; float radius, noisefreq, stepsize; output point cellcenter)

{

float density;

voronoi_f1_3d (Pobj*noisefreq, 1, density, cellcenter); density = 1 - density;

/* Increase Contrast */

density = pow(clamp(density,0,1), 7.28);

/* Fade At Edges Of Sphere */

density *= 1 - smoothstep (0.8, 1, length(Pobj)/radius); return density;

}

color volumecolor (point Pcenter)

{

return color "hsv" (cellnoise(Pcenter), 1, 1);

}

/* We then replace calls to:

*den = volumedensity (Pobj, radius, noisefreq, stepsize);

*with:

*point cellcenter;

*den = volumedensity (Pobj, radius, noisefreq, stepsize, cellcenter);

*and replace:

*volumecolor(Pobj)

*with:

*volumecolor(cellcenter)

*/

15.2.2Solid Hypertextures

Logically, if you can produce clouds it shouldn't be that hard to produce solids using the same basic idea. We use the same basic model Perlin presented in Perlin and Hoffert (1989), which assumes that a solid surface exists wherever the density function is at a particular value, as illustrated in Figure 15.6. We begin with the following algorithm:

Calculate segment to ray trace through. Set starting point

while ( current point is behind final point ) Call density function

Calculate normal

Calculate shading based on normal

424 15 Volumetric Shaders for Visual Effects

Figure 15.6 Rendering with the solid shader.

Update current color total and opacity total

Calculate next sample point along ray using specified step size

end

Set surface Ci/Oi

The basic addition to the original algorithm is the inclusion of a normal calculation for each point along the ray. We can obtain a normal for the "surface" by calculating the gradient of the density field. This gradient can be approximated numerically by:

normal compute_normal (point sampleP; float den)

{

normal norm;

norm = normal ( density_func(sampleP - vector (0.001,0,0)) - den, density_func(sampleP - vector (0,0.001,0)) - den, density_func(sampleP - vector (0,0,0.001)) – den );

return normalize(norm);

}

The parameter samplePoint is the current sample point we wish to obtain a normal for, and the den parameter is the previously calculated density at that sample point.

425

15.2 Ray Marching Techniques

This trick works rather well, but there are several pitfalls that should be pointed out. The first is that if you are using a density function with discontinuities (abrupt changes in otherwise smooth transitions between the values of your density function), then it is best to try to pull the discontinuities out for this normal calculation; otherwise, the results will not be what you expect.

In the solid shader example, I really wanted a density function that changes from 0.0 to 1.0 instantly, creating a discontinuity in the density function. To ensure that the normal calculated is correct, I remove this discontinuity from the density function that the calculate_normal function uses. This works great and solves the problem fairly easily. A second tip is that at the boundary of the shape you are tracing through, you will want to use the normal of the surface geometry; otherwise, you will probably get unwanted normal directions at the boundary edges. One final tip is to use smoothstep to transition between those discontinuities in your density function. This will also help to eliminate unwanted aliasing artifacts.

We can modify shadowedclouds shader to produce a solid volume (see Listing 15.5). This shader runs a great deal faster than the cloud shader simply because the ray does not have to be traced through the entire volume (it can stop when the "surface" is detected). Also, we can skip normal calculations when the density is zero, because no light will be reflected there anyway. In the case of the solid shader, it is silly to trace through the solid to calculate the self-shadowing because shadow maps were designed specifically for this case. Lastly, don't forget that the diffuse( ) and specular( ) functions given by RenderMan return light arriving at P, so we need to calculate the diffuse lighting ourselves and use the specularbrdf( )function for specular lighting calculations.

15.2.3Flying through Volumes and Combining Volumes of Basic Shapes

It is possible to fly through the cloud shader. Simply modify the use of raysphere to take into consideration the case where the camera is inside the sphere. This is the reason we are shading the backside of the sphere-so that if the camera enters the sphere itself, the volumetric effect will continue to work. Using this technique, you can create animations of cloud fly-throughs. However, the expense is high because as you get closer to the volume, your step size must decrease in order that it not be apparent that the pixel-to-pixel spacing is vastly smaller than the depth spacing of the volume samples. The ability to fly through the volume opens the door to many more possible uses of the shader. Note that you will want to set up a clipping plane in the shader in order to fade the density function off close to the camera. This will create a more expected rate of change when flying through cloud volumes. The example picture in Figure 15.7 was taken from within the volume of such an inside-the-sphere cloud shader.

It is also possible to combine solid volumetric shapes to form more complex shapes (transparent textures are a lot more difficult to join). With solid textures,

426 15 Volumetric Shaders for Visual Effect

Listing 15.5 hypertexture to make the cloud surface solid

/* hypertexture - self-shadowing solid clouds

*Params:

*Ka, Kd, Ks - the usual meaning

*radiusobject space radius of the sphere in which the volume resides

*opacdensity - overall smoke density control as it affects its ability

*to block light from behind it.

*lightdensity - smoke density control as it affects light scattering

*toward the viewer.

*shadowdensity - smoke density control as it affects its ability to

*shadow iteself.

*stepsize - step size for integration

*shadstepsize - step size for self-shadowing ray marching

*noisefreq - frequency of the noise field that makes the volume

*threshthreshold value for the solid boundary of the hypertexture

*/

#include "noises.h" #include "filterwidth.h #include "raysphere.h"

float volumedensity (point Pobj; float radius, noisefreq, stepsize;)

{

float density = 0.5 + 0.5 * fBm(Pobj*noisefreq, stepsize*noisefreq,

3, 2, 0.6);

/* Fade At Edges Of Sphere */

density *= 1 - smoothstep <0.8, 1, length(Pobj)/radius); return density;

}

color volumecolor (point Pobj; float stepsize)

{

#pragma nolint 2

return color (0.5 + 0.5*filteredsnoise(Pobj, stepsize), 0.75,

0.5 + 0.5*filteredsnoise(Pobj-10, stepsize));

}

float volumeshadow (point Pobj; vector Lobj;

float radius, density, noisefreq, stepsize, thresh)

{

float Oi = 0;

float Llen = length(Lobj); vector Iobj = normalize(Lobj); float t0, t1;

float hits = raysphere (Pobj, Lobj, radius, 1.Oe-4, t0, tl); float end = (hits > 0) ? t0 : 0; /* distance to march */

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