Class 4: Global illumination (GI) shaders; shadeops
GI calls

GI support was added to PRMan in the fall of 2002, with the release of "PRMan 11". This is the most significant upgrade for PRMan in recent years. For the shader writer, all of the new functionality is available via the following RSL calls and a new looping construct (please see the RI Spec. for details):


GI RSL calls (just nine of them!)

In addition to the above calls, there is also a new GI-related looping construct called gather(){}, which collects (indirect light) from surfaces via raytracing (analogous to the illuminance(){} construct which collects direct light). Likewise, the traverse(){} loop is useful for processing ray hits in order to programmatically control ray continuation through multiple translucent surfaces.


Here are scans of a glossy PRMan 11 brochure that outline the new functionality:

GI shaders

Following are some examples of shaders that contain the GI-related shader calls listed above, and RIB files to test them with. Please see the 'RfB' book for more examples. Also, Pixar's 'Application Notes' #32, #34, #35, #36 and #37 contain useful information related to GI.

rtrace raytracing surface shader
rtrace_test RIB

tintedmirror raytracing shader
checker checkerboard shader
tintedmirror_test RIB

ids colorbleed shader
wh.tex a 'white' texure map
colorbleed_test RIB

spotlight_rts ray-tracing shader
causticlight
simplemirror
ChromePMGen.rib photomap generation
ChromePMUse.rib photomap use
[the causticlight light computes caustics using a .cpm map]

Trefoil SSS, RIB
subsurface_adaptive.sl SSS shader

Shadeops [compiled RSL functions]

Shadeops are simply new RSL calls (precompiled and contained in a separate file, ie. not part of the .sl code) that you can invoke in your shaders. For instance if you want to provide a drop-in replacement for the built-in noise() call and want to call your version noisy(), you'd implement noisy() as a "shadeop". It means that you'd code noisy() as a C/C++ program (eg. noisy.c), compile it, then turn it (the compiled noisy.o object file) into a "shared object" (eg. called noisy.so or on Windows, noisy.dll) and place it in a directory known to the shader compiler. After that, you can use noisy() in a .sl file instead of noise() and compile the .sl file as usual. Now the compiled shader (along with the shadeop file) is ready for use in RIB files. When rendering, PRMan would run your shader which in turn would cause PRMan to load and run noisy() from the shadeop file.

Why create shadeops instead of simply creating functions in RSL? Shadeops run much faster than equivalent RSL functions. Also, RSL functions can only be comprised of other RSL calls, whereas a shadeop can use just about ANY piece of code in ANY library or source file.

Adding C Functions to Shading Language with DSOs is a Pixar document (dated 9/99) that explains shadeops in more detail and provides a simple example to use as a template for your own shadeops.

Let's work through the sqr.c example from the above document. In order to compile it, you'll also need the shadeop.h header file.

Here are the two commands (under gcc, using cygwin's 'bash' on Windows XP):

The output from the commands is the file 'sqr.dll' which contains the code for our sqr() function.

To try it out, create this testsqr.sl surface shader:


surface testsqr (float angle = 0.5;)
{
float x = 2;
vector y = vector (1, 2, 3);
printf ("x = %f, sqr(x) = %f\n", x, sqr(x));
printf ("y = %p, sqr(y) = %p\n", y, sqr(y));
}
Compile it as usual, with 'shader testsqr.sl' (or 'shader -I. testsqr.sl) to obtain testsqr.slo.

Now we're ready to render using this shader. Render this RIB file which contains a 'Surface testsqr' call to our new shader that will invoke the sqr.dll shadeops. When you render, you should see output such as this:

This tells us that our shadeops are properly getting called during runtime (rendering).

As an exercise, try making wnoise() shadeops using code at the end of Cook and De Rose's 'Wavelet noise' paper from SIGGRAPH 2005.