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

Advanced_Renderman_Book[torrents.ru]

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

182 7 Introduction to Shading Language

Shading Language functions obey standard variable lexical scope rules. Functions may be declared outside the scope of the shader itself, as you do in C. By default, SL functions may only access their own local variables and parameters. However, this can be extended by use of an extern declaration-global variables may be accessed by functions if they are accessed as extern variables. Newer SL compilers also support local functions defined inside shaders or other functions-that is, defined anyplace where a local variable might be declared. In the case of local functions, variables declared in the outer lexical scope may be accessed if they are redeclared using the extern keyword. Following is an example:

float x, y;

 

float myfunc (float f)

 

float x;

/* local hides the one in the outer scope */

extern float y;

/* refers to the y in the outer scope */

extern point P;

/* refers to the global P */

Further Reading

More formal documentation on the RenderMan Shading Language can be found in the RenderMan Interface Specification (Pixar, 1989) as well as Upstill (1990) and Hanrahan and Lawson (1990).

Discussions of color spaces can be found in Foley, van Dam, Feiner, and Hughes (1990), Hall (1989), or Glassner (1995).

Texture Mapping and

Displacement

A common technique to make computer-generated imagery more realistic is to apply a scanned or painted image like a "decal" to a surface, thus providing fine color detail on an otherwise plain-looking surface. This is known as texture mapping (see Figure 8.1).

Figure 8.1 Texture mapping applies an image file "decal" to geometry. See also color plate 8.1.

184 8 Texture Mapping and Displacement

Listing 8.1 paintedplastic.sl:the standard “painted plastic” shader. surface

paintedplastic ( float Ka = 1, Kd = .5, Ks = .5, roughness = color specularcolor = 1;

string texturename = ""; )

{

color Ct = Cs;

if (texturename != "")

Ct *= color texture (texturename); normal Nf = faceforward (normalize(N),I); vector V = -normalize(I);

Ci = Ct * (Ka*ambient() + Kd*diffuse(Nf)) + specularcolor * Ks * specular(Nf,V,roughness);

Oi = Os; Ci *= Oi;

8.1Texture Access in Shading Language

In Shading Language, basic texture mapping is accomplished with the texture () function (items in curly braces are optional):

type texture ( filename { [ firstchannel ] } { , coords } { , params } )

The only required parameter is the filename, which is a string giving the name of the disk file containing the texture. The following subsections will describe the various optional parameters.

An example shader that uses the texture () function is paintedplastic, shown in Listing 8.1. Notice that it is much like the plastic.sl shader shown in Listing 7.1. Rather than using the default surface color Cs as the base color of the material, paintedplastic "filters" the base color by the color looked up from the texture map.

8.1.1Texture Return Type

The return type of texture () may be either a float or a col or. These return types would indicate that either one or three channels, respectively, should be read from the texture file. You could explicitly ask for a oneor three-channel texture lookup, as in

f =float texture ("foo.tex"); /* explicit float lookup */ c = color texture ("bar.tex"); /* explicit color lookup */

Alternatively, if you do not explicitly specify the type, the SL compiler will try to infer the type depending on how you are using the resulting data. This is inherently

8.1

185

Texture Access in Shading Language

ambiguous, dangerous, and strongly discouraged! For example, consider the following:

Ci = Cs * texture ("foo.tex");

Does this line read a single channel used to scale the surface color, or does it read three channels of texture to filter the surface color? There is no way that the SL compiler can tell what your intent was, so its best guess could be wrong. In addition, it's possible that different SL compilers could guess differently. For these reasons, let us again urge you to always use the explicitly cast texture calls, as in the preceding examples.

8.1.2Selecting Channels

By default, texture() returns channel 0 (if cast to float) or channels 0, 1, and 2 (if cast to a color). It is also possible to request that the texture lookup begin with some other channel of the texture file. This can be done by specifying the channel number in square brackets immediately after the filename. For example,

f = texture ("foo.tex"[1]);

One use for this feature would be if you are texture-mapping a four-channel texture file-the first three channels are color, and the fourth channel is alpha (coverage). Consider the case where you want to composite the texture over the existing base color. In other words, an alpha of 0 should indicate that no paint is applied, not that black paint is applied. The following shader code fragment implements texture mapping with alpha compositing (assuming that the texture file colors are premultiplied by the alpha values):

/* Assuming Ct contains the base color of the object... */

C

=

texture

(file);

/*

color

in

channels 0-2

*/

a

=

texture

(file[3]);

/*

alpha

in

channel 3 */

 

Ct = C + (1-a) * Ct;

/* Now Ct is the color of the object with paint applied */

Notice that the syntax for selecting texture channels looks suspiciously like the syntax for accessing array elements. This is because texture channels were in the language long before arrays were allowed, and so the potential confusion was overlooked. When arrays were added, it created a grammatical ambiguity. The SL compiler is pretty good at sorting out the difference but still can have difficulties if the name of the texture is an array variable, as in the following example:

string filenames[10]; Ci = float texture ((filenames[i])[1], s, t);

In this example, we have an array of strings, and we are accessing channel 1 from the ith filename. To avoid ambiguities, we have surrounded the filename specifier

186 8 Texture Mapping and Displacement

by parentheses, clearly delineating the array access (inside the parentheses) from the channel selection (outside the parentheses).

8.1.3Texture Coordinates

Stored image file textures are fundamentally 2D and are thus indexed by a pair of numbers: horizontal and vertical offsets from the upper-left corner of the texture, indexed from 0 to 1, called texture coordinates. There are three forms of the texture() call. Channel selections, explicit return type casts, and optional texture arguments may be used with any of these forms. For simplicity, the examples of the three forms of texture() calls are shown unaugmented by these variations.

texture (filename, q, r)

When two floating-point arguments are given as texture coordinates, these numbers are used as the texture lookup coordinates. Furthermore, the renderer will automatically figure out the range of a and r over the shading unit (pixel, micropolygon, sample, etc.) and return the average of the texture over the resulting area in texture space, thus giving you a filtered texture lookup. Chapter 11 will give more details on how you can antialias your own shaders and functions, but this form of the texture() call is one way in which the renderer will automatically perform antialiasing for you.

texture (filename)

Geometric primitives have both 2D parametric coordinates n, v (typically ranging from 0 to 1 in each direction) and also standard texture coordinates s, t. By default, s = u and t = v, but this can be overridden in the RIB file. In either case, for many primitives s, t already contains a well-defined 2D decallike mapping on the primitive and thus can be used as a convenient source of texture coordinates.

If no texture coordinates are supplied at all, the compiler will assume that you want to map based on the standard texture coordinates s, t. Texture filtering is performed as we have described, based on s, t.

texture (filename, q0, r0, q1, r1, q2, r2, q3, r3)

If eight texture coordinates are supplied, they are taken to describe a quadrilateral in texture space over which the texture is filtered. Thus, in this form you are explicitly specifying the filter region, rather than allowing the renderer to compute it. (One explanation of the one-point version of texture() is that the renderer will figure out the appropriate four points given your center point and the derivatives of its coordinates.) This four-point version of textu re () is more rarely used than the single-point version, but occasionally you will want its high degree of control.

8.1

Texture Access in Shading Language

187

 

Listing 8.2 simpletexmap. sl: texture mapping with some extremely basic texture placement.

surface

simpletexmap (float Ka=1, Kd=1, Ks=0.5; float roughness = 0.1; color specularcolor = 1; string texturename = " "; float sstart = 0, sscale = 1; float tstart = 0, tscale = 1; )

{

/* Simple scaled and offset s-t mapping */ float ss = (s -sstart) / sscale;

float tt = (t - tstart) / tscale;

/* Look up texture if a filename was given, otherwise use the default surface color. */

color Ct;

if (texturename != " " {

float opac = float texture (texturename[3], ss, tt);

Ct = color texture (texturename, ss, tt) + (1-opac)*Cs;

}

else Ct = Cs;

/* Simple plastic-like reflection model */ normal Nf = faceforward(normalize(N),I); vector V = -normalize(I);

Ci = Ct * (Ka*ambient( ) + Kd*diffuse(Nf))

+ Ks*specularcolor*specular(Nf,V,roughness); Oi = Os; Ci *= Oi;

}

Listing 8.2 shows an example shader that performs slightly more flexible texture mapping than the standard paintedplastic.sl shader of Listing 8.1. The simpletexmap shader contains two extensions to paintedplastic: (1) rather than simply filtering the surface color by the texture color, it uses an

alpha channel in the texture to properly composite the texture over the surface color; and (2) parameters allow the texture to be offset and scaled on the surface (though it remains aligned with the surface's s, t coordinates).

We have thus far shown techniques for simple s, t decal mapping where we simply filter the surface color by the texture lookup or apply the RGBa texture over the surface color. Not only do you have total freedom in mapping the texture onto the surface but you may use the resulting data from the texture files in any way you choose. Examples include the following:

188 8 Texture Mapping and Displacement

Figure 8.2 Texture mapping with additive blur (compare to Figure 8.1): blur = 0.02 (left); blur = 0.1 (right). See also color plate 8.2.

a shader parameter that swaps s and t for the mapping, rotates the texture on the surface, or even allows an arbitrary affine mapping from s, t space to texture lookup coordinates

multiple, possibly overlapping, texture map files affecting the surface

handling both associated and unassociated alpha texture files

using texture maps to modify the surface color in ways other than filtering and simple compositing-for example, in addition to specifying a base color, texture maps could be used to control opacity, specularity, roughness, or other surface parameters

using texture maps to modulate the transition between radically different surface types-for example, metal versus rust

8.1.4Optional texture() Arguments

In addition to texture filename, channel selection, and texture lookup coordinates, individual renderers allow additional parameters for fine control over some aspects of texture lookups. These additional parameters are specified as additional tokenvalue pairs passed to texture() after the texture coordinates. Each token is a string, and values are typically floating-point numbers.

Blurred Texture Lookup

At times, you may want to blur your texture lookup. This can be accomplished with an additive "blur" parameter, measured relative to the texture image size (see Figure 8.2). For example:

texture (filename, s, t, "blur", 0.01);

This example adds a blur of 1% of the image width, which is equivalent to preblurring the texture by a filter kernel that is 1% of the width of the image. The default additive blur value is 0, indicating that no preblurring should be done to the texture.

189

8.1 Texture Access in Shading Language

Filter Width Multiplier

We described earlier how texture lookups are automatically antialiased, either explicitly (by specifying all four corners of a quadrilateral in texture space) or implicitly (by giving a single s, t coordinate pair and letting the renderer deduce how much it changes from pixel to pixel). In the latter case, you may want to make the implicit texture lookups sharper or more blurry in order to achieve some particular effect. The filter width estimate can be scaled by multiplication using the "width" parameter, taking a floating-point multiplier as a value:

texture (filename, s, t, "width", 2); texture (filename, s, t, "width", 0);

texture (filename, s, t, "swidth", 1, "twidth", 4);

The first example doubles the filter width. This results in antialiasing the texture lookup approximately as if the image resolution was cut in half (or the ShadingRate was multiplied by 4). The middle example multiplies the filter width by zero, effectively point sampling the texture (or coming as close to point sampling as the renderer can). The third example shows how "swidth" and "twidth" parameters can be used to set the filter width multipliers differently for the s and t coordinates. Note that the default value for width multipliers is 1, meaning that the original estimates should be used unmodified.

Note the subtle difference between the additive "blur" and the multiplicative "width" parameters. The former is absolute (at least for fixed texture content), whereas the latter is relative, adapting to the shading rate and thus, indirectly, to the output image resolution and apparent object size. In general, if you want a blurry texture lookup, you should use "blur", but if you want to second-guess the renderer's filter size estimates, then you should use "width".

Texture Filter Selection

We have mentioned filtering of textures but have not specified which filter will be used. By default, a box filter is used by the renderer. However, for more fine control, you may specify which filter to use for the texture lookup:

texture (filename, s, t, "filter", "box"); texture (filename, s, t, "filter", "gaussian");

Note that the selection of valid filter names is implementation dependent; consult your renderer documentation for supported filters.

Fill Color for Missing Channels

What happens when you ask for color texture on a file that contains only a single channel, or when you ask for a channel number higher than exists in the file? In these cases, the renderer will "fill in" the missing channels for you with a constant value. The optional "fill" parameter takes a float argument that specifies the constant used to fill in missing channels.

190 8 Texture Mapping and Displacement

The default fill value is 0. If you examine the simpletexmap.sl shader, you might become suspicious about what happens if the texture file does not contain an alpha channel. In that case, the texture call would return 0, indicating that the texture was transparent everywhere: exactly the opposite of what you want-it would be better for textures with missing alpha channels to appear to be fully opaque everywhere. This can be accomplished by changing a single line of the shader:

float opac = float texture (texturename[3], ss, tt, "fill", 1);

Note that the fill value applies to entire channels that are not present in the texture file and is unrelated to the issue of what happens when you address texture coordinates outside the [0, 1] range. The latter issue is handled separately at texture-generation time and is described in Section 8.1.6.

8.1.5Texture Coordinates and Conditionals

Be careful with textures inside of conditionals, for example:

if (varying-condition) {

ss= some formula ;

tt= another formula ;

Ci = texture ("foo", ss, tt);

}

If the condition is varying (able to take on different true/false values at different points on the surface), then this statement will have unpredictable results. This is because, as we mentioned earlier, the two-coordinate texture lookup tries to estimate how much the texture coordinates change over the shading unit (pixel, micropolygon, etc.) and will filter the texture accordingly. But consider a shading unit that straddles the boundary between where the condition is true and where it is false. The ss and tt variables are well defined only on one side of the boundary, because the assignments to ss and tt are inside the "true" clause of the i f statement. They are uninitialized on the "false" side of the boundary. Thus, it is not possible for the renderer to determine how these variables change over the straddling shading element, and texture filtering will fail unpredictably.

Therefore, it is important to remember that the texture coordinates must be well defined on the entire surface. One solution is to simply move the definitions of the texture lookup coordinates outside the conditional itself. For example:

ss= some formula ;

tt= another formula

if (varyingcondition) {

Ci = texture ("foo", ss, tt);

}

191 8.1 Texture Access in Shading Language

In this example, even though the texture() function is called only at points where the condition is true, the texture coordinates are well defined everywhere, so there are no problems at the boundary conditions.

Note that the original idiom is completely safe if the condition consists only of uniform quantities (such as numerical constants and uniform variables). This guarantees that the condition is either always true or always false and, hence, that there is never a boundary between true and false regions. No boundary, no problems.

For similar reasons, all Shading Language functions that implicitly take derivatives or filter their

results will have the same limitation. The environment, Du(), Dv(), filterstep(), area(), and

calculatenormal() functions all should be avoided when inside varying conditionals or loops. (These functions are described in Sections 8.2, 9.2.1, and 11.2.)

8.1.6 Creating Texture Files

The format for texture files is not dictated by the RenderMan Interface Specification so each renderer is likely to have its own idiosyncratic methods for texture generation and storage, often involving custom texture formats that are designed for efficient access. Thus, a preprocessing step may be necessary to turn your scanned or painted image into a texture file. This can be done from the procedural API or RIB by using the MakeTexture command described in Section 3.7.1. Alternatively, most renderers will come with a separate program that converts ordinary image files (says, in TIFF format) into texture files.

PRMan requires that all textures be in a special, proprietary texture format. The txmake program that comes with PRMan will convert a variety of image file formats into PRMan texture files:

txmake [arguments] inputfile outputfile

Several optional arguments are occasionally useful:

-mode modename

Determines the "wrap mode", or what value the texture takes on for values of s and t outside the range [0,1]. If the modename is black (the default), out-of-range texture lookups will return black for all channels. A modename of clamp treats out-of-range texture lookups as if they happened right on the border of the texture-in other words, the texture extends its border values to infinity. Finally, the modename periodic will cause the texture to simply wrap.

The -mode flag specifies the wrapping behavior for both the s and t directions, but you could specify them separately using the -smode and -tmode flags.

-resize resizemode

The use of this flag is somewhat esoteric, but we strongly recommend that you use resizemode of up-. (Yes, that's "up dash.") The several other options are explained in the PRMan documentation.

-short -float

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