Errata for the RenderMan Companion

(Second Printing)

Pixar
August, 1990

This is a list of known errors in the second printing of The RenderMan Companion, including syntax errors, typographical mistakes and omissions that make the code listings incorrect as they appear in the book. Extra code required to make the examples run, such as camera setup for the geometry chapters, is not discussed here.

Chapter 2

p. 25 -- Listing 2.3: There are two problems with the initialization of the array Cube[6][4]. First, commas are missing between the second and third points and also between the third and fourth points in the bottom face definition. Second, the polygons' normals all face towards the center of the cube. This is a somewhat subtle error, but an error nonetheless. Note that the second and fourth columns of data are switched. The cube's definition should be as follows:

static RtPoint Cube[6][4] = {
    { {L,D,F}, {L,D,N}, {R,D,N}, {R,D,F} },  /* Bottom face */
    { {L,D,F}, {L,U,F}, {L,U,N}, {L,D,N} },  /* Left face   */
    { {R,U,N}, {L,U,N}, {L,U,F}, {R,U,F} },  /* Top  face   */
    { {R,U,N}, {R,U,F}, {R,D,F}, {R,D,N} },  /* Right face  */
    { {R,D,F}, {R,U,F}, {L,U,F}, {L,D,F} },  /* Far face    */
    { {L,U,N}, {R,U,N}, {R,D,N}, {L,D,N} }   /* Near face   */
};

Chapter 4

p. 67 -- Listing 4.3: The call to RiSphere() in the middle of TorusWave() has an incorrect argument list. The comment and code should read as follows:

/* Create the cap for the center of the wave */
RiSphere( innerrad, -innerrad, 0.0, thetamax, RI_NULL);

Chapter 5

p. 76 -- Listing 5.2: The #include reference to surfor.h should not be there. Note that the other #include file pin.hyper.h is not presented.

The comment in PolySurfOR() refers to "the goblet description". This should read "the surface profile".

PolyBoid() is supposed to be provided as a triangular polygon approximation to hyperboloids, with the addition of suitable surface normals. It is meant as an improvement to the PolyBoid() function in Listing 5.1. What is provided instead is a function called PolyBand(), which is semantically identical to the PolyBoid() function presented in Listing 5.3. This function illustrates the RiPointsPolygons() interface call. Replace PolyBand() with the PolyBoid() function shown below:

/* ================================================================ */

#define SWAP(a,b,temp) temp = a; a = b; b = temp;
#define COPY_POINT(d, s)           {d[0]=s[0]; d[1]=s[1]; d[2]=s[2];}

PolyBoid(point0, point1, normal0, normal1, ndivs, parity)
RtFloat *point0, *point1, *normal0, *normal1; int ndivs, parity;
{
	RtPoint 	vertexpair0[2], vertexpair1[2],
			*ptrnextvertex = vertexpair0,
			*ptrlastvertex = vertexpair1,
			*temp,
			vertextriangle[3];
	RtPoint 	normalpair0[2], normalpair1[2],
			*ptrnextnormal = normalpair0,
			*ptrlastnormal = normalpair1,
			normaltriangle[3];
	int i;

	getnextpair(0+parity/2.0, ptrnextvertex, point0, point1, ndivs);
	getnextpair(0+parity/2.0, ptrnextnormal, normal0, normal1, ndivs);
	for (i = 1; i <= ndivs; i++) {
		SWAP(ptrlastvertex, ptrnextvertex, temp)
		SWAP(ptrlastnormal, ptrnextnormal, temp)
		getnextpair(i+parity/2.0, ptrnextvertex,
			point0, point1, ndivs);
		getnextpair(i+parity/2.0, ptrnextnormal,
			normal0, normal1, ndivs);
		COPY_POINT(vertextriangle[0], ptrlastvertex[0]);
		COPY_POINT(vertextriangle[1], ptrlastvertex[1]);
		COPY_POINT(vertextriangle[2], ptrnextvertex[1]);
		COPY_POINT(normaltriangle[0], ptrlastnormal[0]);
		COPY_POINT(normaltriangle[1], ptrlastnormal[1]);
		COPY_POINT(normaltriangle[2], ptrnextnormal[1]);
		RiPolygon(3, RI_P, (RtPointer) vertextriangle, RI_N,
			(RtPointer) normaltriangle, RI_NULL);
		COPY_POINT(vertextriangle[0], ptrnextvertex[0]);
		COPY_POINT(vertextriangle[1], ptrnextvertex[1]);
		COPY_POINT(vertextriangle[2], ptrlastvertex[0]);
		COPY_POINT(normaltriangle[0], ptrnextnormal[0]);
		COPY_POINT(normaltriangle[1], ptrnextnormal[1]);
		COPY_POINT(normaltriangle[2], ptrlastnormal[0]);
		RiPolygon(3, RI_P, (RtPointer) vertextriangle, RI_N,
			(RtPointer) normaltriangle, RI_NULL);
	}
}

/* ================================================================ */

Chapter 6

p. 101 -- Listing 6.3: All references to wrap and nowrap surfaces should be changed to periodic and nonperiodic. The text in this section of the book should be updated to reflect this fact: the term RI_NOWRAP should be changed to RI_NONPERIODIC, and RI_WRAP should be changed to RI_PERIODIC.

Chapter 7

p. 128 -- Listing 7.2: #include <math.h> should be at the top of this listing to support the calls to sqrt().

Chapter 8

p. 154 -- Listing 8.4: The function min() does not exist in the math library, but can be #defined with:

#define min(a,b) ((a)<(b)?(a):(b))

The fov parameter in RiProjection() should be in degrees, not radians. This can be done with the following change:

fov = 2 * atan((min(framewidth,frameheight)*.5)/focallength);

should read

fov = 2 * atan((min(framewidth,frameheight)*.5)/focallength)
    *180.0/3.14159;

Chapter 10

p. 205 -- Listing 10.3: In addition to <ri.h>, <math.h> and <stdio.h> should be #included.

There is no presentation of RiProcedural() in this example. The following code should be inserted between the typedef of FractalTriangle() and the #definition of MOVEPT.

/* ================================================================ */

Go()
{
    int childnum;
    FractalTriangle mytriangle;
    FractalPoint *pVertex;
    RtBound bound;

    pVertex = &(mytriangle.vertices[0]);
    pVertex->location[0] = -1;
    pVertex->location[1] = -0.5;
    pVertex->location[2] = 0;
    (pVertex++)->seed = random();
    pVertex->location[0] = 1;
    pVertex->location[1] = -0.5;
    pVertex->location[2] = 0;
    (pVertex++)->seed = random();
    pVertex->location[0] = 0;
    pVertex->location[1] = 0.5;
    pVertex->location[2] = 1;
    (pVertex++)->seed = random();
    mytriangle.level = 0;
    mytriangle.children = (FractalTriangle *) NULL;
    TriangleBound( &mytriangle, bound );
    RiProcedural( &mytriangle, bound, FractalDiv, FractalFree );
}

/* ================================================================ */

The call to RiProcedural() in TriangleSplit() is missing a semicolon (;) at the end of the line. Also, "data" should be of type RtPointer, not char *. Actually, this doesn't matter much, because TriangleSplit() needs to be fixed, as noted below.

FractalDiv() does not correctly split a triangle and render its children; FractalDiv() and TriangleSplit() should be modified to read as follows:

/* ================================================================ */

#define MAXLEVELS 5
/*
 * FractalDiv(): RenderMan refinement procedure for subdividing a fractal
 *	triangle.
 */
FractalDiv( data, levelofdetail )
RtPointer data;
RtFloat levelofdetail;
{
    FractalTriangle *pTriangle = (FractalTriangle *)data, *pChild;
    RtPoint vertices[3];
    RtBound bound;
    int nchildren;

    if (levelofdetail<1.0 || pTriangle->level>MAXLEVELS ) {
    /* Small enough to be rendered */
	    MOVEPT(pTriangle->vertices[0].location, vertices[0]);
	    MOVEPT(pTriangle->vertices[1].location, vertices[1]);
	    MOVEPT(pTriangle->vertices[2].location, vertices[2]);
	    RiPolygon( 3, RI_P, vertices, RI_NULL );
    } else {				/* Too large; subdivide */
	    if(!pTriangle->children)
		TriangleSplit( pTriangle );
	    pChild = pTriangle->children;
	    nchildren = 4;
	    while( nchildren-- ) {
		/* TriangleBound() computes the bounding box for a triangle */
		TriangleBound( pChild, bound );
		RiProcedural( pChild++, bound, FractalDiv, FractalFree );
	    }
    }
}

TriangleSplit( pFT )
FractalTriangle *pFT;
{
    int childnum;
    FractalTriangle *pChildren;

    pFT->children =
	pChildren = (FractalTriangle *)malloc( 4*sizeof(FractalTriangle) );
    for(childnum = 0; childnum < 4; childnum++) {
	pChildren[childnum].children = NULL;
	pChildren[childnum].level = pFT->level+1;
    }
    for( childnum = 0; childnum < 3; childnum++ ) {
	pChildren[childnum].vertices[0] = pFT->vertices[childnum];
	EdgeSplit( &(pFT->vertices[childnum]),
		   &(pFT->vertices[(childnum+1)%3]),
		   &(pChildren[childnum].vertices[1]));
    }
    for( childnum = 0; childnum < 3; childnum++ ) {
	pChildren[3].vertices[childnum] =
	    pChildren[(childnum+1)%3].vertices[2] =
		pChildren[childnum].vertices[1];
    }
}

/* ================================================================ */

A FractalFree() function is not provided in . Append the following code to Listing 10.3.

/* ================================================================ */

FractalFree( data )
RtPointer data;
{
    FractalTriangle *pTriangle = (FractalTriangle *)data;
    if(pTriangle->level == 0)
	FreeChildren( pTriangle->children );
}

FreeChildren( pFTChildren )
FractalTriangle *pFTChildren;
{
    if( pFTChildren ) {
	FreeChildren( pFTChildren );   free( pFTChildren++ );
	FreeChildren( pFTChildren );   free( pFTChildren++ );
	FreeChildren( pFTChildren );   free( pFTChildren++ );
	FreeChildren( pFTChildren );   free( pFTChildren++ );
    }
}

/* ================================================================ */

Chapter 11

p. 216: RiMatte() has a boolean flag which enables/disables (RI_TRUE/RI_FALSE) the matte attribute.

RiMatte(onoff)
RtBoolean onoff;

p. 227 -- Figure 11.6: I+L should be L-I (to reverse the direction of I). The lengths of I and L should also be normalized before averaging to get H and this should be implied in the figure by making the two vectors the same length. In actuality no equation is really needed at all for H, as the fact that angles A and A are equal, and the description of H in the text are sufficient.

Chapter 12

p. 253 -- Listing 12.1: The order of the points in textcoords[] does not agree with corners[]. The line:

textcoords[] = {{0,0}, {0.5,0}, {0.5,0.5}, {0,0.5}};

should read:

textcoords[] = {{0,0}, {0.5,0}, {0,0.5}, {0.5,0.5}};

p. 254 -- Listing 12.2: In addition to <ri.h>, <math.h> should be #included because of the call to sqrt().

In TextSurfOR(), "tmap" is used as an interface variable, but it is not declared. The call to RiSurface() must be prefaced by the RiDeclare() call:

RtToken RI_TMAP;
RI_TMAP = RiDeclare("tmap", "uniform string");
RiSurface("mytexture", RT_TMAP, (RtPointer)&tmap, RI_NULL);

p. 266 -- Listing 12.3: At the top of the file, add

#include <stdio.h>

before <ri.h>.

The declaration of "filename" should be done outside of the main() function. Change the line

char filename[BUFSIZE], tfilenames[6][BUFSIZE+4];

to read

char tfilenames[6][BUFSIZE+4];

and put the line

char filename[BUFSIZE];

immediately after the CameraFrom declaration at the top of the file.

In the call to RiDeclare(), change the argument "reflmap" to "mapname". The surface used for the sphere is the "shiny" surface, which has "mapname" as its parameter.

The declaration of parameters in Snap() should be changed. "tfilename" should be recognized as a pointer to char, and "resolution" is really an RtInt.

Snap( direction, tfilename, resolution )
char *direction, tfilename;
int resolution;

should read

Snap( direction, tfilename, resolution )
char *direction, *tfilename;
RtInt resolution;

In Snap(), CameraTo is treated as if it were a camera direction. Change the following lines:

CameraTo[0] = CameraTo[1] = CameraTo[2] = 0.0;
switch(direction[0]) {
	case 'x': axis = 0; break;
	case 'y': axis = 1; break;
	case 'z': axis = 2; break;
}
CameraTo[axis] = (direction[1] == '-') ? -1 : 1;

to read

CameraTo[0] = CameraFrom[0];
CameraTo[1] = CameraFrom[1];
CameraTo[2] = CameraFrom[2];
switch(direction[0]) {
	case 'x': axis = 0; break;
	case 'y': axis = 1; break;
	case 'z': axis = 2; break;
}
CameraTo[axis] += (direction[1] == '-') ? -1 : 1;

The coercion of 0.0 to RtFloat in the call to PlaceCamera() is not appropriate. The line should read:

PlaceCamera(CameraFrom, CameraTo, 0.0);

p. 267 -- Listing 12.4: Since chapter 16 presents a reflection map shader ("shiny"), it will be used in this listing. Note that this shader uses "mapname" rather than "reflmap". Change

RiSurface("myreflector",
	(RtToken)"reflmap", (RtPointer) &mapname,
	RI_NULL);

to read

RiSurface("shiny",
	(RtToken)"mapname", (RtPointer) &mapname,
	RI_NULL);

The second argument of RiTranslate() is wrong. Replace

RiTranslate(CameraFrom[0], CameraFrom[2], CameraFrom[2]);

to read

RiTranslate(CameraFrom[0], CameraFrom[1], CameraFrom[2]);

p. 270 -- Listing 12.5: A new RiDisplay() call must be made before the second RiWorldBegin() to change to RGBA rendering rather than z rendering. In addition, it is better style to put both the shadow texture generation and the final output image inside the same RiFrameBegin/End block. The sequence of calls:

RiFrameEnd();
RiMakeShadow( "shdw.z", shadowfile, RI_NULL );
RiFrameBegin(2);
    RiWorldBegin;

should be changed to:

    RiMakeShadow( "shdw.z", shadowfile, RI_NULL );
    RiDisplay("ri.pic", RI_FILE, RI_RGBA, RI_NULL);
    RiWorldBegin();

shadowfile is not declared as an interface variable. The following line should appear next to the char declaration at the top of the listing:

RtToken RI_SHADOWFILE;

and the call to RiLightsource() should have "source" capitalized and be preceded by RiDeclare():

RI_SHADOWFILE = RiDeclare("shadowfile", "uniform string");
RiLightSource("shadowspot", RI_SHADOWSPOT,
    (RtPointer)&shadowfile, RI_NULL);

Chapter 13

p. 282 -- Listing 13.1: An alternate "clouds" shader is presented than that in dark image because the sum of the noise rarely exceeds 0.1. Although lighting can be used here, a more effective looking shader ignores the lights and scales the sum somewhat. The color computation used is:

  1. Substitute line 9, the declaration of "refl", with the declaration of white
    color white = color(1.0, 1.0, 1.0);
    
  2. Delete line 17, the assignment of "refl".
  3. Replace line 18, the assignment of "Ci" with:
    Ci = mix(Cs, white, sum*4.0);
    

Chapter 16

p. 338 -- Listing 16.6: To mimic the standard shader, the default position should be in shader space.

point from   = point "camera" (0,0,0),  /* light position */
      to     = point "camera" (0,0,1))

should read

point from   = point "shader" (0,0,0),  /* light position */
      to     = point "shader" (0,0,1))

p. 339 -- Listing 16.7: To mimic the standard shader, the default position should be in shader space.

point from   = point "camera" (0,0,0))  /* light position */

should read

point from   = point "shader" (0,0,0))   /* light position */

p. 340 -- Listing 16.8: To mimic the standard shader, the default position should be in camera space.

point from   = point "camera" (0,0,0),  /* light position */
      to     = point "camera" (0,0,1);  /* light direction */

should read

point from   = point "shader" (0,0,0),  /* light position */
      to     = point "shader" (0,0,1);  /* light direction */

p. 361 -- Listing 16.21: The initialization of Nf should be removed.

Also the line

in2 = faceforward(normalize(in), path);

should be changed to

in2 = normalize(in);
in2 *= sign(-path.in2);

p. 363 -- Listing 16.22: The I and N vectors must be normalized, as shown below:

point II = normalize(I);
point NN = normalize(N);
diffuse = II.NN / (II.II * NN.NN);

p. 380 -- Listing 16.33: Semicolon (;) should be a comma (,) in the declaration of conedeltaangle, i.e.:

conedeltaangle   = radians(5),

Appendix D

p. 411: RiGeometry() is not listed in the Appendix.

RiGeometry(type)
RtToken type;

Nor is it described in the text of the book other than a mention in the table on page 58.

p. 412: RiMatte() has a boolean flag.

RiMatte(onoff)
RtBoolean onoff;

pp. 414,415: The following are not listed

RiScale(sx, sy, sz)					113
RtFloat sx, sy, sz;
RiSkew(angle, dx1, dy1, dz1, dx2, dy2, dz2)		113
RtFloat angle, dx1, dy1, dz1, dx2, dy2, dz2;
RiTransformBegin()					111
RiTransformEnd()					111
 

Pixar Animation Studios
(510) 752-3000 (voice)   (510) 752-3151 (fax)
Copyright © 1996- Pixar. All rights reserved.
RenderMan® is a registered trademark of Pixar.