Containers, Types, and Variables

Containers, Types, and Variables


RSL supports the following containers:


The Shading Language supports one-dimensional arrays of all the basic data types. Zero and negative-length arrays are not permitted.

As in C, the syntax for declaring a fixed size array of any data type uses square brackets, i.e. datatype[length]. length is a float constant, which is rounded down to generate an integer length. The syntax for specifying the data of a constant array uses curly braces: {value1, value2, ...}. For example:

float a[10];
uniform color C[4], float b[4] = {3.14, 2.17, 0, -1.0};

As in C, individual array elements may be referenced using the familiar square bracket notation. An array element index is a float expression, which is rounded down to generate an integer index. Shading Language arrays perform over/underrun checking at run-time. Unlike C, arrays can be assigned to each other as long as they are of the same type and length. Entire arrays can also be compared for equality.

Shader input parameters and formal parameters of functions may be declared with an unspecified length (i.e. empty square brackets); the length of such arrays is determined by the actual parameter value. The default value of a shader parameter whose length is unspecified must be an empty array.

The length of an array can be determined using the arraylength shadeop.

The following functions operate on arrays:

arrayname [ index ] (in an expression)
Accesses the index-th element of an array arrayname. index is a float expression which rounds down to an integer.
arrayname [ index ] = expression;
Sets the index-th element of an array arrayname to the results of the expression. index is a float expression which rounds down to an integer.
Resizable Arrays

A resizable array can be obtained by declaring a local array variable with an unspecified or non-constant length. Initialization is optional. For example:

float A[];                  // initially empty
float B[] = {1,2};          // initial length is 2
float C[1+arraylength(B)];  // initial length is 3

Only arrays declared with an unspecified or non-constant length are resizable. Only arrays in local variables are resizable. Note that the length of a shader parameter can also be unspecified, but its length is fixed when the shader parameter is bound, and it cannot be resized.

All built-in functions that operate on fixed-length arrays can also operate on resizable arrays. For example, arraylength returns the length of a fixed or resizable array. An array can be resized using the resize function:

void resize(output type array[], uniform float length);
Changes the length of a resizable array. Note that the length is always uniform. If the length is increased, the new elements are uninitialized. An error is reported if the array has a fixed length.

Array assignment resizes the destination array as necessary:

float A[];          // initially empty.
A = B;              // resizes A to accommodate B.

Message passing will also resize the result array to match the length of of the source. For example:

float A[];
surface("A", A);    // resizes A if necessary.

Arrays are also resized by the push and pop functions:

void push(output type array[], type value);
Increases the length of the array by one and stores the given value as the last entry.
type pop(output type array[]);
Returns the last element of the array and decreases its length by one. The capacity is not decreased.
Length and capacity of resizable arrays

A resizable array has both a length and a capacity (similar to an STL vector). Resizing an array increases its capacity if necessary. However, reducing the length does not decrease the capacity (unless it is set to zero, in which case the storage is reclaimed).

The capacity can be increased without changing the length, thus reserving storage for future operations:

void reserve(output type array[], uniform float length);
Increases the capacity of the array if necessary, but does not change its length. The capacity is never reduced, unless it is set to zero.

Reserving storage is preferable to incrementally resizing an array, since increasing capacity generally requires copying the array contents. This is especially useful when repeatedly pushing items on an array:

reserve(A, arraylength(A)+3);       // reallocate A before pushing.
push(A, 1);
push(A, 2);
push(A, 3);

The capacity of an array can be obtained using the following function:

uniform float capacity(type array[]);
Returns the capacity of a fixed or resizable array. The capacity of a fixed-length array is always equal to its length.
Resizable arrays as parameters
  • Functions with array arguments whose dimensions are unspecified can be called with either fixed or resizable arrays.
  • A function can resize an output array (or call push, pop, etc.) It is an error to pass a fixed-length array to such a function. Such errors are often caught at compile time, but in some cases they are not detected until run time.
  • Resizable arrays can be passed to shader plugin functions (DSOs), but there is not yet any way to resize them from inside the plugin.
  • Shader parameters are not resizable. The length of an input shader parameter can be unspecified, but the it becomes fixed when the parameter is bound.
  • Some built-in shadeops require a resizable array in which to store their results. For example, the result of the getlights shadeop is an array of arbitrary length.


The RenderMan shading language supports struct types. A C-style syntax is used for struct definitions, except that a default value is specified for each struct member:

struct Float2 {
    float x=0, y=0;

The arrow operator (e.g. "a->x") is used to access struct members. For example, here is a function that operates on two structs:

float AddX(Float2 a; Float2 b) {
    return a->x + b->x;

A struct variable definition can use the default member values, or it can override them using a constructor call:

struct Plastic {
    float Ks=.5, Kd=.5, Ka=1, roughness=.1;
    color specularcolor=(1,1,1);

Plastic basicPlastic;
Plastic shinyPlastic = Plastic("Ks", .8, "roughness", 0);

Structs can contain uniform and varying data of any type, including arrays, nested structs, etc. Struct members are operated upon by reference, so adopting structs has little performance impact.


The Shading Language is strongly typed; supported types are enumerated below. Note that certain types may be collections of other types.


The Shading Language implements color as an abstract data type independent of the number of samples and the color space. The major operations involving color are color addition (+ operator) corresponding to the mixing of two light sources, and color filtering (* operator) corresponding to the absorption of light by a material. In each case these color operations proceed on a component by component basis.

The number of color samples used in the rendering program is set through the RenderMan Interface. Once the number of color samples has been specified, colors with the appropriate number of samples must be passed to a shader. When setting the number of samples, the user can also specify a transformation from RGB space to this n sample color space. This allows a shader to convert color constants to the specified color space.

Color component values of 0 correspond to minimum intensity, while values of 1 correspond to maximum intensity. A color constant of 0 is equivalent to black, and of 1 is equivalent to white. However, values outside that range are allowed (values less than zero decrement intensity from other sources).

Color variables may be declared with:

color c, d=1, e=color(1,0,0);

The initialization value may be any scalar or color expression that contains only uniform operands. If a scalar expression is used, its value is promoted to a color by duplicating its value into each component.

Color constants are specified by:

color [space] (u,v,w)

The optional specifier space indicates the color coordinate system of the 3-tuple. The default color coordinate system is "rgb." The table below lists the color coordinate systems that are supported in the Shading Language.

Color Coordinate Systems
Coordinate System Description
"rgb" Red, green, and blue
"hsv" Hue, saturation, value
"hsl" Hue, saturation, lightness
"xyz", "XYZ" CIE XYZ coordinates
"YIQ" NTSC coordinates


Floats are used for all scalar calculations. They are also used for integer calculations. Floating-point variables are defined as follows:

float a, b=1;

The initialization value may be any scalar expression that contains only uniform operands.

Points, Vectors, and Normals

Point-like variables are (x,y,z) triples of floats that are used to store locations, direction vectors, and surface normals.

A point is a position in 3D space.  A vector has a length and direction, but does not exist in a particular location.  A normal is a special type of vector that is perpendicular to a surface and thus describes the surface's orientation.

All calculations involving points. vectors and normals are assumed to take place in an implementation-dependent coordinate system, usually either the camera or world coordinate system. Procedures exists to transform points, vectors and normals from the shading coordinate system to various named coordinate systems, or to define a point, vector or normal in one of several coordinate systems and transform it to the shading coordinate system. It should be noted that point locations, direction vectors and normals do not transform in the same way as, and therefore it is important to use the correct transformation routine for each type.

A number of standard coordinate systems are known to a shader. These include: "raster", "NDC", "screen", "camera", "world", and "object". These are discussed in the section on Camera in Part I. In addition, a shader knows the coordinate systems shown in the table below.

Geometric Coordinate Systems
Coordinate System Description
"shader" The coordinate system in which the shader was defined. This is the "object" coordinate system when the shader is defined.
"current" The coordinate system in which the shading calculations are being performed. This is normally the "camera" or "world" coordinate system.
"string" A named coordinate system established using RiCoordinateSystem.

Point variables are declared like so:

point u, v=1, w=point(1,1,1);
vector R;
normal Nf;

The initialization value may be any scalar or point-like expression. If a scalar expression is used, the value is promoted to a point (or vecdtor or normal) by duplicating its value into each component.

Point, vector and normal constants default to be in the "current" coordinate system. Points, vectors and normals can be specified in any known coordinate system with:

point [space] (x,y,z)
vector [space] (x,y,z)
point [space] (x,y,z)

where the space is a string literal containing the name of the coordinate system.  For example,

point "world" (0,0,0)

defines a point at the position (0,0,0) in world coordinates. This point is implicitly transformed to the "current" coordinate system. Similarly,

vector "object" (0,0,1)

defines a vector pointing toward the +z axis in "object" space, which is implicitly transformed into the equivalent vector expressed in the "current" coordinate system.  Points, vectors and normals passed through the RenderMan Interface are interpreted to be in "shader" or "object" space, depending on whether the point variable was set using a shader command or a geometric primitive command, respectively.


Shaders can be types, too, meaning they can be passed as shader parameters. That is, a string (i.e. a shader handle) can be provided as a value for a parameter of type "shader", enabling layer shaders, light shaders, et al., to access co-shaders as parameters, rather than parsing a co-shader list.


Strings are used to name external objects (texture maps, for example). String literals (or constants) are enclosed in double quotes, as in the C language, and may contain any of the shadard C "escape sequences" (such as n for newline or " for a quote).


The Shading Language has a matrix type that represents the transformation matrix require to transform points and vectors from one coordinate system to another.  matrix is a first-class data type in the language, so it can be used to declare shader instance parameters, local variables and function parameters. Beware if you declare matrix of type varying.

matrix variables can be tested for equality and inequality with the == and != boolean operators. In addition, some functions will accept matrix variables as arguments, as described below.

A matrix is, internally, a 4x4 homogeneous premultiplication transformation matrix. However, our intent was to maintain the programmer's model that the matrix is an atomic data type, not a 2-dimensional array. Individual rows, columns or elements will not be accessible through any form of array notation. When used as a shader instance parameter, it is used in cooperation with the matching RIB declaration type matrix to pass 4x4 transformation matrices into the shader. As with the point data type, a matrix in the RIB stream represents a quantity relative to the then-current object space, while a matrix in the Shading Language represents a quantity relative to the renderer's "current" space. Thus, there will be an implicit transformation between the two representations.

The syntax of matrix specification may be subtly confusing the first time it is seen, but it is consistent with the semantics of point specification. A matrix constant can be specified with the following notation:

matrix [space] (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p)

This generates a transformation matrix which transforms points in the "current" coordinate system into some other coordinate system which is relative to space. That is, if the optional specifier space is used, it indicates that the 4x4 array (a,...,p) itself converts points from the coordinate system space to some other coordinate system which is relative to space, and thus the final matrix produced transforms points from "current" space to that other coordinate system relative to space.

If the optional coordinate system specifier space is not used, the default coordinate system is "current", and so the matrix transforms points from "current" space to some system relative to "current".

In addition, the values 0 and 1, when promoted to type matrix, generate the obvious zero matrix (all elements zero), and identity matrix (elements on the diagonal one, the rest zero).

Gearhead detail: Because of the way that math of matrices works, the syntax matrix (a,...,p) actually transforms points from any space X to a space relative to X, and matrix "Y" (a,...,p) is just the "current" to "Y" transform concatenated onto the front of that. And for those with a scorecard, this means that the syntax matrix "object" 1 generates a matrix that simply transforms from "current" coordinates to "object" coordinates.

The following operators and functions operate on matrix variables.

0 and 1
The zero matrix and the identity matrix. More generally, assigning any float f to a matrix results in a matrix which is all zeros except for diagonal entries, which are all f.
matrix * matrix
Concatenates two matrices using matrix multiplication.
matrix1 / matrix2

Concatenates the inverse of matrix2 onto matrix1. This is equivalent to:

matrix1 * inverse(matrix2)

Filter Regions

A "filterregion" provides a way of telling the texturing system what part of the texture to filter over that produces improved filtering over Loop surfaces and in many camera/surface/texture coordinate situations. filterregions can be created from a pair of texture coordinates and passed to the texture call. It can also be scaled to over-/under-filter. Its usage is as follows:

float ss = ...
float tt = ...
filterregion filt;
filt->scale(1.5) // optionally scale to over/under filter
Ci = texture("check_t", filt, "filter", "gaussian");

The result is similar to the elliptical weighted average filter ("filter", "ewa") in that it is designed to filter more properly when the texture coordinates are highly distorted or the surface is viewed at a grazing angle. It eliminates the difficulties of determining the correct four points to pass to the texture call when using the ewa filter, especially on Loop surfaces. It currently supports filter types box, gaussian, and radial-bspline. All other filter types revert to gaussian at this time.

filterregion::calculate1d (s)
filterregion::calculate2d (s,t)
filterregion::calculate3d (triple V)
These functions construct a filterregion by looking at the passed in variables and determining how they change near the vertex.

filterregion::calculate2d (float s, float t, float t0s, float t0t, float t1s, float t1t);
filterregion::calculate3d (triple V, triple T0, triple T1);
These functions construct a filterregion directly, by specifying two axes that define the the region,

fr-> extend (float t0s, float t0t, float t1s, float t1t);
fr-> extend (triple T0, triple T1);
fr-> extend (filterregion fr)
A filterregion may be extended by combining them either with another filterregion or by defining two axes that the region must also bound.

A filterregion can be modified and queried with the following functions:

Name Type Description
clampaspectratio(minaspect) float Clamps the lengths of the axes to be longer than minaspect * longest axis length
scale(amt) float Scales the axes defining the region
blur(amt) float Adds amt length to the axes
maxsize float The length of the longest axis
minsize float The length of the shortest axis
  • Note

    The following functions accept a filterregion as an argument:

    texture (string name; filterregion fr; [parameterlist])
    ptexture (string name; float channel, faceindex; filterregion fr; [parameterlist])
    shadow (string name; filterregion fr; [parameterlist])
    environment (string name; filterregion fr; [parameterlist])
    getpoints (string filenames[]; filterregion frP; filterregion frN; float maxpoints; [parameterlist])
    knoise (filterregion fr, uniform float freqWindow, "keyword", value)
    gather (string category; point P; filterreigon fr; float numSamples; [parameterlist])
    areashadow (string mapnames[]; filterregion from; point lightsamples[]; [parameterlist])

Uniform and Varying Variables

A renderer implementation may choose to shade many points, or even large regions of a surface, at once.  How large such a region may be is implementation-dependent.

Shaders contain two classes of variables: uniform variables are those whose values are constant over whatever portion of the surface begin shaded, while varying variables are those that may take on different values at different locations on the surface being shaded. For example, shaders inherit a color and a transparency from the graphics state. These values do not change from point to point on the surface and are thus uniform variables. Color and opacity can also be specified at the vertices of geometric primitives (see Geometric Primitives). In this case they are bilinearly interpolated across the surface, and therefore are varying variables.

Local variables and arguments to shaders are declared to be either uniform or varying by specifying a storage modifier:

varying point p;
uniform point q;

Variables declared in the argument list of a shader are assumed to be uniform variables by default. These are sometimes referred to as instance variables. If a variable is provided only when a shader is instanced, or if it is attached to the geometric primitive as a whole, it should be declared a uniform variable. However, if a variable is to be attached to the vertices of geometric primitive, it should be declared as a varying variable in the shader argument list.

Variables declared locally in the body of a shader, as arguments to a function, or as local variables are assumed to be varying. Declaring a variable to be uniform inside a shader or function definition is never necessary, but may allow the compiler to generate more efficient code.

If a uniform value (or a constant) is assigned to a varying variable or is used in a varying expression, it will be promoted to varying by duplication. It is an error to assign a varying value to a uniform variable or to use a varying value in a uniform expression.