Let's start with the interaction between light and surface shaders. I explain the details while we are doing a few tests. Please read section 5 for more details about light shaders.
We create a very simple RIB file:
# test.rib
Exposure 1.0 2.2
Display "test.tiff" "file" "rgba"
Format 508 380 1.0
Projection "perspective" "fov" [ 90 ]
WorldBegin
LightSource "pointlight" 0
AttributeBegin
Surface "plastic"
Translate 0 0 1
Patch "bilinear"
"P" [ -1.0 -1.0 0.0 1.0 -1.0 0.0 -1.0 1.0 0.0 1.0 1.0 0.0 ]
AttributeEnd
WorldEnd
You could render this RIB file e.g. with render test.rib and
check that there is something in front of your camera before you start
doing your own tests. It's a simple bilinear patch with 4 control
points in front of the camera with a field of view of 90 degrees and
translated along the positive z-axis18.
Now we start to replace the surface shader and the light shader with
our own shaders. But first change the resolution of the resulting
image. Change the line Format 508 380 1.0 to
Format 5 4 1.0. This will reduce the resolution to only a few
pixels in each direction. You will soon see why. The next thing
which might help compiling your shaders every time you change a line
is to create a Makefile and automate the rendering and shader
compiling:
# Makefile
SHADER = shader
RENDER = render
EXT = slo
all: test.tiff
myLight.$(EXT): myLight.sl
$(SHADER) myLight.sl
mySurface.$(EXT): mySurface.sl
$(SHADER) mySurface.sl
test.tiff: test.rib myLight.$(EXT) mySurface.$(EXT)
$(RENDER) test.rib
clean:
-rm -f *~ *.$(EXT) test.tiff
Now you just type make to compile the shaders and render a
picture called test.tiff. Go back to the RIB file and change
the lines where the shaders are called to use your own light and
surface shader:
...
LightSource "myLight" 0
AttributeBegin
Surface "mySurface"
...
We are ready to create the two shaders. For a start we just print some information that the shader was called. That's why we reduced the image resolution to create only a few lines of output. The light shader should look like this:
/* myLight */
light
myLight()
{
printf("myLight\n");
}
The surface shader looks very similar:
/* mySurface */
surface
mySurface()
{
printf("mySurface\n");
}
But if you render the image only output from the surface shader is
printed. That means the light shader never gets called. OK, maybe we
should use the output variables. Leave the printf line as it is
but add other lines to make the surface shader act like the standard
constant shader:
/* mySurface */
surface
mySurface()
{
printf("mySurface\n");
Oi = Os;
Ci = Os * Cs;
}
The light shader should be similar to the standard
ambientlight:
/* myLight */
light
myLight()
{
printf("myLight\n");
Cl = color (1.0, 1.0, 1.0);
}
We don't use shader parameters at the moment. Most of the time you will start with fixed values in your shaders and make them step by step available as shader parameters later to give the users of your shaders the possibility to change things from outside your shader without changing the shader itself anymore.
If you run the test again you will notice that the light shader still is not called. Why? Well, the surface shader has to call some functions to invoke the light shader. Let's replace the last line in the surface shader:
Ci = Os * Cs * ambient();
Our ambient light source shader will now be called. The light shader
acts like an ambient light source because it does not use a
illuminate or solar statement. See section
5.1.
The output of the shaders might look different for several renderers. For PRMan first only calls to the surface shader are made; after that only calls to the light shader are made19. For BMRT or AIR the calls are intermixed; one call to the surface shader, one call to the light shader, etc. This shouldn't concern you too much as long as both shaders create the same output image for all RenderMan compliant renderers. But it shows for the first time that things might be handled differently for all this RenderMan compliant renderers.
For a complete list of Shading and Lighting Functions I refer to the The RenderMan Interface document from Pixar. Here just a list of functions without further details20:
matte, metal, shinymetal, plastic, or
paintedplastic.
matte, plastic, or
paintedplastic.
metal,
shinymetal, plastic, or paintedplastic.
specular() function, even
if the renderer has an implementation-specific formula for
built-in specular reflection.
glassrefr shader.
Now let's have a look at some of this functions and talk about something else which is important in a real production pipeline. It's very convenient to render several passes of a scene and do adjustments afterwards with a compositing software.
Set the resolution back to the values it had before, get rid of the
printf() statements, and change the surface shader to:
/* mySurface */
surface
mySurface(float roughness = 0.1)
{
normal Nf = faceforward(normalize(N), I);
Oi = color 1;
Ci = (color(1, 0, 0) * ambient() +
color(0, 1, 0) * diffuse(Nf) +
color(0, 0, 1) * specular(Nf, normalize(-I), roughness));
}
The result is not very exciting because the light shader still returns only the ambient lighting. Let's change that21:
/* myLight */
light
myLight(
float intensity = 1;
color lightcolor = 1;
point from = point "shader" (0,0,0);
)
{
illuminate(from)
Cl = intensity * lightcolor / L.L;
}
Basically you separate and color encode the ambient, diffuse, and specular contribution. The ambient term is in the red component, the diffuse term in the green component, and the specular in the blue component. In the compositing program you can separate the three components into three grey scale pictures, multiply it with any color you want, apply filters e.g. blur the highlight from the specular contribution more in x-direction than in the y-direction etc. This can save a lot of rendering time for a complicated geometry to match the lighting and coloring conditions in life action. You could do that in theory for every light. I use it here only to show you that you should keep the post-production in mind and that you might use basically the same RIB file for several passes and modify certain parts to create several pictures which are useful for compositors.
The example scene is not very interesting. So replace the linear patch through a sphere and add an ambient light source to get an extra light contribution:
# test.rib
Exposure 1.0 2.2
Display "test.tiff" "file" "rgba"
Format 508 380 1.0
Projection "perspective" "fov" [ 90 ]
WorldBegin
LightSource "ambientlight" 0
LightSource "myLight" 1
AttributeBegin
Surface "mySurface"
Translate 0 0 2
Sphere 1 -1 1 360
AttributeEnd
WorldEnd
Exercises: