# Containers, Types, and Variables

# Containers, Types, and Variables

### Containers

RSL supports the following containers:

#### Arrays

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.

#### Structs

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.

### Types

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

#### Colors

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.

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

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.

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

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

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).

#### Matrices

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. `filterregion`s 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->calculate2d(ss,tt); 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)

`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);

`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)

`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.