DSO Shadeops with RenderDotC

Contents:
Introduction
Tutorial
Using libsl


Introduction

One of the most powerful features provided by RenderDotC is the ability to write custom shaders in the RenderMan shading language (SL).  Shaders written in SL may call any of the built-in functions, also known as "shadeops".  Shaders may also call user defined functions which are themselves written in SL.

The DSO shadeops feature allows a user defined function to be written in C or C++, compiled to a DSO or DLL, and invoked from within an SL shader or function.  DSO shadeops provide the complete power of C++ such as reading external files, calling functions in the standard libraries, or utilizing class hierarchies.

RenderDotC's shader compiler converts SL to C++ and uses the C++ compiler to convert that to a DSO or DLL.  DSO shadeops are different in that the user writes the C++ code directly.  While this mode is more flexible, errors may be more serious because the functions are not constrained by the RenderMan shading language.  Finally, DSO shadeops can be better optimized than SL functions because the user has a greater degree of control.


Tutorial

To understand how to write, compile, and use a DSO shadeop, let's work through a real example.  Suppose you wanted to write your own version of the built-in RenderMan function cellnoise.  This function takes 1, 2, 3, or 4 floating point arguments and returns a pseudorandom number between 0 and 1 based on the floor of all the inputs.  Basically, it's a repeatable random number generator: pass it the same inputs and you'll get the same output.  To distinguish our DSO shadeop version from the built-in one, let's call ours "cellrand".

Here's a scene rendered with cellrand:

cellnoisy.tif
Here is the RIB for this scene:


Declare "scale" "uniform float"
Format 300 300 -1
Display "cellnoisy.tif" "file" "rgb"
Projection "orthographic"
LightSource "ambientlight" 1
WorldBegin
    Surface "cellnoisy" "scale" [20]
    Translate 0 0 5
    Patch "bilinear" "P" [-1 1 0  1 1 0  -1 -1 0  1 -1 0]
WorldEnd

cellpatch.rib
 

And here is the surface shader:


surface cellnoisy(float scale = 1;)
{
    float uu = scale * u;
    float vv = scale * v;
    setcomp(Ci, 0, cellrand(uu, vv));
    setcomp(Ci, 1, cellrand(uu + 123, vv + 234));
    setcomp(Ci, 2, cellrand(uu + 345, vv + 456));
    Oi = 1;
}

cellnoisy.sl

The calls to the function cellrand() invoke our DSO shadeop.  Note that it appears in the shader just as any other call to a built-in or user-defined function.

As yet, this shader will not compile because cellrand() has not been defined.  The shader compiler will identify it as an undefined function and halt.  The  sections below show step by step how to create and compile the DSO shadeop.

The shadeop table

To create the cellrand DSO shadeop, use your favorite text editor to create a C source code file.  Although the name of the file need not match the name of the function, let's name it cellnoise.c for clarity.

At the top of the file, put:

    #include "shadeop.h"

This header file will define various macros and types that are useful for writing DSO shadeops.  The first macro we'll utilize is SHADEOP_TABLE:

    SHADEOP_TABLE(cellrand) = {
        { "float cellrand_f(float)",         "init", "cleanup" },
        { "float cellrand_ff(float, float)", "init", "cleanup" },
        { "float cellrand_p(point)",         "init", "cleanup" },
        { "float cellrand_pf(point, float)", "init", "cleanup" },
        { "" },
    };

There's quite a bit going on here.  SHADEOP_TABLE is a macro defined in shadeop.h.  It takes a single argument: the name of the new function as it will appear in SL code that calls it.  Assign to SHADEOP_TABLE an array of structs, each containing three strings.  The array must be terminated with a struct whose first string is empty "".

Each element in this array represents a C function that implements the shadeop.  The reason that there are four C functions for one SL function is that cellrand() is polymorphic.  That is, the actual function selected depends upon the type and number of arguments.  Remember, cellrand() may be 1, 2, 3, or 4 dimensional.  Therefore, we need four versions of cellrand().  For non-polymorphic shadeops, there would be one triple followed by the null string that terminates the array.

Consider the first line in the shadeop table.  It contains the complete prototype of the one dimensional cellrand method including return value and argument types.  The name of the function is as it will appear in the C code.  No two members of the shadeop table should have the same method name.  Return value and argument types may be any of "float", "point", "color", or "string".  In addition, the return value type may be "void".

The other two strings name the C functions which will perform any desired initialization and cleanup.  Each of these is optional and may be replaced by the empty string "" if absent.  Unlike the actual shadeop method, init and cleanup functions may be repeated within the shadeop table.  They may even be repeated in additional shadeop tables within the same source code file, which leads us to the next topic.

A single source code file may have more than one shadeop table.  Our sample code contains a second shadeop called "printit", so we define a second shadeop table:

    SHADEOP_TABLE(printit) = {
        { "void printit(string)", "", "" },
        { "" },
    };

You need to define a separate shadeop table for each unique function name that may appear in the SL code that calls it.  The printit function is non-polymorphic, so there's only one triple in the table and the C name "printit" can be identical to the SL name "printit".

The init function

All of our cellrand methods share the same initializer function.  Here it is:


#include <stdlib.h>                     /* For rand() */
/*
 * Constants
 */
#define CELLNUM 2048

/*
 * Types
 */
typedef struct {
    float unif[CELLNUM + CELLNUM];      /* Uniform random distribution */
    int perm[CELLNUM + CELLNUM];        /* Random permutation */
} celltables;
/*
 * Create the pseudorandom tables as initdata
 */
SHADEOP_INIT(init)
{
    register int i, j;
    int tmp;
    celltables *initdata;

    initdata = (celltables *)malloc(sizeof(celltables));
    if (initdata) {
        /*
         * Create an array of pseudorandom numbers uniformly
         * distributed over the range [0.0, 1.0]
         */
        for (i = 0; i < CELLNUM; i++) {
            initdata->unif[i] = (float)rand() / RAND_MAX;
            initdata->perm[i] = i;
        }

        /*
         * Create a pseudorandom permutation of [0..CELLNUM-1]
         */
        for (i = CELLNUM - 1; i > 0; i -= 2) {
            j = rand() % CELLNUM;
            if (i != j) {
                tmp = initdata->perm[i];
                initdata->perm[i] = initdata->perm[j];
                initdata->perm[j] = tmp;
            }
        }

        /*
         * Extend unif and perm arrays to allow for faster indexing.
         */
        for (i = 0; i < CELLNUM; i++) {
            initdata->unif[CELLNUM + i] = initdata->unif[i];
            initdata->perm[CELLNUM + i] = initdata->perm[i];
        }
    }

    return (void *)initdata;
}

Here, we use the SHADEOP_INIT macro to declare the init function.  You can declare it longhand but there are a couple of gotchas to be aware of.  First, if you write a shadeop in C++, the exported functions need to be declared extern "C" to turn off C++ name mangling.  Second, on Windows the functions need to be declared with __declspec(dllexport) so that they are visible outside of the DLL.  So in the worst case of a C++ shadeop on Windows, the correct declaration of init is as follows:

    extern "C" void __declspec(dllexport) *init(int ctx, void *texturectx)

That's a mouthful - better to use SHADEOP_INIT and let the macro take care of the platform specific details.

The init function takes two arguments: a thread id number and a pointer to a texture mapping context.  Neither are implemented in RenderDotC at this time.  The return value is of type void pointer.  It can be NULL or it can point to data, as in our example.  This pointer will later be passed to our method and cleanup functions.

Each init function is called no more than once per thread (just once per run in a single threaded renderer).  It is guaranteed to be called before the corresponding method in the shadeop table.  Even if the init function is shared between multiple entries in a shadeop table or multiple tables within the same file, it will only be called once.  The init function may allocate memory, initialize the state, create a context, or whatever one time initialization makes sense for your shadeop.

In our example, the init function creates two tables in dynamic memory.  One has 4096 uniformly distributed pseudorandom numbers.  The other is a pseudorandom permutation of the numbers 0 through 4095.  Both arrays are then extended to 8192 members by duplicating the original tables.  The two arrays are packaged into a single struct because init can have only one return value.
 

The cleanup function

Here's our cleanup function:


/*
 * Free the initdata
 */
SHADEOP_CLEANUP(cleanup)
{
    free((char *)initdata);
}

The cleanup function may or may not be called when the renderer exits.  It can undo whatever the init function did (as shown here) or it can perform other cleanup activities.  It takes one argument, the pointer that was returned from the init function.

The methods

The heart of any DSO shadeop are the methods themselves.  These are the functions that are called more or less directly by the shader.  Consider our 1D cellrand() method:


/*
 * One dimensional cellrand method
 */
SHADEOP(cellrand_f)
{
    int bx0;
    celltables *t =  (celltables *)initdata;
    float *result =  (float *)argv[0];
    float  x      = *(float *)argv[1];

    if (x < 0) x--;
    bx0 = (long)x & (CELLNUM - 1);
    *result = t->unif[bx0];

    return 0;
}

Again, we use a macro from "shadeop.h".  The SHADEOP macro takes one argument: the name of the function as it should appear to the C/C++ compiler.  This name should match the first column of one of the entries in  SHADEOP_TABLE.

Methods take three arguments:

    void *initdata    The pointer returned from the init function, if any
    int argc         The number of arguments to this method, including the return value
    void **argv      Pointers to the return value and all of the arguments

The return value is pointed to by argv[0].  In the case of a shadeop that returns void, argv[0] is undefined.  In all other cases, the method should store the return value there.  All methods should return 0 to indicate success and 1 upon failure.

Because all of the inputs are void pointers, it's up to the shadeop programmer to cast these pointers to the appropriate types.  Our cellrand method does that right at the beginning, in some cases declaring local pointers of the correct type for later use.

Here are the other three cellrand methods:


/*
 * Two dimensional cellrand method
 */
SHADEOP(cellrand_ff)
{
    int bx0, by0;
    celltables *t =  (celltables *)initdata;
    float *result =  (float *)argv[0];
    float  x      = *(float *)argv[1];
    float  y      = *(float *)argv[2];

    if (x < 0) x--;
    if (y < 0) y--;
    bx0 = (long)x & (CELLNUM - 1);
    by0 = (long)y & (CELLNUM - 1);
    *result = t->unif[t->perm[bx0] + by0];

    return 0;
}

/*
 * Three dimensional cellrand method
 */
SHADEOP(cellrand_p)
{
    int bx0, by0, bz0;
    float x, y, z;
    celltables *t =  (celltables *)initdata;
    float *result =  (float *)argv[0];
    float *p      =  (float *)argv[1];

    x = (p[0] < 0 ? p[0] - 1 : p[0]);
    y = (p[1] < 0 ? p[1] - 1 : p[1]);
    z = (p[2] < 0 ? p[2] - 1 : p[2]);
    bx0 = (long)x & (CELLNUM - 1);
    by0 = (long)y & (CELLNUM - 1);
    bz0 = (long)z & (CELLNUM - 1);
    *result = t->unif[t->perm[t->perm[bx0] + by0] + bz0];

    return 0;
}

/*
 * Four dimensional cellrand method
 */
SHADEOP(cellrand_pf)
{
    int bx0, by0, bz0, bw0;
    float x, y, z;
    celltables *t =  (celltables *)initdata;
    float *result =  (float *)argv[0];
    float *p      =  (float *)argv[1];
    float  w      = *(float *)argv[2];

    x = (p[0] < 0 ? p[0] - 1 : p[0]);
    y = (p[1] < 0 ? p[1] - 1 : p[1]);
    z = (p[2] < 0 ? p[2] - 1 : p[2]);
    if (w < 0) w--;
    bx0 = (long)x & (CELLNUM - 1);
    by0 = (long)y & (CELLNUM - 1);
    bz0 = (long)z & (CELLNUM - 1);
    bw0 = (long)w & (CELLNUM - 1);
    *result = t->unif[t->perm[t->perm[t->perm[bx0] + by0] + bz0] + bw0];

    return 0;
}

cellrand.c

Method arguments

Note that float, point, and color arguments are all cast to (float *).  In the case of points and colors, the pointer addresses an array of three single precision floating point numbers.  All arguments are call-by-reference.  That is, the shadeop may modify the values of arguments and the shader or SL function which called the shadeop will receive the results (provided that the actual parameter was an lvalue).

Strings are handled somewhat differently.  Here is a sample method that takes a string argument and prints it:

    #include <stdio.h>

    SHADEOP(printit)
    {
        STRING_DESC *sd = (STRING_DESC *)argv[1];
        printf("%s\n", sd->s);
        return 0;
    }

STRING_DESC is a type defined in "shadeop.h".  Of its three elements, the one named `s' points to the nul-terminated string.  The other two elements are reserved for internal use.

Compiling the shadeop

Compilation details depend up the platform.

SGI mips2:    CC -I$RDCROOT/include -mips2 -o32 -O -elf -shared cellrand.c -o cellrand.so
SGI mips3:    CC -I$RDCROOT/include -mips3 -n32 -O -elf -shared cellrand.c -o cellrand.so
SGI mips4:    CC -I$RDCROOT/include -mips4 -n32 -O -elf -shared cellrand.c -o cellrand.so
Visual C++:   cl -I%RDCROOT%/include -LD cellrand.c
Borland C++:  bcc32 -I%RDCROOT%/include -tWD cellrand.c
Linux:        g++ -I$RDCROOT/include -shared cellrand.c -o cellrand.so
BSD/OS:       g++ -I$RDCROOT/include -shared cellrand.c -o cellrand.so

Compiling the shader

The shadeop must be successfully compiled before you compile the shader.  When the shader compiler encounters a call to an unknown function, it will examine every file with the ".so" extension (on Windows, ".dll") for a shadeop table with the matching name.  The first one encountered will be selected.  If none is found, compilation halts on an unknown function.

Make sure that the compiled shadeop is on the shader compiler's include path (the list of directories searched for header files).  If cellnoisy.sl and cellrand.so are both in the current directory, then the following command is sufficient:

    shaderdc cellnoisy.sl

A quick way to add a directory to the include path is with the -I option to shaderdc.  For example:

    shaderdc -I./irix_mips3 cellnoisy.sl

Rendering the RIB

When rendering a scene, both the compiled shader and any DSO shadeops they use must be on the shader search path.  An easy way to ensure that DSO shadeops will be found at render time is to put the compiled shadeops in the same directory as the compiled shaders that call them.

To render our example, type:

    renderdc cellpatch.rib


Using libsl

When RenderDotC compiles a RenderMan SL shader, it doesn't put all of the detailed code in the intermediate C++ file.  Instead, the compiled shader calls upon an extensive C++ utility library called "libsl".  As the author of a DSO shadeop, you can also take advantage of the power of libsl.  For example, you can call the same random(), noise(), specularbrdf() and texture() functions as the shader and get exactly the same results.

The steps are the same as above with the following additions.  First, you must program your shadeop in C++.  Some classes are defined and used by libsl and they won't compile under standard C.  Your source code file should have a C++ extension such as ".cpp", ".C", or ".c++" depending upon your compiler.

Secondly, your shadeop need to include libsl's header file:

    #include "sl.h"

Finally, when compiling your shadeop, you must link against the libsl library file:

SGI mips2:   CC -I$RDCROOT/include -L$RDCROOT/lib -mips2 -o32 -O -elf -shared my.c++ -o my.so -lsl
SGI mips3:   CC -I$RDCROOT/include -L$RDCROOT/lib32/mips3 -mips3 -n32 -O -elf -shared my.c++ -o my.so -lsl
SGI mips4:   CC -I$RDCROOT/include -L$RDCROOT/lib32/mips4 -mips4 -n32 -O -elf -shared my.c++ -o my.so -lsl
Visual C++:  cl -LD -I%RDCROOT%/include my.cpp -link -libpath:"%RDCROOT%/lib/vc50" sl.lib
Borland C++: bcc32 -I%RDCROOT%/include -L%RDCROOT%/lib/bc50 -tWD my.cpp sl.lib
Linux:       g++ -I$RDCROOT/include -L$RDCROOT/lib -shared my.c++ -o my.so -lsl
BSD/OS:      g++ -I$RDCROOT/include -L$RDCROOT/lib -shared my.c++ -o my.so -lsl

The libsl interface is documented in the sections below.

Math

The sl.h file includes both <math.h> and <float.h>.  sl.h defines single precision floating point constants PI, PI_2, and PI_4.

    RtBoolean epsilon(float a)

Returns RI_TRUE if a is within FLT_EPSILON (defined in <float.h>) of 0.

    float radians(float degrees)
    float degrees(float radians)

The function radians() converts from degrees to radians; and conversely, the function degrees() converts from radians to degrees.

    float atan(float y, float x)

atan() returns the arc tangent of y/x in the range -pi to pi.

    float log(float x, float base)
    float fpow(float x, float y)
    float inversesqrt(float x)

log() with two arguments returns the logarithm in the specified base (log(pow(base, x), base)).  fpow() returns x^y (0 on domain error).  inversesqrt() returns 1.0 / sqrt(x) (0 on domain error).

    float mod(float a, float b)
    float sign(float x)

mod() returns a value greater than 0 and less than or equal to b such that mod(a,b) = a - n*b for some integer n.  Note that this has different semantics than the C function fmod().  sign() returns -1 if its argument is negative, 1 if its argument is positive, and 0 if its argument is zero.

    RtInt clamp(RtInt a, RtInt min, RtInt max)
    float clamp(float a, float min, float max)

clamp(a, min, max) returns min if a is less than min, max if a is greater than max; otherwise it returns a.  Two forms are provided, one for integers and one for reals.

    float round(float x)

round() returns the integer closest to x.

    float mix(RtFloat low, RtFloat high, RtFloat value)

mix() returns the interpolated value ((1-value)*low + value*high)

    float step(float min, float value)

step() returns 0 if value is less than min; otherwise it returns 1.

    float fdiv(float a, float b)

Safely divides a by b, even for very small b and/or very large a.  Returns +/- RI_INFINITY (correct sign) instead of causing a floating point overflow.

    float fminv(RtInt n, ...)
    float fmaxv(RtInt n, ...)

Returns the minimum (or maximum) of a set of n doubles.  For example: fminv(3, 2.0, 3.0, 1.0) returns 1.0.

    float smoothstep(float min, float max, float value)

smoothstep returns 0 if value is less than min, 1 if value is greater than or equal to max, and performs a smooth Hermite interpolation between 0 and 1 in the interval min to max.

    float fsplinev(const char *basis, float value, RtInt n, ...)

fsplinev() fits a interpolating spline of the given basis to n control points.  At least four control points must always be given.  The basis may be "bspline", "bezier", "hermite", "linear", or "catmull-rom".

    RtFloat filterstep(RtFloat edge, RtFloat val, RtFloat w, ...)

Much like step(edge, val) except filtered so that there is a smooth transition from 0 to 1.  A filter kernel is centered at edge and has width w.  filterstep() returns the fraction of the area under the curve that lies to the right of edge.  In the case of a catmull-rom filter, the return value may lie slightly out of the range from 0 to 1 due to the filter shape.

The parameter list may contain string-value pairs and must be terminated by a null pointer.  To specify which filter kernel to use, include the string "filter" followed by "box", "triangle", "catmull-rom", or "gaussian".  For example, to select the Gaussian filter kernel:

     filterstep(edge, val, w, "filter", "gaussian", 0)

The default filter is "catmull-rom".

To adjust the amount of overfilter, use the token "width" (or "swidth" synonymously) followed by a floating point number indicating a factor to multiply the filter width by.  For example, to double the filter size:

     filterstep(edge, val, w, "width", 2.0, 0)

Each filter kernel has an inherent width of support.  Outside of that area of support, filterstep returns either 0 or 1.  For reference, with w = 1:

     "box" is defined over the range x = -0.5 to 0.5 (width 1).
     "triangle" is defined over the range x = -1 to 1 (width 2).
     "catmull-rom" is defined over the range x = -2 to 2 (width 4).
     "filter", "gaussian" is defined over the range x =-1.5 to 1.5 (width 3).

Increasing w or increasing the amout of overfilter with the "width" token will increase the area of support for all of the filter kernels.  In other words, the total support width of a filter is the product of its inherent width, the overfilter factor, and w.

All of the filters used by filterstep() change their shape to match the width.  Contrast that with the pixel filters RiCatmullRomFilter() and RiSincFilter() which retain their shape but are merely windowed by xwidth and ywidth.

    float frandom()

frandom() returns a float that is a uniformly distributed random number between 0 and 1.

    RtFloat fperlinf(RtFloat v)
    RtFloat fperlinff(RtFloat u, RtFloat v)
    RtFloat fperlinp(const Point &pt)
    RtFloat fperlinpf(const Point &p, RtFloat w)
    RtFloat flewisf(RtFloat v)
    RtFloat flewisff(RtFloat u, RtFloat v)
    RtFloat flewisp(const Point &pt)
    RtFloat flewispf(const Point &p, RtFloat w)

Two implementations of a set of noise functions that return a value which is a pseuodrandom function of its arguments; the value is always between 0 and 1.

    typedef RtFloat (*RtNoise1Func)(RtFloat)
    typedef RtFloat (*RtNoise2Func)(RtFloat, RtFloat)
    typedef RtFloat (*RtNoise3Func)(const Point &)
    typedef RtFloat (*RtNoise4Func)(const Point &, RtFloat)
    extern RtNoise1Func fnoisef
    extern RtNoise2Func fnoiseff
    extern RtNoise3Func fnoisep
    extern RtNoise4Func fnoisepf

Function pointers to the currently selected set of noise functions.  The user may choose either of the two function sets above.  The selection impacts all flavors of fpernoise, pnoise, ppernoise, cnoise, and cpernoise.  It has no impact on any form of cellnoise.  The domain of the noise function can be 1-D (fnoisef), 2-D (fnoiseff), 3-D (fnoisep) or 4-D (fnoisepf).

    RtFloat fpernoisef(RtFloat x, RtFloat period)
    RtFloat fpernoiseff(RtFloat x, RtFloat y, RtFloat xperiod, RtFloat yperiod)
    RtFloat fpernoisep(const Point &p, const Point &period)
    RtFloat fpernoisepf(const Point &p, RtFloat w, const Point &pperiod, RtFloat wperiod)

Periodic noise behaves the same as regular noise but with the additional property that the return value repeats itself with the frequency given by the period argument.  In other words, fpernoise(v, period) == fpernoise(v + period, period)  For best results, all periods should be specified with integers.

    RtFloat fcellnoisef(RtFloat x)
    RtFloat fcellnoiseff(RtFloat x, RtFloat y)
    RtFloat fcellnoisep(const Point &p)
    RtFloat fcellnoisepf(const Point &p, RtFloat w)

Returns a pseudorandom value that depends upon the floor of all its arguments.  The random values are uniformly distributed over the range from 0 to 1.  The results are repeatable: given the same input (or input similar enough that the floor of all arguments is identical) cellnoise will return the same value.

Point

The Point class has three member variables:

    RtFloat x, y, z

All member functions and member variables are public.  All member functions are inline to keep the interface strictly extern "C" and avoid name-mangling issues that a C++ interface brings.  Functions that were too complex to be made inline were made non-member functions with C interface.

    Point()
    Point(RtFloat c)
    Point(RtFloat xx, RtFloat yy, RtFloat zz)
    Point(const Point &p)

Warning: The constructor without arguments is a null function.  It does not initialize x, y, and z.  This tradeoff was made in order to quickly declare
Point variables without constructor overhead.  The single argument constructor sets x, y, and z to the same value.

    Point operator-() const

Unary negation.  Returns the point with x, y, and z negated.  Does not modify the original point.

    Point &operator=(RtFloat c)
    Point &operator=(const Point &p)

Straight assignment operators.  The single value version sets x, y and z to the same value.

    Point &operator+=(RtFloat c)
    Point &operator+=(const Point &p)
    Point &operator-=(RtFloat c)
    Point &operator-=(const Point &p)
    Point &operator*=(RtFloat c)
    Point &operator*=(const Point &p)
    Point &operator/=(RtFloat d)
    Point &operator/=(const Point &p)

These assignment operators work on a component-by-component basis.  All modify the existing point and return the result.  The division operators do nothing if any divisor is within FLT_EPSILON of 0 (see epsilon()).

    Point &operator^=(const Point &p)

Replaces the current point with the cross product of itself and another point.

    Point operator+(const Point &p1, const Point &p2)
    Point operator-(const Point &p1, const Point &p2)
    Point operator*(RtFloat f, const Point &p2)
    Point operator*(const Point &p1, RtFloat f)
    Point operator*(const Point &p1, const Point &p2)
    Point operator/(const Point &p1, RtFloat d)
    Point operator/(const Point &p1, const Point &p2)

Component-by-component operators return a new point without modifying either of the operands.  Three forms of multiplication are allowed: float times point, point times float, and point times point (componentwise).

    RtFloat dot(const Point &p1, const Point &p2)
    Point operator^(const Point &p1, const Point &p2)

Dot product and cross product binary operators.  Neither modifies the input operands.

    RtBoolean operator==(const Point &p1, const Point &p2)
    RtBoolean operator!=(const Point &p1, const Point &p2)

Equality and inequality operators work component-by-component.  No error tolerance is given - the floating point numbers are tested for exact equality.

    Point transform(const Matrix &m, const Point &p)
    Point vtransform(const Matrix &m, const Point &p)
    Point ntransform(const Matrix &m, const Point &p)

transform() assumes that p is a homogeneous point with w = 1 and multiplies it by the 4x4 matrix m.  The result is homogenized (i.e. divided through by the new w) and returned.  vtransform() assumes that p is a homogeneous vector with w = 0 and multiplies it by the 4x4 matrix m.  ntranform() also assumes that p is a vector with w = 0 but, instead, multiplies it by the inverse transpose of m.

    Point transforms(const char *tospace, const Point &p)
    Point transformm(const Matrix &m, const Point &p)
    Point transformss(const char *fromspace, const char *tospace, const Point &p)
    Point transformsm(const char *fromspace, const Matrix &m, const Point &v)
    Point vtransforms(const char *tospace, const Point &v)
    Point vtransformm(const Matrix &m, const Point &v)
    Point vtransformss(const char *fromspace, const char *tospace, const Point &v)
    Point vtransformsm(const char *fromspace, const Matrix &m, const Point &v)
    Point ntransforms(const char *tospace, const Point &n)
    Point ntransformm(const Matrix &m, const Point &n)
    Point ntransformss(const char *fromspace, const char *tospace, const Point &n)
    Point ntransformsm(const char *fromspace, const Matrix &m, const Point &n)

Additional forms of transform functions where the from and to spaces may each be specified by name (s) or by matrix (m).  In the cases where the from space is omitted, it is assumed to be the "current" coordinate system.  The SL library stores matrices for every built-in and user defined coordinate system.  This information is communicated to SL library directly from the renderer, bypassing all shaders.

    Point pminv(RtInt n, ...)
    Point pmaxv(RtInt n, ...)

Returns the minimum (or maximum) of a set of n points.  The result contains the independent minimum (or maximum) of all x, y, and z values.  For example: pmaxv(2, Point(1.0, 2.0, 3.0), Point(2.0, 2.0, 2.0)) returns (2.0, 2.0, 3.0).

    Point pclamp(const Point &f, const Point &min, const Point &max)

pclamp() returns min if f is less than min, max if f is greater than max; otherwise it returns f.  It works independently on x, y, and z.

    Point psplinev(const char *basis, float value, RtInt n, ...)

psplinev() fits a interpolating spline of the given basis to n control points.  At least four control points must always be given.  The basis may be "bspline", "bezier", "hermite", "linear", or "catmull-rom".

    Point prandom()

prandom() returns a point whose x, y, and z values are uncorrelated uniformly distributed random numbers between 0 and 1.

    Point pnoisef(RtFloat v)
    Point pnoiseff(RtFloat u, RtFloat v)
    Point pnoisep(const Point &pt)
    Point pnoisepf(const Point &pt, RtFloat w)

pnoise() returns a point whose components are independent pseuodrandom functions of the arguments; values are always between 0 and 1. The domain of this noise function can be 1-D (pnoisef), 2-D (pnoiseff), 3-D (pnoisep) or 4-D (pnoisepf).

    Point ppernoisef(RtFloat v, RtFloat period)
    Point ppernoiseff(RtFloat u, RtFloat v, RtFloat uperiod, RtFloat vperiod)
    Point ppernoisep(const Point &pt, const Point &period)
    Point ppernoisepf(const Point &pt, RtFloat w, const Point &pperiod, RtFloat wperiod)

Periodic noise behaves the same as regular noise but with the additional property that the return value repeats itself with the frequency given by the period argument.  In other words, ppernoise(v, period) == ppernoise(v + period, period)  For best results, all periods should be specified with integers.

    Point pcellnoisef(RtFloat v)
    Point pcellnoiseff(RtFloat u, RtFloat v)
    Point pcellnoisep(const Point &pt)
    Point pcellnoisepf(const Point &pt, RtFloat w)

Returns a pseudorandom point that depends upon the floor of all its arguments. The random values are uniformly distributed over the range from 0 to 1.  The results are repeatable: given the same input (or input similar enough that the floor of all arguments is identical) cellnoise will return the same value.

    RtFloat xcomp(const Point &p)
    RtFloat ycomp(const Point &p)
    RtFloat zcomp(const Point &p)
    void setxcomp(Point &p, RtFloat x)
    void setycomp(Point &p, RtFloat y)
    void setzcomp(Point &p, RtFloat z)
    RtFloat compp(const Point &p, RtFloat index)
    void setcompp(Point &p, RtFloat index, RtFloat value)

Overkill functions for getting and setting the x, y, and z values of a point.  Provided for easy mapping from the SL functions of the same names.  The compp() and setcompp() refer to x, y, and z with indeces 0, 1, and 2, respectively.

    Point pmix(const Point &p0, const Point &p1, RtFloat value)

pmix() returns the interpolated point ((1-value)*p0 + value*p1)

    RtFloat length(const Point &p)

Returns the euclidean length of the vector from the origin to p.

    RtFloat distance(const Point &p1, const Point &p2)

Returns the euclidean length of the vector from p1 to p2.

    RtFloat ptlined(const Point &p0, const Point &p1, const Point &q)

Returns the euclidean distance from point q to the nearest point on the line segment from p0 to p1.  Note that if the point nearest q on the infinite line through p0 and p1 does not lie between p0 and p1, it will be clamped to that range before the distance is taken.  In other words, ptlined() does not return the distance between q and an infinite line.

    Point normalize(const Point &p)

Returns the point divided by its length.  Does not modify original point.

    Point faceforward(const Point &n, const Point &i, const Point &g)

Returns n so that it faces in the direction opposite to i, from the point of view of the current surface element. The surface element's point of view is the defined by the normal vector g.

    Point reflect(const Point &i, const Point &n)

Return the reflection vector given an incident direction i and a normal vector n.

    Point refract(const Point &i, const Point &n, RtFloat eta)

Return the transmitted vector given an incident direction i, the normal vector n and the relative index of refraction eta. eta is the ratio of the index of refraction in the volume containing the incident vector to that of the volume being entered. This vector is computed using Snell's law.  If the returned vector has zero length, then there is no transmitted light because of total internal reflection.

    void fresnel5(const Point &i, const Point &n, RtFloat eta, RtFloat &Kr, RtFloat &Kt)
    void fresnel6(const Point &i, const Point &n, RtFloat eta, RtFloat &Kr, RtFloat &Kt, Point &r)
    void fresnel7(const Point &i, const Point &n, RtFloat eta, RtFloat &Kr, RtFloat &Kt, Point &r, Point &t)

Return the reflection coefficient Kr and refraction (or transmission) coefficient Kt given an incident direction i, the surface normal n, and the relative index of refraction eta.  eta is the ratio of the index of refraction in the volume containing the incident vector to that of the volume being entered.  These coefficients are computed using the Fresnel formula.  The fresnel6 and fresnel7 versions returns the reflected (r) vector.  The fresnel7 version also returns the transmitted (t) vector.  The transmitted vector is computed using Snell's law.

    Point rotate(const Point &q, RtFloat angle, const Point &p0, const Point &p1)

Returns point q after being rotated by angle radians (note: not degrees) about the axis that passes from p0 through p1.  The left-hand rule is followed, assuming the axis is in the direction from p0 towards p1.

    RtFloat depth(const Point &p)

Assumes that p.z is in camera coordinates.  The depth is normalized to lie between 0 (at the near clipping plane) and 1 (at the far clipping plane).

Color

The Color class has three member variables:

    RtFloat r, g, b

All member functions and member variables are public.  All member functions are inline to keep the interface strictly extern "C" and avoid name-mangling issues that a C++ interface brings.  Functions that were too complex to be made inline were made non-member functions with C interface.

    Color()
    Color(RtFloat c)
    Color(RtFloat rr, RtFloat gg, RtFloat bb)
    Color(const Color &c)
    Color(const Point &p)

Warning: The constructor without arguments is a null function.  It does not initialize r, g, and b.  This tradeoff was made in order to quickly declare color variables without constructor overhead.  The single argument constructor sets r, g, and b to the same value.  The copy constructor from point assigns x to r, y to g, and z to b.

    Color &operator=(const Color &c)

Straight assignment operator sets r, g and b to the same value.

    Color &operator+=(const Color &c)
    Color &operator-=(const Color &c)
    Color &operator*=(RtFloat m)
    Color &operator*=(const Color &c)
    Color &operator/=(RtFloat m)
    Color &operator/=(const Color &c)

These assignment operators work on a component-by-component basis.  All modify the existing color and return the result.  The division operators do nothing if any divisor is within FLT_EPSILON of 0 (see epsilon()).

    Color operator+(const Color &c1, const Color &c2)
    Color operator-(const Color &c1, const Color &c2)
    Color operator*(RtFloat f, const Color &c2)
    Color operator*(const Color &c1, RtFloat f)
    Color operator*(const Color &c1, const Color &c2)
    Color operator/(const Color &c1, RtFloat d)
    Color operator/(const Color &c1, const Color &c2)

Component-by-component operators return a new color without modifying either of the operands.  Three forms of multiplication are allowed: float times color, color times float, and color times color (componentwise).

    RtFloat dot(const Color &c1, const Color &c2)

Dot product binary operator doesn't modify the input operands.

    RtBoolean operator==(const Color &c1, const Color &c2)
    RtBoolean operator!=(const Color &c1, const Color &c2)

Equality and inequality operators work component-by-component.  No error tolerance is given - the floating point numbers are tested for exact equality.

    Color hsv(const Color &c)
    Color hsl(const Color &c)
    Color XYZ(const Color &c)
    Color xyY(const Color &c)
    Color YIQ(const Color &c)

Returns the color converted from another space to RGB.  Does not modify the input color.  The r, g and b values in the input are assumed to be values in the non-RGB space given in the same order as the name of that space.  For example, hsv() assumes r is h, g is s, and b is v.  In the hsv and hsl model, h must be in degrees.  XYZ follows CIE Rec 601-1 (C illuminant).

    Color cminv(RtInt n, ...)
    Color cmaxv(RtInt n, ...)

Returns the minimum (or maximum) of a set of n colors.  The result contains the independent minimum (or maximum) of all r, g, and b values.  For  example: cmaxv(2, Color(1.0, 2.0, 3.0), Color(2.0, 2.0, 2.0)) returns (2.0, 2.0, 3.0).

    Color cclamp(const Color &f, const Color &min, const Color &max)

cclamp() returns min if f is less than min, max if f is greater than max; otherwise it returns f.  It works independently on r, g, and b.

    Color csplinev(const char *basis, float value, RtInt n, ...)

csplinev() fits a interpolating spline of the given basis to n control colors. At least four control colors must always be given.  The basis may be "bspline", "bezier", "hermite", "linear", or "catmull-rom".

    Color crandom()

crandom() returns a point whose r, g, and b values are uncorrelated uniformly distributed random numbers between 0 and 1.

    Color cnoisef(RtFloat v)
    Color cnoiseff(RtFloat u, RtFloat v)
    Color cnoisep(const Point &pt)
    Color cnoisepf(const Point &pt, RtFloat w)

cnoise() returns a color whose components are independent pseuodrandom functions of the arguments; values are always between 0 and 1. The domain of this noise function can be 1-D (cnoisef), 2-D (cnoiseff), 3-D (cnoisep) or 4-D (cnoisepf).

    Color cpernoisef(RtFloat v, RtFloat period)
    Color cpernoiseff(RtFloat u, RtFloat v, RtFloat uperiod, RtFloat vperiod)
    Color cpernoisep(const Point &pt, const Point &period)
    Color cpernoisepf(const Point &pt, RtFloat w, const Point &pperiod, RtFloat wperiod)

Periodic noise behaves the same as regular noise but with the additional property that the return value repeats itself with the frequency given by the period argument.  In other words, cpernoise(v, period) == cpernoise(v + period, period)  For best results, all periods should be specified with integers.

    Color ccellnoisef(RtFloat v)
    Color ccellnoiseff(RtFloat u, RtFloat v)
    Color ccellnoisep(const Point &pt)
    Color ccellnoisepf(const Point &pt, RtFloat w)

Returns a pseudorandom point that depends upon the floor of all its arguments. The random values are uniformly distributed over the range from 0 to 1.  The results are repeatable: given the same input (or input similar enough that the floor of all arguments is identical) cellnoise will return the same value.

    RtFloat compc(const Color &c, RtFloat index)
    void setcompc(Color &c, RtFloat index, RtFloat value)

Note: these functions do not simply get and set r, g, and b.  Instead, they convert to and from the current color space as set by RiColorSamples and act in that space.

    Color cmix(const Color &color1, const Color &color2, RtFloat value)

cmix() returns the interpolated color ((1-value)*color1 + value*color2)

    Color ctransforms(const char *tospace, const Color &c)
    Color ctransformss(const char *fromspace, const char *tospace, const Color &c)

Converts from one colorspace to another.  In the ctransforms() version, fromspace is assumed to be "rgb".  Recognized spaces are "rgb", "hsv", "hsl", "xyz", "XYZ", "xyY", and "YIQ".  In the hsv and hsl models, h is in degrees.  XYZ and xyz follow CIE Rec 601-1 (C illuminant).

Matrix

The Matrix class has one member variable:

    RtFloat a[4][4]

All member functions and member variables are public.  All member functions are inline to keep the interface strictly extern "C" and avoid name-mangling issues that a C++ interface brings.

    Matrix()
    Matrix &operator=(const Matrix &m)

Warning: The constructor is a null function.  It does not initialize a.  This tradeoff was made in order to quickly declare Matrix variables without constructor overhead.

    Matrix &operator=(RtFloat d)

Initializes matrix with d on diagonal and zeroes elsewhere.  For example, Matrix m = 1 creates the identity matrix.

    Matrix projectss(RtToken fromstring, RtToken tostring)
    Matrix projectms(const Matrix &frommatrix, RtToken tostring)
    Matrix projectsm(RtToken fromstring, const Matrix &tomatrix)
    Matrix projectmm(const Matrix &frommatrix, const Matrix &tomatrix)

Returns matrix for transforming points and vectors from one space to another where the from and to spaces may each be specified by name (s) or by matrix (m).  The SL library stores matrices for every built-in and user defined coordinate system.  This information is communicated to SL library directly from the renderer, bypassing all shaders.

String

The SL library does not export a string class.  Instead, the string functions operate on null-terminated C strings.  Functions that return strings statically allocate the return value within each function and overwrite it upon each call.

    char *concatv(RtInt n, ...)

Returns the concatenation of n strings.

    char *format(const char *pattern, ...)

Much like sprintf(3S), pattern is a format control string that may contain conversion specifiers such as "%f".  Arguments which follow are converted and formatted under control of the string.  The resulting string is returned.

    RtBoolean match(const char *pattern, const char *subject)

Returns RI_TRUE if the regular expression specified by pattern is found anywhere in the subject string, and returns RI_FALSE otherwise.  The format of the regular expression is defined as follows:

   1. A character matches itself, unless it is a special character (metachar): . \ [ ] * + ^ $
   2. A period "." matches any character.
   3. A backslash "\" matches the character following it, except when followed by a left or right round bracket, a digit 1 to 9 or a left or right angle bracket (see [7], [8] and [9]). It is used as an escape character for all other meta-characters, and itself. When used in a set ([4]), it is treated as an ordinary character.
   4. A set of characters enclosed in square brackets "[" and "]" matches one of the characters in the set. If the first character in the set is "^", it matches a character NOT in the set, i.e. complements the set. A shorthand S-E is used to specify a set of characters S upto E, inclusive.  The special characters "]" and "-" have no special meaning if they appear as the first chars in the set.  For example:
         [a-z] matches any lowercase alpha
         [^]-] matches any char except ] and -
         [^A-Z] matches any char except uppercase alpha
         [a-zA-Z] matches any alpha.
   5. Any regular expression form [1] to [4], followed by an asterisk "*" matches zero or more matches of that form.
   6. A plus sign "+" behaves the same as [5], except it matches one or more.
   7. A regular expression in the form [1] to [10], enclosed by "\(" and "\)" matches the regular expression but also creates a set of tags, used for [8] and for pattern substution. The tagged forms are numbered starting from 1.
   8. A backslash "\" followed by a digit 1 to 9 matches whatever a previously tagged regular expression ([7]) matched.
   9. A regular expression starting with a "\<" construct and/or ending with a "\>" construct, restricts the pattern matching to the beginning of a word, and/or the end of a word. A word is defined to be a character string beginning and/or ending with the characters A-Z a-z 0-9 and _.  It must also be preceded and/or followed by any character outside those mentioned.
  10. A composite regular expression xy where x and y are in the form [1] to [10] matches the longest match of x followed by a match for y.
  11. A regular expression starting with a caret "^" character and/or ending with a dollar "$" character, restricts the pattern matching to the beginning of the line, or the end of line. Elsewhere in the pattern, ^ and $ are treated as ordinary characters.

Lighting

Lighting information is passed from light source shaders (with solar or illuminate statements) to surface shaders (with illuminance statements) using a linked list data structure:

    struct SLlight {
        SLlight *next;
        Point L;                            // Ray direction
        Color Cl;                           // Ray color
        Color Ol;                           // Ray opacity
    };

Each element of the list carries a direction, color and opacity.  In light shaders, L points from the light to the surface being illuminated.  In surface shaders, the direction of L is reversed.  It is customarily the responsibility of the light shader to invert the direction of L after the illuminate or solar loop.

    SLlight *newlight()
    void freelight(SLlight *p)

These functions allocate and free a light node.

    Color diffuse(const SLlight *lights, const Point &N)
    Color specular(const SLlight *lights, const Point &N, const Point &V, RtFloat roughness)
    Color phong(const SLlight *lights, const Point &N, const Point &V, RtFloat size)

These functions iterate over the light list and sum various components of the RenderMan shading model.  L is assumed to point from the surface to the light.  Note that input vectors N and V are not automatically normalized.

For lights with L pointing in the hemisphere centered at N, diffuse() returns the sum of Cl * dot(L, N).

For the same lights, phong() calculates R = reflect(-V, N) and returns the sum of Cl * pow(dot(R, N), size).

specular() iterates over all lights and returns the sum of Cl * specularbrdf(L, N, V, roughness).

    RtBoolean illuminates(const Point &position, const Point &axis, RtFloat angle, const Point &Ps)

illuminates() returns RI_TRUE if the point Ps is in the light cone defined by position (the apex of the cone), axis, and angle (in radians).

    RtBoolean illuminance(const Point &axis, RtFloat angle, const Point &L)

illuminance() returns RI_TRUE if the vector L points in a direction within angle radians of axis.  Consider a cone with its apex at the origin and
flairing angle radians about axis.  Then illuminance() returns RI_TRUE if L is in that cone.  It turns out that the choice of apex is arbitrary.

    Color standardbrdf(const Point &L, const Point &N, const Point &V, RtFloat roughness)
    Color bmrtbrdf(const Point &L, const Point &N, const Point &V, RtFloat roughness)
    Color prmanbrdf(const Point &L, const Point &N, const Point &V, RtFloat roughness)
    Color rdcbrdf(const Point &L, const Point &N, const Point &V, RtFloat roughness)
    Color torrancebrdf(const Point &L, const Point &N, const Point &V, RtFloat roughness)

Various implementations of built-in Bidirectional Reflectance Distribution Functions (BRDF's).  Parameters are vector from surface to light, shading normal, vector from surface to viewer, and surface roughness.  None of the input vectors are automatically normalized.

    typedef Color (*RtBrdfFunc)(const Point &, const Point &, const Point &, RtFloat)
    extern RtBrdfFunc RI_IMPORT specularbrdf

Function pointer to the currently selected BRDF function.  The user may choose any one of the five BRDF functions above.

    Color trace(const Point &, const Point &)

Returns black unless renderer implements ray tracing capability.

Texture Mapping

These functions take string-value pairs terminated by a null pointer.  For example:

    ctexture(texturename, (RtInt)0, s0, t0, s1, t1, s2, t2, s3, t3, "width", 5.0, (const char *)0))

Recognized strings are "fidelity", "samples", "swidth", "twidth", "width", "fill", "lerp", "filter", "blur", "sblur", "tblur", and "bias".  See the RenderMan spec and RDC's slext for more information on these strings.

In all cases below, name is the filename of the map and channel is the base channel number for the map lookup.

    RtFloat ftexture(const char *name, RtInt channel, RtFloat s0, RtFloat t0, RtFloat s1, RtFloat t1, RtFloat s2, RtFloat t2, RtFloat s3, RtFloat t3, ...)
    Color ctexture(const char *name, RtInt channel, RtFloat s0, RtFloat t0, RtFloat s1, RtFloat t1, RtFloat s2, RtFloat t2, RtFloat s3, RtFloat t3, ...)

These functions perform a plain-texture map lookup filtered over the quadrilateral defined by the 8 texture coordinates.  ftexture() reads a single channel and ctexture() reads three consecutive channels.

    RtFloat fenvironment(const char *name, RtInt channel, const Point &d0, const Point &d1, const Point &d2, const Point &d3, const char *token1, ...)
    Color cenvironment(const char *name, RtInt channel, const Point &d0, const Point &d1, const Point &d2, const Point &d3, const char *token1, ...)

The environment() functions lookup grayscale or color data filtered over the directions defined by the 4 input vectors.

    RtFloat shadow(const char *name, RtInt channel, const Point &d0, const Point &d1, const Point &d2, const Point &d3, const char *token1, ...)

shadow() uses percentage closer filtering to return the fraction of the surface that is farther from the light than the surface recorded in the depth map.  The coordinates are directions from the light source.

    Point bump(const char *)

Always returns Point(0, 0, 0)

Runflags

The following Abstract Data Types (ADT) are useful in simulating Single Instruction Multiple Data (SIMD) control flow on a single CPU system.

    struct Set

An opaque type.  Implements sets using bitvectors.  Potential members of a set are numbered 0 through nmembers - 1.

    struct Set *set_create(int nmembers)

Creates a new set of maximum size nmembers.  Returns opaque pointer to new set.  Warning: membership is left uninitialized.  Do not assume that a new set is an empty set.

    struct Set *set_copy(const struct Set *s)

Creates a new set by duplicating an existing set.  Copies membership info.

    struct Set *set_assign(Set *dest, const struct Set *s)

Quickly copies set membership info in set s to identically-sized set dest.  Warning: both sets must have the same number of members before function call.  Returns dest.

    void set_destroy(struct Set *s)

Frees memory associated with set s.

    void set_makeempty(struct Set *s)
    void set_makefull(struct Set *s)

set_makeempty() removes all members from the set.  set_makefull() adds every potential member to the set.

    void set_insert(struct Set *s, int n)
    void set_remove(struct Set *s, int n)

Inserts (or removes) member number n from set s.

    int set_ismember(const struct Set *s, int n)

Returns 0 if n is not a member of set s, non-zero otherwise.  Warning: does not necessarily return 1 for TRUE.

    int set_isempty(const struct Set *s)

Returns 1 if set has no members, 0 otherwise.

    void set_intersection(struct Set *s, const struct Set *s2)

Modifies set s to contain the intersection of sets s and s2.

    void set_union(struct Set *s, const struct Set *s2)

Modifies set s to contain the union of sets s and s2.

    void set_not(struct Set *s)

Inverts the membership of set s.

    struct RunFlag

An opaque type.  Implements a stack of Sets.

    struct RunFlag *runflag_create(int nprocs)

Creates a new stack of sets with up to nprocs members each.  Initially, there is one element in the stack and it is made full (i.e. all bits are set).

    void runflag_destroy(struct RunFlag* rf)

Frees memory associated with this stack.

    int runflag_isset(const struct RunFlag *rf, int proc)

Returns 0 if proc is not a member of the set at the top of stack rf.  Returns non-zero otherwise.  Warning: does not necessarily return 1 for TRUE.

    int runflag_anyset(const struct RunFlag *rf)

Returns 1 if the set at the top of stack rf has any members.  Returns 0 if it is empty.

    void runflag_push(struct RunFlag *rf)

Takes the set at the top of stack rf and pushes a copy of it onto the stack.

    void runflag_pop(struct RunFlag *rf)

Removes the set at the top of stack rf.

    void runflag_if(struct RunFlag *rf, struct Set *c)

Takes the intersection of the set atop rf and set c.  Replaces the top of the stack with the intersection.

    void runflag_suspend(struct RunFlag *rf, int proc, int levels)

Removes member proc from the top levels sets of stack rf.  If levels = 1, then proc is simply removed from the set at the top of the stack.

    void runflag_else(struct RunFlag *rf)

First, replaces the set at the top of stack rf with its negation.  Second, if there's another level below the top of the stack, takes the intersection
of the top two levels of the stack and overwrites the top of the stack with that set.

DSO Shadeops

The following Abstract Data Type (ADT) manages user-provied DSO's loaded to assist in shading.

    struct DsoShadeop

An opaque type.  One instance represents a prototype of a C/C++ function that has been requested by a shader.

    struct DsoShadeop *dsolookup(const char *filename, const char *slname, const char *cname, const char *prototype)

Looks up a C/C++ function given the DSO filename, the name of the function as it appears in SL, the name of the function in C, and the complete function prototype.  For example:

    shadeopptr = dsolookup("sqr.so", "sqr", "sqr_f", "float sqr_f(float)")

dsolookup uses hash tables to only create one DsoShadeop per unique function prototype and to only call shared init() functions once per DSO file.

    RtFloat fdso(struct DsoShadeop *h, const char *format, ...)
    Point pdso(struct DsoShadeop *h, const char *format, ...)
    Color cdso(struct DsoShadeop *h, const char *format, ...)
    const char *sdso(struct DsoShadeop *h, const char *format, ...)
    RtVoid vdso(struct DsoShadeop *h, const char *format, ...)

Once you have a handle to a DsoShadeop, use one of these functions to actually call the method.  The first letter of the function name designates the return type.  Other than that, all five varieties are identical.  The format string gives the types and number of the arguments that follow, much like printf except without the percent signs.  For example, a format string "fpp" means that one float and two points will follow.  Possible letters in format are 'f' for float, 'p' for point, 'c' for color, and 's' for string.  Floats, points, and colors are passed as (RtFloat *): pointers to single precision floating point arrays.  Strings are passed as (char **).  Note the double indirection.

Miscellaneous

    RtFloat incident(const char *, RtFloat)
    RtFloat incident(const char *, const Color &)
    RtFloat incident(const char *, const Point &)
    RtFloat opposite(const char *, RtFloat)
    RtFloat opposite(const char *, const Color &)
    RtFloat opposite(const char *, const Point &)

These all return 0 unless the renderer implements volume shaders.

    void slprintf(const char *format, ...)

slprintf() is just like printf() except it handles points (%p) and colors (%c).

    RtFloat atmosphere(const char *, ...)
    RtFloat displacement(const char *, ...)
    RtFloat lightsource(const char *, ...)
    RtFloat surface(const char *, ...)

These all return 0 unless the renderer implements message passing.


Copyright © 2000-2006 Dot C Software, Inc. All rights reserved.
Dot C Software, Inc., 182 Kuuhale St., Kailua, HI 96734
(808) 262-6715 (voice) (808) 261-2039 (fax)
The RenderMan® Interface Procedure and RIB Protocol are:
Copyright © 1988, 1989, Pixar. All rights reserved.
RenderMan® is a registered trademark of Pixar.