Geometric Primitives

Polygons
Patches
Subdivision Surfaces
Quadrics
Point and Curve Primitives
Blobby Implicit Surfaces
Procedural Primitives
Implementation-specific Geometric Primitives
Solids and Spatial Set Operations
Retained Geometry


The RenderMan Interface supports only surface- and solid-defining geometric primitives. Solid primitives are created from surfaces and combined using set operations. The geometric primitives include:

Control vertex points are used to construct polygons, patches, NURBS, subdivision meshes, point clouds, and curves. Point positions can be either an (x,y,z) triplet ("P") or an (x,y,z,w) 4-vector ("Pw"). If the vertex is part of a patch mesh, the position may be used to define a height field. In this case the vertex point contains only a (z) coordinate ("Pz"), and the (x,y)s of points of the height field are set equal to the parametric surface parameters of the mesh.

All primitives have well-defined geometric surface normals, so normals need not be provided with any primitive. The surface normal for a polygon is the perpendicular to the plane containing the polygon. The surface normal for a parametric curved surface is computed by taking the cross product of the surface's parametric derivatives: (dPdu) x (dPdv). As mentioned in the Section 4.2.13, Orientation and Sides, if the current orientation does not match the orientation of the current coordinate system, normals will be flipped. It is also possible to provide additional shading normals ("N") at polygon and bilinear patch vertices to help make the surface appear smooth.

Quadrics, patches and patch meshes, and NURBS primitives have well-defined global two-dimensional surface parameters. All the points on the surface of each primitive are functions of these parameters (u,v). Except for NURBS and polygons, the domain of the surface parameters is the unit square from 0 to 1. Texture coordinates may be attached to primitives by assigning four sets of texture coordinates, one set to each corner of this unit square.  This is done by setting the current set of texture coordinates or by defining texture coordinates with the geometric primitives as described below.

Subdivision surfaces and implicit surfaces have locally defined parameterizations, but no globally consistent parameterization across an arbitrary surface of that type. All geometric primitives normally inherit their color and opacity from the graphics state. However, explicit colors and opacities can be provided when defining the primitive ("Cs" and Os").

Associated with each geometric primitive definition are additional primitive variables that are passed to their shaders. These variables may define quantities that are constant over the surface (class constant), piecewise-constant but with separate values per subprimitive (class uniform), bilinearly interpolated (class varying and facevarying), or fully interpolated (class vertex). If the primitive variable is uniform, there is one value per surface facet. If the primitive variable is varying, there are four values per surface facet, one for each corner of the unit square in parameter space (except polygons, which are a special case). On parametric primitives (quadrics and patches), varying primitive variables are bilinearly interpolated across the surface of the primitive. Colors, opacities, and shading normals are all examples of varying primitive variables.  If a primitive variable is facevarying it will be linearly interpolated.  For the RiPointsPolygons, RiPointsGeneralPolygons, RiSubdivisionMesh, and RiHierarchicalSubdivisionMesh primitives it is expected that the the number of values supplied for a facevarying variable is equal to the sum of all entries in the vertices array. Compare this to varying, where the number of values expected is equal to nverts, the number of distinct vertices in the mesh. For primitives other than RiPointsPolygons, RiPointsGeneralPolygons, RiSubdivisionMesh and RiHierarchicalSubdivisionMesh, facevarying is equivalent to varying.

The standard predefined primitive variables are defined in Table 5.1, Standard Geometric Primitive. Other primitive variables may be predefined by specific implementations or defined by the user with the RiDeclare function, or may be declared in-line" as part of the parameter name itself (see Section 3). Primitive variables which are declared to be of type point (including the three predefined position variables), vector, normal, or matrix are specified in object space, and will be transformed by the current transformation matrix. Any vector or normal variables will be transformed by the equivalent transformation matrix for vectors or normals. Primitive variables which are declared to be of type color must contain the correct number of floating point values as defined in RiColorSamples. More information about how to use primitive variables is contained in Part II: The RenderMan Shading Language.

Table 5.1 Standard Geometric Primitive Variables

Information Name Type Class Floats
Position "P" 
"Pz" 
"Pw"
point 
point 
hpoint
vertex 
vertex 
vertex


4
Normal "N" 
"Np"
normal 
normal
varying 
uniform
3 3
Color "Cs" color varying (3)
Opacity "Os" color varying (3)
Texture Coordinates "s" 
"t" 
"st"
float 
float 
2 float
varying 
varying 
varying


2

5.1 Polygons

The RenderMan Interface supports two basic types of polygons: a convex polygon and a general concave polygon with holes. In both cases the polygon must be planar. Collections of polygons can be passed by giving a list of points and an array that indexes these points.

The geometric normal of the polygon is computed by computing the normal of the plane containing the polygon (unless it is explicitly specified). If the current orientation is left-handed, then a polygon whose vertices were specified in clockwise order (from the point of view of the camera) will be a front-facing polygon (that is, will have a normal vector which points toward the camera). If the current orientation is right-handed, then polygons whose vertices were specified in counterclockwise order will be front-facing. The shading normal is set to the geometric normal unless it is explicitly specified at the vertices.

The surface parameters of a polygon are its (x,y) coordinates. This is because the height z of a plane is naturally parameterized by its (x,y) coordinates, unless it is vertical. Texture coordinates are set equal to the surface parameters unless texture coordinates are given explicitly, one set per vertex. Polygons do not inherit texture coordinates from the graphics state.

The rules for primitive variable interpolation and texture coordinates are different for polygons than for all other geometric primitives. Constant primitive variables are supplied as a single value for the entire aggregate primitive. Uniform primitive variables are supplied for each polygon. Both varying and vertex primitive variables are supplied for each polygon vertex, and are interpolated across the interior without regard to the artificial surface parameters defined above. Note that interpolating values across polygons is inherently ill-defined. However, linearly interpolating values across a triangle is always well defined.  Thus, for the purposes of interpolation, polygons are always decomposed into triangles.  However, the details of how this decomposition is done is implementation-dependent and may depend on the view.


RiPolygon( RtInt nvertices, ...parameterlist... )

nvertices is the number of vertices in a single closed planar convex polygon. parameterlist is a list of token-array pairs where each token is one of the standard geometric primitive variables or a variable which has been defined with RiDeclare. The parameter list must include at least position ("P") information. If a primitive variable is varying, the array contains nvertices elements of the type corresponding to the token. If the variable is uniform, the array contains a single element. The number of floats associated with each type is given in Table 5.1, Standard Geometric Primitive Variables.

No checking is done by the RenderMan Interface to ensure that polygons are planar, convex and nondegenerate. The rendering program will attempt to render invalid polygons but the results are unpredictable.

RIB BINDING
	Polygon ...parameterlist...

The number of vertices in the polygon is determined implicitly by the number of elements in the required position array.

EXAMPLE
	 RtPoint points[4] = ( 0.0, 1.0, 0.0,  0.0, 1.0, 1.0,
		0.0, 0.0, 1.0,   0.0, 0.0, 0.0);
	 RiPolygon(4, RI_P, (RtPointer)points, RI_NULL);

 SEE ALSO

     RiGeneralPolygon, RiPointsGeneralPolygons, RiPointsPolygons

An example of the definition of a “Gouraud-shaded" polygon is:

	RtPoint	points[4];
	RtColor	colors[4];
	RiPolygon( 4, "P", (RtPointer)points, "Cs", (RtPointer)colors, RI_NULL );

A “Phong-shaded" polygon is given by:

	RtPoint	points[4];
	RtPoint	normals[4];
	RiPolygon( 4, "P", (RtPointer)points, "N", (RtPointer)normals, RI_NULL );

A “Phong-shaded" polygon with a precomputed plane normal is:

	RtPoint	points[4];
	RtPoint	normals[4];
	RiPolygon( 4, "P", (RtPointer)points, "N", (RtPointer)normals, RI_NULL );

RiGeneralPolygon( RtInt nloops, RtInt nvertices, ...parameterlist... )

Define a general planar concave polygon with holes. This polygon is specified by giving nloops lists of vertices. The first loop is the outer boundary of the polygon; all additional loops are holes. The array nvertices contains the number of vertices in each loop, and has length nloops. The vertices in all the loops are concatenated into a single vertex array. The length of this array, n, is equal to the sum of all the values in the array nvertices.

parameterlist is a list of token-array pairs where each token is one of the standard geometric primitive variables or a variable that has been defined with RiDeclare. The parameter list must include at least position ("P") information. If a primitive variable is varying, the array contains n elements of the type corresponding to the token. If the variable is uniform, there is a single element of that type. The number of floats associated with each type is given in Table 5.1, Standard Geometric Primitive Variables. The interpretation of these variables is the same as for a convex polygon.

No checking is done by the RenderMan Interface to ensure that polygons are planar and nondegenerate. The rendering program will attempt to render invalid polygons but the results are unpredictable.

RIB BINDING
	GeneralPolygon nvertices parameterlist

The number of loops in the general polygon is determined implicitly by the length of the nvertices array.

EXAMPLE
	GeneralPolygon [4 3] "P" [
		0 0 0  0 1 0  0 1 1  0 0 1
		0 0.25 0.5  0 0.75 0.75  0 0.75 0.25 ]

SEE ALSO
	RiPolygon, RiPointsPolygons, RiPointsGeneralPolygons

RiPointsPolygons( RtInt npolys, RtInt nvertices[], RtInt vertices[], ...parameterlist...) 

Define npolys planar convex polygons that share vertices. The array nvertices contains the number of vertices in each polygon and has length npolys. The array vertices contains, for each polygon vertex, an index into the varying primitive variable arrays. The varying arrays are 0-based. vertices has length equal to the sum of all of the values in the nvertices array. Individual vertices in the parameterlist are thus accessed indirectly through the indices in the array vertices.

parameterlist is a list of token-array pairs where each token is one of the standard ge-ometric primitive variables or a variable that has been defined with RiDeclare. The parameter list must include at least position ("P") information. If a primitive variable is of class vertex or varying, the array contains n elements of the type corresponding to the token, where the number n is equal to the maximum value in the array vertices plus one. If the variable is uniform, the array contains npolys elements of the asso-ciated type. If the variable is constant, the array contains exactly one element of the associated type. The number of floats associated with each type is given in Table 5.1, Standard Geometric Primitive Variables. The interpretation of these variables is the same as for a convex polygon.

No checking is done by the RenderMan Interface to ensure that polygons are planar, convex and nondegenerate. The rendering program will attempt to render invalid polygons but the results are unpredictable.

RIB BINDING
	 PointsPolygons nvertices vertices parameterlist

The number of polygons is determined implicitly by the length of the nvertices array.

EXAMPLE
	PointsPolygons [3 3 3] [0 3 2  0 1 3  1 4 3]
		"P"   [0 1 1  0 3 1  0 0 0  0 2 0  0 4 0]
		"Cs" [0 .3 .4  0 .3 .9  .2 .2 .2  .5 .2 0  .9 .8 0]

SEE ALSO
	RiGeneralPolygon, RiPointsGeneralPolygons, RiPolygon

RiPointsGeneralPolygons( RtInt npolys, RtInt nloops, RtInt nvertices[], 
				RtInt vertices[], ...parameterlist... )

Define npolys general planar concave polygons, with holes, that share vertices. The array nloops indicates the number of loops comprising each polygon and has a length npolys. The array nvertices contains the number of vertices in each loop and has a length equal to the sum of all the values in the array nloops. The array vertices contains, for each loop vertex, an index into the varying primitive variable arrays. All of the arrays are 0-based. vertices has a length equal to the sum of all the values in the array nvertices. Individual vertices in the parameterlist are thus accessed indirectly through the indices in the array vertices.

parameterlist is a list of token-array pairs where each token is one of the standard geometric primitive variables or a variable that has been defined with RiDeclare. The parameter list must include at least position ("P") information. If a primitive variable is varying, the array contains n elements of the type corresponding to the token. The number n is equal to the maximum value in the array vertices plus one. If the variable is uniform, the array contains npolys elements of the associated type. The number of floats associated with each type is given in Table 5.1, Standard Geometric Primitive Variables. The interpretation of these variables is the same as for a convex polygon.

No checking is done by the RenderMan Interface to ensure that polygons are planar and nondegenerate. The rendering program will attempt to render invalid polygons but the results are unpredictable.

RIB BINDING
	PointsGeneralPolygons nloops nvertices vertices ...parameterlist...

The number of polygons is determined implicitly by the length of the nloops array.

EXAMPLE
	PointsGeneralPolygons [2 2] [4 3 4 3] [0 1 4 3 6 7 8 1 2 5 4 9 10 11]
		"P" [0 0 1  0 1 1  0 2 1  0 0 0  0 1 0  0 2 0
		0 0.25 0.5  0 .75 .75  0 .75 .25
		0 1.25 0.5  0 1.75 .75  0 1.75 .25]

SEE ALSO
	RiGeneralPolygon, RiPointsPolygons, RiPolygon

5.2 Patches

Patches can be either uniform or non-uniform (contain different knot values). Patches can also be non-rational or rational depending on whether the control points are (x,y,z) or (x,y,z,w). Patches may also be bilinear or bicubic. The graphics state maintains two 4x4 matrices that define the bicubic patch basis matrices. One of these is the current u-basis and the other is the current v-basis. Basis matrices are used to transform from the power basis to the preferred basis.


RiBasis( RtBasis ubasis, RtInt ustep, RtBasis vbasis, RtInt vstep )

Set the current u-basis to ubasis and the current v-basis to vbasis. Predefined basis matrices exist for the common types:

	RtBasis	RiBezierBasis;
	RtBasis	RiBSplineBasis;
	RtBasis	RiCatmullRomBasis;
	RtBasis	RiHermiteBasis;
	RtBasis	RiPowerBasis;

The variables ustep and vstep specify the number of control points that should be skipped in the u and v directions, respectively, to get to the next patch in a bicubic patch mesh. The appropriate step values for the predefined cubic basis matrices are:

BasisStep
RiBezierBasis 3
RiBSplineBasis 1
RiCatmullRomBasis 1
RiHermiteBasis 2
RiPowerBasis 4

The default basis matrix is RiBezierBasis in both directions.

RIB BINDING

	Basis uname ustep vname vstep
	Basis uname ustep vbasis vstep
	Basis ubasis ustep vname vstep
	Basis ubasis ustep vbasis vstep

For each basis, either the name of a predefined basis (as a string) or a matrix may be supplied. If a basis name specified, it must be one of: "bezier", "b-spline", "catmull-rom", "hermite", or "power."

EXAMPLE
	Basis "b-spline" 1 [-1 3 -3 1 3 -6 3 0 -3 3 0 0 1 0 0 0] 1

SEE ALSO
	RiPatch, RiPatchMesh

Note that the geometry vector used with the RiHermiteBasis basis matrix must be (point0, vector0, point1, vector1), which is a permutation of the Hermite geometry vector often found in mathematics texts. Using this formulation permits a step value of 2 to correctly increment over data in Hermite patch meshes.


RiPatch( RtToken type, ...parameterlist... )

Define a single patch. type can be either "bilinear" or "bicubic". parameterlist is a list of token-array pairs where each token is one of the standard geometric primitive variables or a variable which has been defined with RiDeclare. The parameter list must include at least position ("P", "Pw" or "Pz") information. Patch arrays are specified such that u varies faster than v.

Four points define a bilinear patch, and 16 define a bicubic patch. The order of vertices for a bilinear patch is (0,0),(1,0),(0,1),(1,1). Note that the order of points defining a quadrilateral is different depending on whether it is a bilinear patch or a polygon. The vertices of a polygon would normally be in clockwise (0,0),(0,1),(1,1),(1,0) order.

Patch primitive variables that are constant or uniform should supply one value, which is constant over the patch. Primitive variables that are varying should supply four values, one for each parametric corner of the patch (the data will be interpolated bilinearly). Primitive variables that are vertex should supply four values for a bilinear patch, or 16 values for a bicubic patch —that is, the same number of values as control vertices "P". A vertex primitive variable will be interpolated across the surface in the same manner as the surface position "P". In all cases, the actual size of each array is this number of values times the size of the type associated with the variable.

RIB BINDING
	Patch type parameterlist

EXAMPLE
	Patch "bilinear" "P" [ -0.08 0.04 0.05  0 0.04 0.05
		-0.08 0.03 0.05  0 0.03 0.05]

SEE ALSO
	RiBasis, RiNuPatch, RiPatchMesh

Figure 5.1 Bicubic patch vertex ordering

(click on image to view a larger version)


RiPatchMesh( RtToken type, RtInt nu, RtToken uwrap, RtInt nv, RtInt vwrap, 
		...parameterlist...  )

This primitive is a compact way of specifying a quadrilateral mesh of patches. Each individual patch behaves as if it had been specified with RiPatch. type can be either "bilinear" or "bicubic." parameterlist is a list of token-array pairs where each token is one of the geometric primitive variables or a variable which has been defined with RiDeclare. The parameter list must include at least position ("P", "Pw" or "Pz") information. Patch mesh vertex data is supplied in first u and then v order just as for patches. The number of control points in a patch mesh is (nu)*(nv).

Meshes can wrap around in the u or v direction, or in both directions. If meshes wrap, they close upon themselves at the ends and the first control points will be automatically repeated. As many as three control points may be repeated, depending on the basis matrix of the mesh. The way in which meshes wrap is indicated by giving a wrap mode value of either "periodic" or "nonperiodic."

The actual number of patches produced by this request depends on the type of the patch and the wrap modes specified. For bilinear patches, the number of patches in the u direction, nupatches, is given by

while for bicubic patches,

The same rules hold in the v direction. The total number of patches produced is equal to the product of the number of patches in each direction.

A PatchMesh primitive variable of class vertex has the same number of entries as the position "P" (i.e., nu x nv) and is interpolated using the same order and basis matrices.  Any varying primitive variables are interpolated piecewise-bilinearly across the patch mesh and contain n values, one for each patch corner, where n is defined by:

(with nupatches and nvpatches defined as given above). If a variable is uniform, it contains nupatches x nvpatches elements of its type, one for each patch (see Figure 5.2). Primitive variables of class constant have exactly one data element of the appropriate type.

A patch mesh is parameterized by a (u,v) which goes from 0 to 1 for the entire mesh. Texture maps that are assigned to meshes that wrap should also wrap so that filtering at the seams can be done correctly (see the section on Texture Map Utilities). If texture coordinates are inherited from the graphics state, they correspond to the corners of the mesh.

Height fields can be specified by giving just a z coordinate at each vertex (using "Pz"); the x and y coordinates are set equal to the parametric surface parameters. Height fields cannot be periodic.

RIB BINDING
	PatchMesh type nu uwrap nv vwrap ...parameterlist...

EXAMPLE
	RtPoint pts[28]
	RtFloat foos[2];
	RtFloat bars[6];
	RiBasis(RiBezierBasis, 3, RiBezierBasis, 3);
	RiDeclare("foo", "uniform float");
	RiDeclare("bar", "varying float");
	RiPatchMesh("bicubic", 7, "nonperiodic", 4, "nonperiodic",
		"P", (RtPointer)pts, "foo", (RtPointer)foos,
		"bar", (RtPointer)bars, RI_NULL);

SEE ALSO
	RiBasis, RiNuPatch, RiPatch

Figure 5.2 Patch meshes

(click on image to view a larger version)


Non-uniform B-spline patches are also supported by the RenderMan Interface. Rational quadratic B-splines provide exact representations of many different surfaces including general quadrics, torii, surfaces of revolution, tabulated cylinders, and ruled surfaces.  NURBS may contain trim regions and holes that are specified by giving curves in parameter space.

RiNuPatch( RtInt nu, RtInt uorder, RtFloat uknot[], RtFloat umin, RtFloat umax, 
		RtInt nv, RtInt vorder, RtFloat vknot[], RtFloat vmin, 
		RtFloat vmax, ...parameterlist... )

This procedure creates a tensor product rational or polynomial non-uniform B-spline surface patch mesh. parameterlist is a list of token-array pairs where each token is one of the standard geometric primitive variables or a variable that has been defined with RiDeclare. The parameter list must include at least position ("P" or "Pw") information.

The surface specified is rational if the positions of the vertices are 4-vectors (x,y,z,w), and polynomial if the positions are 3-vectors (x,y,z). The number of control points in the u direction equals nu and the number in the v direction equals nv. The total num-ber of vertices is thus equal to (nu)*(nv). The ordermust be positive and is equal to the degree of the polynomial basis plus 1. There may be different orders in each parametric direction. The number of control points should be at least as large as the order of the polynomial basis. If not, a spline of order equal to the number of control points is computed. The knot vectors associated with each control point (uknot[], vknot[]) must also be specified. Each value in these arrays must be greater than or equal to the previous value. The number of knots is equal to the number of control points plus the order of the spline. The surface is defined in the range umin to umax and vmin to vmax. This is different from other geometric primitives where the parameter values are always assumed to lie between 0 and 1. Each min must be less than its max. min must also be greater than or equal to the corresponding (order-1)th knot value. max must be less than or equal to the nth knot value.

A NuPatch may be thought of as a nonperiodic uniform B-spline mesh with (1+nu - uorder) segments in the u parametric direction, and (1+nv - vorder) segments in the v parametric direction. RiNuPatch primitive variables are therefore defined to have one uniform value per segment and one varying value per segment corner. The number of uniform primitive variables is therefore nusegments x nvsegments, and the number of varying variables is (nusegments+1) x (nvsegments+1). Note that this results in redundant parameter values corresponding to repeated knot values, for instance when the knot vector indicates the RiNuPatch is in Bezier form. Primitive variables of class vertex contain nu x nv values of the appropriate type, and are interpolated using the same methods as the surface position "P". Primitive variables that are of class constant will have a single value for the entire mesh.  If texture coordinates primitive variables are not present, the current texture coordinates are assigned to corners defined by the rectangle (umin,umax) and (vmin,vmax) in parameter space.

RIB BINDING
	NuPatch nu uorder uknot umin umax nv vorder vknot vmin vmax ...parameterlist...

EXAMPLE
	NuPatch 9 3 [ 0 0 0 1 1 2 2 3 3 4 4 4 ] 0 4
	2 2 [ 0 0 1 1 ] 0 1
	"Pw" [   1  0  0 1  1  1  0 1  0  2  0 2
		-1  1  0 1 -1  0  0 1 -1 -1  0 1
		 0 -2  0 2  1 -1  0 1  1  0  0 1
		 1  0 -3 1  1  1 -3 1  0  2 -6 2
		-1  1 -3 1 -1  0 -3 1 -1 -1 -3 1
		 0 -2 -6 2  1 -1 -3 1  1  0 -3 1 ]
SEE ALSO

	 RiPatch, RiPatchMesh, RiTrimCurve

RiTrimCurve( RtInt nloops, RtInt ncurves[], RtInt order[], RtFloat knot[], 
		RtFloat min[], RtFloat max[], RtInt n[], RtFloat u[], RtFloat v[], 
		RtFloat w[] )

Set the current trim curve. The trim curve contains nloops loops, and each of these loops contains ncurves curves. The total number of curves is equal to the sum of all the values in ncurves. Each of the trimming curves is a non-uniform rational B-spline curve in homogeneous parameter space (u,v,w). The curves of a loop connect in head-to-tail fashion and must be explicitly closed. The arrays order, knot, min, max, n, u, v, w contain the parameters describing each trim curve. All the trim curve parameters are concatenated together into single large arrays. The meanings of these parameters are the same as the corresponding meanings for a non-uniform B-spline surface.  Trim curves exclude certain areas from the non-uniform B-spline surface definition.  The inside must be specified consistently using two rules: an odd winding rule that states that the inside consists of all regions for which an infinite ray from any point in the region will intersect the trim curve an odd number of times, and a curve orientation rule that states that the inside consists of the regions to the "left" as the curve is traced.

Trim curves are typically used to specify boundary representations of solid models.  Since trim curves are approximations and not exact, some artifacts may occur at the boundaries between intersecting output primitives. A more accurate method is to specify solids using spatial set operators or constructive solid geometry (CSG). This is described in the section on Solids and Spatial Set Operations, p. 93.  The list of Trim Curves is part of the attribute state, and may be saved and restored using RiAttributeBegin and RiAttributeEnd.

RIB BINDING
	TrimCurve ncurves order knot min max n u v w

The number of loops is determined implicitly by the length of the ncurves array.

EXAMPLE
	RtInt nloops = 1;
	RtInt ncurves[1] = { 1 };
	RtInt order[1] = { 3 };
	RtFloat knot[12] = { 0,0,0,1,1,2,2,3,3,4,4,4 };
	RtFloat min[1] = { 0 };
	RtFloat max[1] = { 4 };
	RtInt n[1] = { 9 };
	RtFloat u[9] = {  1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0 };
	RtFloat v[9] = {  0.5, 1.0, 2.0, 1.0, 0.5, 0.0, 0.0, 0.0, 0.5 };
	RtFloat w[9] = {  1.0, 1.0, 2.0, 1.0, 1.0, 1.0, 2.0, 1.0, 1.0 };
	RiTrimCurve(nloops, ncurves, order, knot, min, max, n, u, v, w);

SEE ALSO
	RiNuPatch, RiSolidBegin

5.3 Subdivision Surfaces

The RenderMan Interface includes support for subdivision surfaces. Ordinary cubic B-spline surfaces are rectangular grids of tensor-product patches. Subdivision surfaces generalize these to control grids with arbitrary connectivity. The API for subdivision surfaces looks a lot like RiPointsPolygons, with additional parameters to permit the specification of scheme-specific and implementation-specific enhancements.  

A subdivision surface, like a parametric surface, is described by its control mesh of points.  The surface itself can approximate or interpolate this control mesh while being piecewise smooth. Furthermore, its control mesh is not confined to be rectangular, which is a major limitation of NURBs and uniform B-splines. In this respect, the control mesh is analogous to a polygonal description. But where polygonal surfaces require large numbers of data points to approximate being smooth, a subdivision surface is smooth —meaning that polygonal artifacts are never present, no matter how the surface animates or how closely it is viewed.


RiSubdivisionMesh ( RtToken scheme, RtInt nfaces, RtInt nvertices[], RtInt vertices[],
			RtInt ntags, RtToken tags[], RtInt nargs[],
			RtInt intargs[], RtFloat floatargs[], ..parameterlist...)
RiSubdivisionMesh defines a subdivision mesh or surface obeying the subdivision scheme specified by scheme. The only standard scheme is "catmull-clark", specifying the Catmull-Clark subdivision method. Implementations may also support other schemes. The subdivision mesh is made up of nfaces faces. The array nvertices, of length nfaces, contains the number of vertices in each face. The array vertices contains, for each face vertex, an index into the vertex primitive variable arrays. The array vertices has a length equal to the sum of all the values in the array nvertices. All the arrays are 0-based.

parameterlist is a list of token-array pairs where each token is one of the standard geometric primitive variables, a variable that has been defined with RiDeclare, or is given as an inline declaration. The parameter list must include at least position ("P") information. If a primitive variable is vertex, varying or facevarying, the array contains n elements of the type corresponding to the token, where n is equal to the maximum value in the array vertices plus one. Primitive variables that are vertex will be interpolated according to the subdivision rules (just as "P" is), whereas varying and facevarying data will be interpolated linearly across faces (as is done for a RiPointsPolygons). If the variable is uniform, the array contains nfaces elements of the associated type. If the variable is constant, a single element of the associated type should be provided.  A component is either a face, a vertex, or a chain of edges. Components of the subdivision mesh may be tagged by the user to have various implementation-specific properties. The token array tags, of length ntags, identifies these tags. Each tag has zero or more integer arguments, and zero or more floating-point arguments. The number of arguments provided with each tag is specified by the array nargs, which has a length of ntags x 2. For each tag, nargs contains an integer specifying the number of integer operands found in the array intargs, followed by an integer specifying the number of floating-point operands found in the array floatargs. Thus, the length of intargs is equal to the sum of all the even-numbered elements of the array nargs.  The length of floatargs is equal to the sum of all the odd-numbered elements of the array nargs.

The standard tags are "hole", "crease", "corner", and "interpolateboundary".  The "hole" tag specifies that certain faces are holes. This tag has n integer arguments, one for each face that is a hole, and zero floating-point arguments. Each face is specified by its index in the nvertices array.

The "crease" tag specifies that a certain chain of edges should be a sharp crease. This tag has n integer arguments specifying a chain of vertices that make up the crease, and one floating-point argument that is expected to be RI_INFINITY. Each sequential pair of vertices in a crease must be the endpoints of an edge of the subdivision mesh. A mesh may have any number of independent "crease" tags. Individual renderer implementations may choose to expand the functionality of the "crease" tag by making use of tag values other than RI_INFINITY.

The "corner" tag may be used to mark certain vertices as sharp corners. This tag has n integer arguments containing the vertex numbers of the corners and either one or n floating-point arguments that are expected to be  RI_INFINITY. Individual renderer implementations may choose to expand the functionality of the "crease" tag by making use of tag values other than RI_INFINITY.

The "interpolateboundary" tag controls how interpolation boundary face edges are interpolated. This tag has one optional integer argument and zero floating-point arguments. If the integer argument is not specified it is assumed to have a value of 1. A value of 0 specifies that no boundary interpolation behavior should occur (the default behavior when this tag is not specified). A value of 1 indicates that all the boundary edge-chains are sharp creases and that boundary vertices with exactly two incident edges are sharp corners. A value of 2 indicates that all the boundary edge-chains are sharp creases; boundary vertices are not affected.

RIB BINDING
SubdivisionMesh scheme nvertices vertices tags nargs intargs floatargs 
		...parameterlist...

The number of faces is determined implicitly by the length of the nvertices array. The number of tags is determined implicitly by the length of the tags array, and must match the value as determined from the nargs array.

EXAMPLE
        SubdivisionMesh "catmull-clark" [4 4 4 4 4 4 4 4 4] [ 0 4 5 1 1 5 6 2 2 6 7
		3 4 8 9 5 5 9 10 6 6 10 11 7 8 12 13 9 9 13 14 10 10 14 15 11 ]
		["interpolateboundary"] [0 0] [] [] "P" [-60 60 0 -60 20 0 -60
		-20 0 -60 -60 0 -20 60 0 -20 20 45 -20 -20 45 -20 -60 0 20 60 0
		20 20 45 20 -20 45 20 -60 0 60 60 0 60 20 0 60 -20 0 60 -60 0]
SEE ALSO
      RiPointsPolygons
      RiHierarchicalSubdivisionMesh

RiHierarchicalSubdivisionMesh ( RtToken scheme, RtInt nfaces, RtInt nvertices[], RtInt vertices[],
			RtInt ntags, RtToken tags[], RtInt nargs[],
			RtInt intargs[], RtFloat floatargs[], RtString stringargs[], ..parameterlist...)

RiHierarchicalSubdivisionMesh is an extension of RiSubdivisionMesh. It is similar to that call in that it defines a subdivision mesh or surface obeying the subdivision scheme specified by scheme (with the only standard scheme being "catmull-clark", specifying the Catmull-Clark subdivision method). The subdivision mesh is made up of nfaces faces. The array nvertices, of length nfaces, contains the number of vertices in each face. The array vertices contains, for each face vertex, an index into the vertex primitive variable arrays. The array vertices has a length equal to the sum of all the values in the array nvertices. All the arrays are 0-based. parameterlist is a list of token-array pairs where each token is one of the standard geometric primitive variables, a variable that has been defined with RiDeclare, or is given as an inline declaration. The parameter list must include at least position ("P") information. If a primitive variable is vertex, varying or facevarying, the array contains n elements of the type corresponding to the token, where n is equal to the maximum value in the array vertices plus one. Primitive variables that are vertex will be interpolated according to the subdivision rules (just as "P" is), whereas varying and facevarying data will be interpolated linearly across faces (as is done for a RiPointsPolygons). If the variable is uniform, the array contains nfaces elements of the associated type. If the variable is constant, a single element of the associated type should be provided.

Like the RiSubdivisionMesh call, components of the subdivision mesh may be tagged by the user to have specific properties. Unlike the RiSubdivisionMesh call, each tag can have zero or more string arguments, as well as zero or more integer arguments and zero or more floating-point arguments. Hence, the number of arguments provided with each tag is specified by the array nargs, which has a length of ntags x 3 (unlike RiSubdivisionMesh, where the length is ntags x 2). For each tag, nargs contains an integer specifying the number of integer operands found in the array intargs, followed by an integer specifying the number of floating-point operands found in the array floatargs, followed by an integer specifying the number of string operands found in the array stringargs. Thus, given m in [0, ntags - 1], the length of intargs is equal to the sum of all the 3m numbered elements of the array nargs; the length of floatargs is equal to the sum of all the 3m+1 numbered elements of the array nargs; and the length of stringargs is equal to the sum of all 3m+2 numbered elements of the array nargs.

Hierarchical subdivision surfaces are required to support the same tags as standard subdivision surfaces, which are all specified in the same fashion save that the number of string arguments are set to zero. In addition, they are required to support hierarchical edit tags, all of which accept and require string arguments. A hierarchical edit is an edit made to any one of the faces, edges, or vertices that arise anywhere during subdivision. Normally these subdivision components inherit values from their parents based on a set of subdivision rules that depend on the subdivision scheme. A hierarchical edit overrides these values. This allows for a compact specification of localized detail on a subdivision surface, without having to express information about the rest of the subdivision surface at the same level of detail. In order to perform a hierarchical edit, we need to be able to name the subdivision component we are interested in, no matter where it may occur in the subdivision hierarchy. This leads us to a hierarchical path specification for faces, since once we have a face we can navigate to an incident edge or vertex by association. In a subdivision mesh, a face always has incident vertices, which are labelled (in relation to the face) with an integer index starting at zero and in consecutive order according to the usual winding rules for subdivision surfaces. Faces also have incident edges, and these are labelled according to the origin vertex of the edge.

A subdivided face is specified by a series of integers that indicate a path of traversal, starting at a base face and proceeding down the subdivision hierarchy. The first integer is the index of a base face in the mesh - it is the same number used to index into the nvertices array passed to the HierarchicalSubdivisionMesh call. Subsequent integers in the path are a zero-based index to one of the children of the previous face in the path. Note that in the Catmull-Clark subdivision scheme, the first subdivision splits all non-quad faces into quads, resulting in a mesh consisting solely of quadrilateral faces. This means that the second integer (the child of the base face) can be an integer greater than 3 (depending on the number of vertices in the base face), but every integer after that must be less than 4.

To properly specify a face, we need to know how child faces are indexed with respect to their parent. In turn, this ordering depends on how the vertices of the parent face are indexed. The indexing of subfaces and vertices ultimately depends on whether or not the first face in the path is extraordinary (has a number of vertices not equal to four in the Catmull-Clark scheme). In particular, the immediate child subfaces of an extraordinary face have different vertex ordering than the immediate child subfaces of a regular face. The following diagram illustrates the ordering. Note that subface vertices are overlaid on top of the parent vertices for simplicity, but obviously during subdivision they may not have the same position.

In this diagram, the indices of the vertices of the base face are marked in red; so on the left we have an extraordinary Catmull-Clark face with five vertices (labeled 0-4) and on the right we have a regular Catmull-Clark face with four vertices (labelled 0-3). The indices of the child faces are blue; note that in both the extraordinary and regular cases, the child faces are indexed the same way, i.e. the subface labeled n has one incident vertex that is the result of the subdivision of the parent vertex also labeled n in the parent face. Specifically, we note that the subface 1 in both the regular and extraordinary face is nearest to the vertex labelled 1 in the parent.

The indices of the vertices of the child faces are labeled green, and this is where the difference lies between the extraordinary and regular case; in the extraordinary case, vertex to vertex subdivision always results in a vertex labeled 0, while in the regular case, vertex to vertex subdivision assigns the same index to the child vertex. Again, specifically, we note that the parent vertex indexed 1 in the extraordinary case has a child vertex 0, while in the regular case the parent vertex indexed 1 actually has a child vertex that is indexed 1. Note that this indexing scheme was chosen to maintain the property that the vertex labeled 0 always has the lowest u/v parametric value on the face.

By appending a vertex index to a face specification, we can create a vertex path specification. Similary, we can create a edge path specification by assuming a winding order, and using a vertex path specification to specify the origin vertex of the edge component. For example, a vertex patch specification of (656 4 1 2 3) specifies the 3rd vertex of the 2nd child face of the 1st child face of the 4th child face of the 656th face of the subdivision mesh. (Since the second integer is four, the 656th face of the mesh must have at least five vertices for this to be a valid path.)

The hierarchical edit tags that are required to be supported are "vertexedit", "edgeedit", and "faceedit".

A "vertexedit" tag has the following structure:

  ["vertexedit"] 
     [<Σ(vertex path lengths)> <(amount of float data per vertex) × nvertices> <3 × noperators>] 
     [<vertex paths>] 
     [<edit values>] 
     [<"set"|"add"> <variable name> <"value"|"sharpness"> ... ]

This tag specifies a vertex edit, which overrides the value or the sharpness of a set of primitive variables on the subdivision mesh, at a set of vertices anywhere in the subdivision hierarchy. It is easiest to interpret this tag as a set of operators specified in the string list, each applied to a set of vertices specified in the integer list, with the arguments to the operators specified in the float list.

The operators accepted by the vertexedit tag are described by triplets of strings. The size and interpretation of the float arguments are determined by these operators, as described below.

  1. The first string can be one of the values "set" or "add". "set" indicates the primitive variable value or sharpness is to be set directly to the values specified in the floating point list. "add" adds a value to the normal result computed via standard subdivision rules. In other words, this operation allows value offsets to be applied to the mesh at any level of the hierarchy.
  2. The second string is the name of a variable to edit. This variable must exist in the parameter list of the subdivision surface, and must be of storage class varying, vertex, or facevarying (since it is nonsensical to apply a vertex edit to a variable of uniform or constant storage).
  3. The third string can be either "value" or "sharpness". "value" causes the value of the primitive variable to be overriden, while "sharpness" overrides the subdivision mask of the primitive variable at that vertex. If "value" is specified, the type of the variable as declared in the parameter list determines the number of floating point arguments that will be required for each vertex. For variables that are of type point, vector, normal, or matrix, it is assumed that the float data will be presented in object space. An appropriate transformation will be applied during the render. Note that in the special case of a "add" operation applied to data of type point the three floats will be subject to a vector transformation, not a simple point transformation. When "sharpness" is specified, only one floating point argument will be required.

The integer arguments will be interpreted as a series of vertex path specifications, as defined above. Each vertex path must be prefaced with a path length, with the end result being a "packed" encoding of vertices. The number of vertices being edited is implicit and computed by unpacking these integers into individual paths.

The floating point arguments supplied are values to the operators, one set per vertex, specified in a vertex interleaved fashion, with the contents of each set determined by the string operators as described above. For example, if the vertexedit tag specifies the three operators "set" "P" "sharpness", "add" "P" "value", and "set" "s" "value", for each vertex we expect a set of 5 float values in order: 1 for the sharpness edit on "P", 3 for the value edit on "P", and 1 for the value edit on "s". These five float values are expected to be packed together and specified for each vertex indicated in the vertex paths.

Unlike all other tags, vertexedit tags can be deformation motion blurred with different values. In other words, multiple HierarchicalSubdivisionMesh calls within the same MotionBegin/MotionEnd blocks can have changing vertexedit tags which will be motion blurred.

An "edgeedit" tag has the following structure:

  ["edgeedit"] 
     [<Σ(edge path lengths)> <nedges> <3 × nedges>] 
     [<edge paths>] 
     [<sharpness values>] 
     [<"set"|"add"> <variable name> "sharpness" ... ]

This tag specifies an edge edit, which modifies a property of any edge in the subdivision hierarchy. Implementations may allow for modification of any edge property; the only required property is the ability to modify the sharpness of a primitive variable (the crease strength); in this case, only one float argument needs to be specified per vertex in all cases. In all other respects an edge edit is specified in the same way as a vertex edit, except that the paths will be interpreted to point at the origin vertex of the edge; the destination vertex is implied to be the next vertex in order.

A "faceedit" tag has the following structure:

  ["faceedit"] 
     [<Σ(face path lengths)> <(amount of float data per face) × nfaces> <size of string values>] 
     [<face paths>] 
     [<edit values>] 
     [<"hole"> or
      < <"set"|"add"> <variable name> "value" > ]

A face edit is analogous to a vertex edit: it modifies a property of a group of faces each of which can be anywhere in the subdivision hierarchy. The standard properties that can be modified include marking faces as holes and editing the values of uniform or constant primitive variables associated with those faces.

Like vertex edits, operators are described in the string argument list. Unlike the vertex edit tags, some of the arguments to the operators can themselves be strings and can be found in the same string argument list. Hence the format of the string argument list is somewhat more free form.

RIB BINDING
HierarchicalSubdivisionMesh scheme nvertices vertices tags nargs intargs floatargs stringargs
		...parameterlist...
SEE ALSO
      RiPointsPolygons
      RiSubdivisionMesh

5.4 Quadrics

Many common shapes can be modeled with quadrics. Although it is possible to convert quadrics to patches, they are defined as primitives because special-purpose rendering programs render them directly and because their surface parameters are not necessarily preserved if they are converted to patches. Quadric primitives are particularly useful in solid and molecular modeling applications.

All the following quadrics are rotationally symmetric about the z axis (see Figure 5.3). In all the quadrics u and v are assumed to run from 0 to 1. These primitives all define a bounded region on a quadric surface. It is not possible to define infinite quadrics. Note that each quadric is defined relative to the origin of the object coordinate system. To position them at another point or with their symmetry axis in another direction requires the use of a transformation. The geometric normal to the surface points "outward" from the z-axis, if the current orientation matches the orientation of the current transformation and "inward" if they don't match. The sense of a quadric can be reversed by giving negative parameters. For example, giving a negative thetamax parameter in any of the following definitions will turn the quadric inside-out.

Each quadric has a parameterlist. This is a list of token-array pairs where each token is one of the standard geometric primitive variables or a variable which has been defined with RiDeclare. For all quadrics, primitive variables of class constant and uniform must supply a single data element of the appropriate type. Primitive variables that are varying or vertex must supply 4 data values, which will be interpolated bilinearly across the quadric surface.  

Position variables should not be given with quadrics. All angular arguments to these functions are given in degrees. The trigonometric functions used in their definitions are assumed to also accept angles in degrees.

Strictly speaking, torii are not quadrics, but we include them in this section because they fit nicely here.


RiSphere( RtFloat radius, RtFloat zmin, RtFloat zmax, RtFloat thetamax, 
	...parameterlist... )

Requests a sphere defined by the following equations:

Note that if zmin > -radius or zmax < radius, the bottom or top of the sphere is open, and that if thetamax is not equal to 360 degrees, the sides are also open.

RIB BINDING
	Sphere radius zmin zmax thetamax ...parameterlist...
	Sphere [radius zmin zmax thetamax] ...parameterlist...

EXAMPLE
	RiSphere(0.5, 0.0, 0.5, 360.0, RI_NULL);

SEE ALSO
	RiTorus

RiCone( RtFloat	height, RtFloat	radius, RtFloat	thetamax, ...parameterlist... )

Requests a cone defined by the following equations:

Note that the bottom of the cone is open, and if thetamax is not equal to 360 degrees, the sides are open.

RIB BINDING
	Cone height radius thetamax ...parameterlist...
	Cone [height radius thetamax] ...parameterlist...

EXAMPLE
	RtColor four_colors[4];
	RiCone(0.5, 0.5, 270.0, "Cs", (RtPointer)four_colors, RI_NULL);

SEE ALSO
	RiCylinder, RiDisk, RiHyperboloid
 

RiCylinder( RtFloat radius, RtFloat zmin, RtFloat zmax, RtFloat thetamax,
	 ...parameterlist... )

Requests a cylinder defined by the following equations:

Note that the cylinder is open at the top and bottom, and if thetamax is not equal to 360 degrees, the sides also are open.

RIB BINDING
	Cylinder radius zmin zmax thetamax parameterlist
	Cylinder [radius zmin zmax thetamax] parameterlist

EXAMPLE
	Cylinder  .5 .2 1 360

SEE ALSO
	RiCone, RiHyperboloid

RiHyperboloid( RtPoint point1, RtPoint point2, RtFloat thetamax, 
	...parameterlist... )

Requests a hyperboloid defined by the following equations:

assuming that point1 = (x1,y1, z1) and point2 = (x2, y2, z2).

The cone, disk and cylinder are special cases of this surface. Note that the top and bottom of the hyperboloid are open when point1 and point2, respectively, are not on the z-axis. Also, if thetamax is not equal to 360 degrees, the sides are open.

RIB BINDING
	Hyperboloid x1 y1 z1 x2 y2 z2 thetamax parameterlist
	Hyperboloid [x1 y1 z1 x2 y2 z2 thetamax] parameterlist

EXAMPLE
	Hyperboloid 0 0 0 .5 0 0 270 "Cs" [1 1 1  .5 .9 1  .2 .9 0  .5 .2 0]

SEE ALSO
     RiCone, RiCylinder, RiDisk

RiParaboloid( RtFloat rmax, RtFloat zmin, RtFloat zmax, RtFloat thetamax,
 	...parameterlist... )

Requests a paraboloid defined by the following equations:

Note that the top of the paraboloid is open, and if thetamax is not equal to 360 degrees, the sides are also open.

RIB BINDING
	Paraboloid rmax zmin zmax thetamax ...parameterlist...
	Paraboloid [rmax zmin zmax thetamax] ...parameterlist...

EXAMPLE
	Paraboloid .5 .2 .7 270

SEE ALSO
	RiHyperboloid

RiDisk( RtFloat height, RtFloat radius, RtFloat thetamax, 
	...parameterlist... )

Requests a disk defined by the following equations:

Note that the surface normal of the disk points in the positive z direction when thetamax is positive.

RIB BINDING
	Disk height radius thetamax parameterlist
	Disk [height radius thetamax] parameterlist

EXAMPLE
	RiDisk(1.0, 0.5, 270.0, RI_NULL);

SEE ALSO
	RiCone, RiHyperboloid

RiTorus( RtFloat majorradius, RtFloat minorradius, RtFloat phimin, 
	RtFloat phimax, RtFloat thetamax, ...parameterlist... )

Requests a torus defined by the following equations:

Note that if phimax-phimin or thetamax is not equal to 360 degrees, the torus is open.

RIB BINDING
	Torus rmajor rminor phimin phimax thetamax parameterlist
	Torus [rmajor rminor phimin phimax thetamax] parameterlist

EXAMPLE
	Torus 1 .3 60 90 360

SEE ALSO
	RiSphere

Figure 5.3 Quadric surface primitives

  

 

(click on image to view a larger version)


5.5 Point and Curve Primitives

The RenderMan Interface includes lightweight primitives for specifying point clouds, lines, curves, or ribbons. These primitives are especially useful for representing many particles, hairs, etc.


RiPoints ( RtInt npoints, ...parameterlist...)

Draws npoints number of point-like particles. parameterlist is a list of token-array pairs where each token is one of the standard geometric primitive variables, a variable that has been defined with RiDeclare, or is given as an inline declaration. The parameter list must include at least position ("P") information, one value for each particle. If a primitive variable is of class varying or vertex, the array contains npoints data values of the appropriate type, i.e., one per particle. If the variable is uniform or constant, the array contains a single element.

The size, in object space, of each particle can be specified in the parameter list by using the primitive variable "width", which provides a varying float. If "width" is not specified in the parameter list then it will default to 1.0, meaning that all particles should have an apparent diameter 1.0 units in object space. If all the points are of the same size, the user may specify the variable "constantwidth", which is defined as type constant float to supply a single width value for all points.

Each particle is treated independently. This means a particle is shaded only once and does not have access to derivative information.

RIB BINDING
	Points ...parameterlist...

The number of points is determined implicitly by the length of the "P" array.

EXAMPLE
	Points "P" [.5 -.5 0 -.5 -5 0 -.5 .5 0 .5 .5 0] "width" [.1 .12 .05 .02]

RiCurves ( RtToken type, RtInt ncurves, RtInt nvertices[], RtToken wrap, ...parameterlist...)

Draws ncurves number of lines, curves, or ribbon-like particles of specified width through a set of control vertices. Multiple disconnected individual curves may be specified using one call to RiCurves. The parameter ncurves is the number of indi-vidual curves specified by this command, and nvertices is an array of length ncurves integers specifying the number of vertices in each of the respective curves.

The interpolation method given by type can be either "linear" or "cubic". Cubic curves interpolate using the v basis matrix and step size set by RiBasis. The u parameter changes across the width of the curve (if it has any discernable width), whereas the v parameter changes across the length of the curve (i.e., the direction specified by the control vertices). Curves may wrap around in the v direction, depending on whether wrap is "periodic" or "nonperiodic". Curves that wrap close upon themselves at the ends and the first control points will be automatically repeated. As many as three control points may be repeated, depending on the basis matrix of the curve.

parameterlist is a list of token-array pairs where each token is one of the standard geometric primitive variables, a variable that has been defined with RiDeclare, or is given as an inline declaration. The parameter list must include at least position ("P" or "Pw") information. The width along the curve may be specified with either a special "width" parameter that is a varying float argument, or a "constantwidth" parameter that is a constant float (one value for the entire RiCurves). Widths are specified in object space units of the curve. If no "width" vector or "constantwidth" value is given, the default width is 1.0 units in object space.

Primitive variables of class constant should supply a single value of the appropriate type for the entire RiCurves. Primitive variables of class uniform should supply a total of ncurves values of the appropriate type. Primitive variables of class varying should supply  (nsegs i + 1)   values for nonperiodic curves, and nsegs i   values for periodic curves, where nsegs i is the number of segments of the ith curve (see below). Primitive variables of class vertex should supply nvertices i   values of the appropriate type, that is, one value for every control vertex "P".

The number of piecewise-linear or piecewise-cubic segments of each individual curve is given by

Since the control vertices only specify the direction of the “spine" of the curves, by default the curves are assumed to always project a cross-section of the specified width (as if it were a hair or a strand of spaghetti). However, if "N" values are supplied, the curves will be interpreted as flat" ribbons oriented perpendicularly to the supplied normals, thus allowing user-controlled rotation of the ribbon.

RIB BINDING
	Curves type [nvertices] wrap ...parameterlist...

The number of curves is determined implicitly by the length of the nvertices array.

EXAMPLE
	Curves "cubic" [4] "nonperiodic" "P" [0 0 0 -1 -.5 1 2 .5 1 1 0 -1 ] "width" [.1 .04]
	Curves "linear" [5] "nonperiodic" "P" [0 0 0 3 4 5 -1 -.5 1 2 .5 1 1 0 -1 ]
		"constantwidth" [0.075]
SEE ALSO
	RiBasis

5.6 Blobby Implicit Surfaces

The RenderMan Interface allows the use of free-form, self-blending, implicit-function surfaces in the style of Jim Blinn's blobby molecules, Nishimura et al.'s metaballs, and Wyvill, McPheeters, and Wyvill's soft objects. Blobby surfaces may be composed of spherical and sausage-like line-segment primitives with extremely flexible control over blending. The surface type also provides for repulsion to avoid intersection with irregular ground planes, represented by depth maps.

RiBlobby ( RtInt nleaf, RtInt ncode, RtInt code[], RtInt nfloats, RtFloat floats[], 
	RtInt nstrings, RtString strings[], ...parameterlist...)

The code array is a sequence of machine language-like instructions describing the object's primitive blob fields and the operations that combine them. Floating point parameters of the primitive fields are stored in the floats array. File names of the depth files of repellers are in the strings array. The integer nleaf is the number of primitive blobs in object, also the number of items in each varying or vertex parameter. Parameters with storage class constant or uniform have exactly one data value for the entire RiBlobby primitive.

Each instruction has a numeric opcode followed by a number of operands. Instructions specifying primitive fields start at 1000 and are listed in Table 5.2.

Table 5.2: RiBlobby opcodes for primitive fields.

Opcode Operands Operation
1000 float constant
1001 float ellipsoid
1002 float segment blob
1003 float, string  repelling plane
1004 name, count, floats, count, strings field plugin
1005-1099   reserved

For all four of these operators, the operands are indices into the appropriate arrays. For opcode 1000 (constant) the operand indexes a single floating-point number in the floats array. The index of the first item in the array is zero.

There are several more opcodes that compute composite fields by combining the results of previous instructions in various ways. Every instruction in the code array has a number, starting with zero for the first instruction, that when used as an operand refers to its result. The combining opcodes are given in Table 5.3.

Table 5.3: RiBlobby opcodes for combining fields.

Opcode  Operands  Operation
0 count, ...  add
1 count, ...  multiply
2 count, ...  maximum
3 count, ...  minimum
4 subtrahend, minuend  subtract
5 dividend, divisor  divide
6 negand negate
7 idempotentate identity
8-99   reserved

The add, multiply, maximum, and minimum operations all take variable numbers of arguments. The first argument is the number of operands, and the rest are indices of results computed by previous instructions. The identity operator does nothing useful, and is only included for the convenience of programs that automatically generate RenderMan input.

RiBlobby primitives may be shaded much like ordinary parametric primitives, with the caveat that just like subdivision surfaces, they have no global u and v parameters. Nevertheless, they may be given vertex values by attaching scalar values or reference coordinate fields to primitive sub-objects.

The Plugin Interface

For efficiency, PRMan's blobby implementation depends on getting quite a lot of information from the primitives, so this interface is fairly elaborate. It is possible to get away with just writing a constructor and methods that compute the field value and its gradient; the price you pay is efficiency and accuracy.

Every Implicit Field plugin must have an entry point declared:

	extern "C" ImplicitField *ImplicitFieldNew(
		int nfloat, const RtFloat *float0, const float *float1,
		int nstring, const RtString *string);

The arguments are just the floating point and string parameters specified when the plugin was mentioned in the code of the RiBlobby call. For motion blur purposes, float0 and float1 give the floating point values at shutter open and shutter close. If RiBlobby was not called in a motion block, they are identical. It is guaranteed that no argument data will be freed during the life of the plugin. In addition, the plugin must define a variable:

	extern "C" const int ImplicitFieldVersion=1;
specifying that this plugin implements version 1 of the field plugin interface. (There are no other versions, but there may be in the future.) The FIELDCREATE manifest, in ImplicitField.h defines ImplicitFieldVersion appropriately and emits the function header for ImplicitFieldNew. (The example below indicates usage.)

The return value of ImplicitFieldNew is an instance of a subclass of class ImplicitField, whose definition is in ImplicitField.h in the PRMan include directory:

	#include 
	class ImplicitVertexValue{
	private:
		/* inhibit copying */
		ImplicitVertexValue(const ImplicitVertexValue &);
		ImplicitVertexValue &operator=(const ImplicitVertexValue &);
	public:
		ImplicitVertexValue(){}
		virtual void GetVertexValue(RtFloat *result, const RtPoint p)=0;
	};
	class ImplicitField{
	public:
		/*
		 * bounding box at t=0,
		 * ImplicitField::ImplicitField fills this in
		 */
		RtBound bbox;
	private:
		/* inhibit copying */
		ImplicitField(const ImplicitField &);
		ImplicitField &operator=(const ImplicitField &);
	public:
		ImplicitField(){}
		virtual ~ImplicitField(){}
		virtual RtFloat Eval(const RtPoint p) = 0;
		virtual void GradientEval(RtPoint result, const RtPoint p)=0;
		virtual void Range(RtInterval result,
		    const RtPoint corners[8], const RtVolumeHandle h){
			result[0]=-1e30;
			result[1]=1e30;
		}
		virtual void Motion(RtPoint result, const RtPoint p){
			result[0]=0.;
			result[1]=0.;
			result[2]=0.;
		}
		virtual void BoxMotion(RtBound result, const RtBound b){
			for(int i=0;i!=6;i++) result[i]=b[i];
		}
		virtual void VolumeCompleted(const RtVolumeHandle h){}
		virtual ImplicitVertexValue *CreateVertexValue(
		    const RtToken name, int nvalue){
			return 0;
		}
	};

The bbox field must be filled in during initialization with a bounding box (in the object coordinate system active at the call to RiBlobby) outside which the field value is guaranteed to be identically zero. (Type RtBound is defined in ri.h to be an array of 6 floats. bbox[0], bbox[2] and bbox[3] are the lower bounds on x, y and z, and bbox[1], bbox[3] and bbox[5] are the upper bounds.)

The methods are:

RtFloat Eval(const RtPoint p)
Eval returns the field value at point p, in object coordinates, at shutter open time.
void GradientEval(RtPoint result, const RtPoint p)
GradientEval stores the field gradient at point p (calculated at shutter open time) into result.
void Range(RtInterval result, const RtPoint corners[8], RtVolumeHandle h)
Range stores into result an interval that bounds the values of the field function inside the convex frustum with the given corners, at shutter open. The base version always stores result[0]=-1e30 and result[1]=1e30. The volume handle h identifies the volume. The same value will later be passed to a call of VolumeCompleted.
void Motion(RtPoint result, const RtPoint p)
Motion stores into result how much the point p moves between shutter open and shutter close. The base version, assuming no motion, sets result to (0,0,0).
void BoxMotion(RtBound result, const RtBound b)
BoxMotion stores into result a bounding box at shutter closecorresponding to the argument b. The base version just copies b to result.
void VolumeCompleted(RtVolumeHandle h)
This is a courtesy callback, hinting that prman has finished processing all points inside the volume with the given handle, so that the plugin can discard data that it no longer needs. Using VolumeCompleted is a little tricky: prman calls Range with a particular RtVolumeHandle when it starts to work on a part of the level-set, and calls VolumeCompleted with the same handle when it's done. But it may in the interim have subdivided and called Range on smaller contained volumes in which it may maintain an interest after it has called VolumeCompleted on the parent volume. The handle passed to VolumeCompleted may be reused in a subsequent call to Range, but it will never ambiguously identify two volumes in which prman simultaneously maintains an interest.
ImplicitVertexValue *CreateVertexValue(const RtToken name, int nvalue)
Informs the plugin of a vertex variable declaration, asking that the plugin provide prman with an entry point that evaluates the variable. Arguments are the name of a vertex variable and the number of float components it has, 1 for scalars or 3 for point types. You are requested to allocate (using C++'s new operator) and return an instance of a subclass of ImplicitVertexValue. Prman will call delete on the result when it is done with it. If name is unknown to the plugin, the call should return NULL. (The base-class implementation always returns NULL.)

The ImplicitVertexValue class has a single virtual method, GetVertexValue(RtFloat *result, const RtPoint p), which the plugin should define to store in result the value of the named vertex variable, evaluted at point p. On entry the result parameter has the value given to the vertex variable in the RiBlobby call.

RIB BINDING
	Blobby nleaf [ code ] [ floats ] [ strings ] ...parameterlist...
EXAMPLE
	Blobby 2 [ 1001 0 1003 0 16 0 2 0 1 ]
		[1.5 0 0 0 0 1.5 0 0 0 0 1.5 0 0 0 -.1 1 .4 .01 .3 .08] ["flat.zfile"]

5.7 Procedural Primitives

Procedural primitives can be specified as follows:


RiProcedural( RtPointer data, RtBound bound, 
	RtProcSubdivFunc subdividefunc, RtProcFreeFunc freefunc )

This defines a procedural primitive. The data parameter is a pointer to an opaque data structure that defines the primitive. (The rendering program does not “look inside" data, it simply records it for later use by the procedural primitive.) bound is an array of floats that define the bounding box of the primitive in object space. subdividefunc is the routine that the renderer should call (when necessary) to have the primitive subdivided. A bucket-based rendering scheme can potentially save memory space by delaying this call until the bounding box overlaps a bucket that must be rendered. The calling sequence for subdividefunc is:

	(*subdividefunc)( RtPointer data, RtFloat detail )

where data is the parameter that was supplied in defining the primitive, and detail is the screen area of the bound of the primitive. When subdividefunc is called, it is expected to subdivide the primitive into other smaller procedural primitives or into any number of non-procedural primitives. If the renderer can not determine the true detail of the bound (e.g., if the geometric primitive data is being archived to a file), subdividefunc may be called with a detail value equal to RI_INFINITY. This should be interpreted by the subdividefunc as a request for the immediate full generation of the procedura primitive.

freefunc is a procedure that the rendering program calls to free the primitive when the data is no longer needed. The calling sequence for freefunc is:

	(*freefunc)( RtPointer data )

Note that the rendering program may call back multiple times with the same procedural primitive, so the data area should not be overwritten or freed until the freefunc is called.

The RenderMan Interface provides three standard built-in procedural primitives. Their declarations are:

	RtVoid RiProcDelayedReadArchive (RtPointer data, RtFloat detail);
	RtVoid RiProcRunProgram (RtPointer data, RtFloat detail);
	RtVoid RiProcDynamicLoad (RtPointer data, RtFloat detail);

These built-in procedurals are the subdivide routines.

RIB BINDING
	Procedural procname [args] [bound]

The procedural name procname must be a built-in procedural, either one of the standard ones described below or an implementation-specific procedural. The args parameter is an array of strings supplying arguments to the built-in procedural. The expected arguments for each type of procedural are explained in the following sections on those primitives.

EXAMPLE
	RtString args[] = { "sodacan.rib" } ;
	RtBound mybound = { -1, 1, -1, 1, 0, 6 } ;
	RiProcedural ((RtPointer)args, mybound, RiProcDelayedReadArchive, NULL);
	Procedural DelayedReadArchive [ "sodacan.rib" ] [ -1 1 -1 1 0 6 ]
SEE ALSO
RiProcDelayedReadArchive, RiProcRunProgram, RiProcDynamicLoad

RiProcDelayedReadArchive ( RtPointer data, RtFloat detail )

The built-in procedural RiProcDelayedReadArchive operates exactly like RiReadArchive, except that the reading is delayed until the procedural primitive bounding box is reached (unlike RiReadArchive, which reads RIB files immediately during parsing). The advantage of the procedural primitive is that because the reading is delayed, memory for the read primitives is not used unless (or until) the contents of the bound-ing box are actually needed.

The data parameter consists of a pointer to an RtString array. The first element of the array (that is, ((RtString*)data)[0]) is the name of a RIB file to read.  The file can contain any amount of valid RIB, although it is suggested that it either be flat" (have no hierarchy) or have some balanced hierarchy (matching Begin-End calls). As with all RIB parameters that are bounding boxes, the boundingbox is an array of six floating-point numbers, which are xmin, xmax, ymin, ymax, zmin, zmax in the current object space.

RIB BINDING
	Procedural "DelayedReadArchive" [filename] [boundingbox]

The argument string list contains a single string giving the filename the file to read when the contents of the boundingbox are needed.

EXAMPLE
	RtString args[] = f "sodacan.rib" g ;
	RtBound mybound = f -1, 1, -1, 1, 0, 6 g ;
	RiProcedural ((RtPointer)args, mybound, RiProcDelayedReadArchive, NULL);
	Procedural "DelayedReadArchive" [ "sodacan.rib" ] [ -1 1 -1 1 0 6 ]
SEE ALSO
	RiProcedural, RiProcRunProgram, RiProcDynamicLoad

RiProcRunProgram ( RtPointer data, RtFloat detail )

The built-in procedural RiProcRunProgram will run an external helper program, capturing its output and interpreting it as RIB input. The data parameter consists of a pointer to an RtString array. The first element of the array is the name of the program to run (including any command line arguments), and the second element is an argument string to be written to the standard input stream of the helper program.

The helper program generates geometry on-the-fly in response to procedural prim-itive requests in the RIB stream. Each generated procedural primitive is described by a request to the helper program, in the form of an ASCII datablock that describes the primitive to be generated. This datablock can be anything that is meaningful and adequate to the helper program, such as a sequence of a few floating-point numbers, a filename, or a snippet of code in an interpreted modeling language. In addition, the renderer supplies the detail size of the primitive’s bounding box, so that the generating program can decide what to generate based on how large the object will appear on-screen.

The generation program reads the request datablocks on its standard input stream and emits RIB commands on its standard output stream. These RIB streams are read into the renderer as though they were read from a file (as with ReadArchive) and may include any standard RenderMan attributes and primitives (including procedural primitive calls to itself or other helper programs). As long as any procedural primitives exist in the rendering database that require the same helper program for processing, the socket connection to the program will remain open. This means that the program should be written with a loop that accepts any number of requests and generates a RIB “snippet" for each one.

RIB BINDING
Procedural "ReadProgram" [programname datablock] [boundingbox] 

The argument list is an array of two strings. The first element is the name of the helper program to execute and may include command line options. The second element is the generation-request data block. It is an ASCII printable string that is meaningful to the helper program and describes the children to be generated. Notice that the data block is a quoted ASCII string in the RIB file, so if it is a complex description that contains quote marks or other special characters, these must be escaped in the standard way. (Similar to C, using backslash metacharacters like \n) The boundingbox is an array of six floating-point numbers, which is xmin, xmax, ymin, ymax, zmin, zmax in the current object space.

EXAMPLE
        RtString args[] = { "perl gencan.pl", "" } ;
	RtBound mybound = { -1, 1, -1, 1, 0, 6 } ;
	RiProcedural ((RtPointer)args, mybound, RiProcRunProgram, NULL);
	Procedural "RunProgram" [ "perl gencan.pl" "" ] [ -1 1 -1 1 0 6 ] 

The example above presumes that you have a Perl script called gencan.pl that will generate RIB for a model and write that RIB to its standard output stream.

SEE ALSO
RiProcedural, RiProcRunProgram, RiProcDynamicLoad

RiProcDynamicLoad ( RtPointer data, RtFloat detail )

A more efficient method for accessing subdivision routines is to write them as dynamic shared objects, DSOs (on some systems called dynamically linked libraries, or DLLs), and dynamically load them into the renderer executable at run-time. In this case, you write your subdivision and free routines in C, exactly as you would if you were writing them to be linked into the renderer using the C RiProcedural interface. DSOs are compiled with special compiler options to make them run-time loadable and are specified in the RIB file by the name of the shared object file. The renderer will load the DSO the first time that the subdivision routine must be called, and from then on, it is called as if (and executes as fast as if) it were statically linked. DSOs are more efficient than external programs because they avoid the overhead of interprocess communication.

When writing a procedural primitive DSO, you must create three specific public subroutine entry points, named Subdivide, Free, and ConvertParameters. Subdivide is a standard RiProcedural() primitive subdivision routine, taking a blind data pointer to be subdivided and a floating-point detail to estimate screen size. Free is a standard RiProcedural primitive free routine, taking a blind data pointer to be released. ConvertParameters is a special routine that takes a string and returns a blind data pointer. It will be called exactly once for each DynamicLoad procedural primitive in the RIB file, and its job is to convert a printable string version of the progenitor's blind data (which must be in ASCII in the RIB file), into something that the Subdivide routine will accept.

The C prototypes for these functions are as follows:

RtPointer ConvertParameters(char * initialdata ); 
void Subdivide(RtPointer blinddata, RtFloat detailsize ); 
void Free( RtPointer blinddata ); 

Note that if the DSO Subdivide routine wants to recursively create child procedural primitives of the same type as itself, it should specify a direct recursive call to itself, with RiProcedural(newdata,newbound,Subdivide,Free), not call itself as a DynamicLoad procedural. The latter would eventually just call the former after wasting time checking for and reloading the DSO.

The conventions for how dynamic objects are compiled are implementation-dependent (and also very likely OS-dependent).

RIB BINDING
        Procedural "DynamicLoad" [dsoname paramdata] [boundingbox] 

The argument list is an array of two strings. The first element is the name of the shared object file that contains the three required entry points and has been compiled and pre-linked as described earlier. The second element is the ASCII printable string that represents the initial data to be sent to the ConvertParameters routine. The boundingboxis an array of six floating-point numbers, which is xmin, xmax, ymin, ymax, zmin, zmax in the current object space.

 EXAMPLE
	RtString args[] = { "mydso.so", "" } ;
	RtBound mybound = { -1, 1, -1, 1, 0, 6 } ;
	RiProcedural ((RtPointer)args, mybound, RiProcDynamicLoad, NULL);

 	Procedural "DynamicLoad" [ "mydso.so" "" ] [ -1 1 -1 1 0 6 ]
SEE ALSO
	RiProcedural, RiProcDelayedReadArchive, RiProcRunProgram

5.8 Implementation-specific Geometric Primitives

Additional geometric primitives can be specified using the following procedure.


RiGeometry( RtToken type, ...parameterlist... )

This procedure provides a standard way of defining an implementation-specific geometric primitive. The values supplied in the parameter list for each primitive is implementation specific.

RIB BINDING

	Geometry name ...parameterlist...

EXAMPLE

	RiGeometry("teapot," RI_NULL);

5.9 Solids and Spatial Set Operations

All of the previously described geometric primitives can be used to define a solid by bracketing a collection of surfaces with RiSolidBegin and RiSolidEnd. This is often referred to as the boundary representation of a solid. When specifying a volume it is important that boundary surfaces completely enclose the interior. Normally it will take several surfaces to completely enclose a volume since, except for the sphere, the torus, and potentially a periodic patch or patch mesh, none of the geometric primitives used by the rendering interface completely enclose a volume. A set of surfaces that are closed and non-self-intersecting unambiguously defines a volume. However, the RenderMan Interface performs no explicit checking to ensure that these conditions are met. The inside of the volume is the region or set of regions that have finite volume; the region with infinite volume is considered outside the solid. For consistency the normals of a solid should always point outwards.


RiSolidBegin( RtToken operation )
RiSolidEnd()

RiSolidBegin the definition of a solid. operation may be one of the following tokens:

intersection and union operations form the set intersection and union of the specified solids. Difference operations require at least 2 parameter solids and subtract the last n-1 solids from the first (where n is the number of parameter solids).

When the innermost solid block is a "primitive" block, no other RiSolidBegin calls are legal. When the innermost solid block uses any other operation, no geometric primitives are legal.

RiSolidEnd terminates the definition of the solid.

RIB BINDING

	SolidBegin operation

	SolidEnd -

EXAMPLE

	SolidBegin "union"

SEE ALSO

	RiInterior, RiTrimCurve

A single solid sphere can be created using

	SolidBegin( "primitive" );
		RiSphere( 1.0, -1.0, 1.0, 360.0, RI_NULL );
	RiSolidEnd();

Note that if the same sphere is defined outside of a RiSolidBegin-RiSolidEnd block, it is not treated as a volume-containing solid. A solid hemisphere can be created with

SolidBegin( "primitive" );
	RiSphere( 1.0, 0.0, 1.0, 360.0, RI_NULL );
	RiDisk( 0.0, 1.0, -360.0, RI_NULL );
RiSolidEnd();

(Note that the -360 causes the surface normal of the disk to point towards negative z.)

A composite solid is one formed using spatial set operations. The allowed set operations are "intersection," "union," and "difference." A spatial set operation has n operands, each of which is either a primitive solid defined using SolidBegin("primitive")-RiSolidEnd, or a composite solid that is the result of another set operation. For example, a closed cylinder would be subtracted from a sphere as follows:

	SolidBegin( "difference" );
		SolidBegin( "primitive" );
			RiSphere( 1.0, -1.0, 1.0, 360.0, RI_NULL );
		RiSolidEnd();
		SolidBegin( "primitive" );
			RiDisk(  2.0, 0.5,  360.0, RI_NULL );
			RiCylinder( 0.5, -2.0, 2.0, 360.0, RI_NULL );
			RiDisk( -2.0, 0.5, -360.0, RI_NULL );
		RiSolidEnd();
	RiSolidEnd();

When performing a difference the sense of the orientation of the surfaces being subtracted is automatically reversed.

Attributes may be changed freely inside solids. Each section of a solid's surface can have a different surface shader and color. For consistency a single solid should have a single interior and exterior volume shader.

If the Solid Modeling optional capability is not supported by a particular implementation, all primitives are rendered as a collection of surfaces, and the spatial set operators are ignored.

5.10 Retained Geometry

A single geometric primitive or a list of geometric primitives may be retained by enclosing them with RiObjectBegin and RiObjectEnd. The RenderMan Interface allocates and re-turns an RtObjectHandle for each retained object defined in this way. This handle can sub-sequently be used to reference the object when creating instances with RiObjectInstance.  Objects are not rendered when they are defined within an RiObjectBegin-RiObjectEnd block; only an internal definition is created.

Transformations, and even Motion blocks, may be used inside an Object block, though they obviously imply a relative transformation to the coordinate system active when the Object is instanced. All of an object’s attributes are inherited at the time it is instanced, not at the time at which it is created. So, for example, shader assignments or other attributes are not allowed within an Object block. The only exception is RiBasis, which may set the interpolation basis matrix used for RiPatch, RiPatchMesh, or RiCurves primitives that are within the Object block.


RtObjectHandle RiObjectBegin()
               RiObjectEnd()

RiObjectBegin starts the definition of an object and return a handle for later use with RiObjectInstance. If the handle returned is NULL, an object could not be created.
RiObjectEnd ends the definition of the current object.

RIB BINDING

	ObjectBegin handle
	ObjectEnd -

The handle is a unique object identification number or string which is provided by the RIB client to the RIB server. Both client and server maintain independent mappings between the handle and their corresponding RtObject-Handles. If handle has been used to define a previous object, that object is replaced with the new definition. When specified as a number, the handle must be in the range 0 to 65535.

EXAMPLE
	ObjectBegin 2
		Sphere 1 -1 1 360
	ObjectEnd

SEE ALSO

     RiFrameEnd, RiObjectInstance, RiWorldEnd

RiObjectInstance( RtObjectHandle handle );

Create an instance of a previously defined object. The object inherits the current set of attributes defined in the graphics state.

RIB BINDING

	ObjectInstance handle

The object must have been defined to have a handle with a previous RiObjectBegin.

EXAMPLE

     ObjectInstance 2

SEE ALSO

	RiFrameEnd, RiObjectBegin, RiWorldEnd

No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior written permission of Pixar. The information in this publication is furnished for informational use only, is subject to change without notice and should not be construed as a commitment by Pixar. Pixar assumes no responsibility or liability for any errors or inaccuracies that may appear in this publication.

 

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