Part II The RenderMan Shading Language
Section 14 SHADERS AND FUNCTIONS
SHADERS AND FUNCTIONS
The Shading Language distinguishes between shadersand functions. Shaders are proce- dures that are referred to by RenderMan Interface procedures. Functions are procedures that can be called from within the Shading Language. The distinction between shaders and functions is primarily a consequence of different argument passing conventions.
14.1
Shaders
Shaders are introduced by the keywordslight, surface,volume, displacement, orimager and are followed by the name of the shader and the statements that comprise its definition. These keywords indicate the type of RenderMan Interface shader that is being defined. The RenderMan Interface uses the shader type to enforce a type match in subsequent calls to RenderMan Interface procedures. For example, it is illegal to declare a shader to be of typelightand then instance it usingRiSurface.
The arguments to a shader are referred to as itsinstance variables. All of these variables are required have default values, and are assumed to beuniformunless declared otherwise. Shader instance variable values can be changed when a particular shader is instanced from the RenderMan Interface. For example, consider the shader weird:
surface
weird( float a=0.5; varying float b=0.25 ) {
Ci = color (mod(s,a), abs(sin(a+b)), mod(b,t)); }
This surface shader may be referenced through the RenderMan Interface with theRiSurface
command. For example,
RiSurface( ”weird”, RI NULL );
Shader instance variable values can be changed from their defaults by passing their new values through the RenderMan Interface. This first requires that the type of the variable be declared. The declaration for weird would be:
RiDeclare( ”a”, ”uniform float” );
RiDeclare( ”b”, ”varying float” );
Of course, theRiDeclarecan be eliminated if “in-line declarations” are used (see Section 3). In either case, uniform instance variables can be set by passing them in theparameterlistof a shader instance. In the following example, ais redefined whileb remains equal to its default:
RtFloata = 0.3;
RiSurface( ”weird”, ”a”, (RtPointer)&a, RI NULL );
Shader variables can also be set in geometric primitives. For example, the weird shader variables could be set when defining a primitive:
RtFloata;
RtFloatbs[4];
RtPointPs[4];
RiPolygon(4, ”P”, Ps, ”a”, (RtPointer)&a, ”b”, (RtPointer)bs, RI NULL)
ais a single float andbis an array containing four values, since it is avaryingvariable. The standard variable”P”is predeclared to be of typevarying point.
If a geometric primitive sets a shader variable that is defined in none of the shaders as- sociated with that primitive, it is ignored. Variables that are set on geometric primitives override the values set by the shader instance.
Shader instance variables are read-only in the body of the shader, unless they are declared using theoutputkeyword:
displacement
lumpy ( float a=0.5; output varying float height=0 ) { float h = a * noise(P); P += h * normalize(N); N = calculatenormal(N); height = h; }
This displacement shader produces a lumpy surface and also saves the displacement amount in anoutputvariableheight. Output variables of one shader may be read by other shaders on the same primitive (see Section 15.8) in order to pass information between them.
14.2
Functions
Functions are similar to shaders except they can be called from shaders or other functions, but cannot be instanced from the RenderMan Interface. Functions in the Shading Language are also much like functions in C. For example, the following defines a functionnormalize that returns a unit vector in the direction V:
vector
normalize ( vector V ) {
return V/length(V); }
Function calls use the traditional C syntax, except for the issues noted below.
Functions may return any of the basic types (float, color, point, vector, normal, matrix, string). Functions may not return arrays, though they make take arrays as parameters. Functions must have exactly onereturnstatement, except for those functions declared with typevoid, which may not have any return statement.
Function parameters are passed by reference; in other words, parameters are not copied into private data areas of the function. Nevertheless, function parameters are not writable unless specified with theoutputkeyword. For example:
void
normalize inplace ( output vector V ) {
V = V/length(V); }
Functions in the Shading Language cannot be called recursively.
Functions may be polymorphic — that is, you may define multiple functions with the same name, as long as they take different types or numbers of arguments. Functions may even be polymorphic based on return type. For example:
float snoise (point p) {
return 2 * (float noise(p)) - 1; }
float snoise (float s, float t) {
return 2 * (float noise(s, t)) - 1; }
vector snoise (point p) {
return 2 * (vector noise(p)) - 1; }
Functions may be declared anyplace that it is legal to declare a variable or have any other language statement, including inside the body of a shader, inside another function, or in- side any other code block. A function may only be called within the lexical scope in which it is declared, and only after its declaration (the same rules that apply to using a declared variable). The only data that functions may access are those passed as parameters, except for variables declared with theexternkeyword, which indicates that the variable is in an outer scope rather than that local storage should be allocated. For example:
surface mysurf (float Knoise = 0.5;) /* parameter */ {
color shadernoise (point p) {
extern float Knoise;
return Knoise * color noise (transform (”shader”, p)); }
/* code for the shader */
Ci = shadernoise(P) * diffuse(faceforward(normalize(N),I)); }