# Class Specifiers for Primitive Variables

## Class Specifiers for Primitive Variables

*March, 1997*

### Contents:

## Primitive Variables

It is often useful to attach data to the surface of an object. In
RenderMan this is done by including the data as a token value pair when a
surface primitive is declared. Once data is attached it is available as
parameters to the shaders that are bound to the surface primitive.
Parameters that are attached to surface geometry in this way are called
*primitive variables*. Any parameter to a shader can be attached
to surface geometry. For example, in the case of a bilinear patch:

Declare"bar" "constant float" . . .Surface"foo"Patch"bilinear" "P" [-0.5 0.5 0 0.5 0.5 0 -0.5 -.5 0 0.5 -.5 0] "bar" [.5]

The *primitive variable* bar is now set to .5 and is constant
over the entire patch. The shader foo is defined as follows:

surfacefoo (... float bar = 1.0;...) { . . . }

The key word **constant** used in the Declare statement
is called a *class specifier*. The next section introduces
*class specifiers*.

## Class Specifiers

*Class specifiers* identify how a *primitive variable*
will be interpolated over the uv parameter space of a surface primitive.
The set of class specifiers for Photorealistic RenderMan is:

**Constant:** One value remains constant over the entire
surface primitive.

**Uniform:** One value remains constant for each uv patch
segment of the surface primitive.

**Varying:** Four values are interpolated over each uv patch
segment of the surface. Bilinear interpolation is used for interpolation
between the four values.

**Vertex:** Values are interpolated between each vertex
in the surface primitive. The basis function of the surface is used for
interpolation between vertices.

**Facevarying:** For polygons and subdivision surfaces,
four values are interpolated over each face of the
mesh. Bilinear interpolation is used for interpolation between the
four values.

A variable's class and the surface primitive determine how many samples
must be included when a *primitive variable* is attached to a surface
primitive. The following two tables identify the correct number of values
that must be included for each combination of *class specifier* and
surface primitive.

**Quadric and Polygon Surface Primitives**

Surface Primitive | Constant | Uniform | Varying | Vertex | Facevarying |
---|---|---|---|---|---|

Quadrics | 1 | 1 | 4 | 4 | 4 |

Polygon | 1 | 1 | nverts | nverts | nverts |

GeneralPolygon | 1 | 1 | nverts | nverts | nverts |

PointsPolygons | 1 | npolys | nverts | nverts | sum(nvertices_{i}) |

PointsGeneralPolygons | 1 | npolys | nverts | nverts | sum(nvertices_{i}) |

Points | 1 | 1 | npoints | npoints | npoints |

SubdivisionMesh | 1 | nfaces | nverts | nverts | sum(nvertices_{i}) |

**Parametric Surface Primitives**

Surface Primitive | Constant | Uniform | Varying and Facevarying | Vertex |
---|---|---|---|---|

Patch bilinear | 1 | 1 | 4 | 4 |

Patch bicubic | 1 | 1 | 4 | 16 |

PatchMesh bilinear | 1 | (nu-unowrap)* (nv-vnowrap) | nu*nv | nu*nv |

PatchMesh bicubic | 1 | usegs*vsegs | (usegs+unowrap)* (vsegs+vnowrap) |
nu*nv |

NuPatch | 1 | (nu-uorder+1)* (nv-vorder+1) |
(nu-uorder+2)* (nv-vorder+2) | nu*nv |

Curves linear | 1 | sum(nsegs_{i}-nowrap) |
sum(nvertices_{i}) |
sum(nvertices_{i}) |

Curves cubic | 1 | sum(nsegs_{i}) |
sum(nsegs_{i}+nowrap) |
sum(nvertices_{i}) |

Blobby | 1 | 1 | nleaf | nleaf |

Note, in the above table for parametric surface primitives, `unowrap`, `vnowrap`, and `nowrap` are assumed to be a boolean
value of either 0 = periodic or 1 = nonperiodic. This is NOT how `uwrap`,
`vwrap`, and `wrap` are used in the RenderMan interface.

For a bicubic patch mesh the number of values for the **uniform
** and **varying** classes is determined by the number
of patch segments in the u and v direction (`usegs`, `vsegs`,
and `nsegs _{i}` in the above table). This is determined by
the u and v basis functions of the patch mesh. The following table gives
the formula for calculating the number of patch segments, in the u or v
direction, of a patch mesh. N is the number of control vertices in the u
or v direction (

`nu`or

`nv`in the above table).

**Patch and Curve Segments**

Basis | Nonperiodic | Periodic | Step |
---|---|---|---|

Bezier | (N-1)/3 | N/3 | 3 |

B-spline | (N-3) | N | 1 |

Catmull-Rom | (N-3) | N | 1 |

Hermite | (N-2)/2 | N/2 | 2 |

Power | N/4 | N/4 | 4 |

For the Curves primitive the number of sample points is calculated by
summing the number of curve segments `nsegs _{i}` for each
curve

`i`that is included in the

**RiCurves**statement.

When attaching *primitive variables* to a NURB patch, it is
important to include the appropriate number of samples in the
*primitive variable* data, even for degenerate patch segments.

## Example

One of the most common examples of attaching *primitive variables*
to an object's surface is used in animation when deforming geometry. In this
case we want the texture to stick to the geometry even though the vertices
are moving through space.

The image to the left shows a pop can with a very simple procedural shader attached to it. Now if we bend the pop can by deforming the patch vertices we get the image to the right. Notice how the deformed surface has moved through texture space.

What we really want is to force the texture to stick to the surface. This
is done by attaching the original patch control vertices to the surface as a
*primitive variable*.

The following code segment shows how the original patch control vertices are attached to the surface:

void main(void) { RtFloat usePref[1] = { 1 }; RtInt nu = NU; RtInt uorder = UORDER; RtInt nv = NV; RtInt vorder = VORDER; RtFloat uknot[NU+UORDER]; RtFloat uknot[NV+VORDER]; RtFloat umax, umin, vmax, vmin; RtPoint Pref[NU*NV]; /* holds original CVs */ RtPoint Pdef[NU*NV]; /* holds deformed CVs */ . . . /* Define NURB patch mesh */ . . . /* Deform patch CVs */ . . .RiDeclare("usePref", "constant float");RiDeclare("Pref", "vertex point"); . . .RiSurface("myshader", RI_NULL);RiNuPatch(nu, uorder, uknots, umin, umax, nv, vorder, vknots, vmin, vmax, RI_P, (RiPointer)Pdef, "usePref", (RiPointer)usePref, "Pref", (RiPointer)Pref, RI_NULL); . . . }

The surface shader myshader could be written to use Pref as follows:

surfacemyshader (... uniform float usePref = 0.0; varying point Pref = 0.0;) { point PP; . . . /* Perform texture calculations on PP instead of P */ if (usePref == 0.0) PP = transform("shader",P); /* Use P */ else PP = transform("shader",Pref); /* Use Pref */ . . . }

In the parameter definitions of the surface shader the variables usePref
and Pref are defined as **uniform float** and **varying
point** respectively. The storage modifiers **uniform**
and **varying** are used in the shading language to identify
that a variable is constant or changing over the surface. In the shading
language, variables are either **uniform** or **varying
**, there is no need for **constant** or **vertex
**. If the storage class is left out then a variable will default to
**uniform** if it is declared as a shader parameter, or **
varying** if it is declared local to the shader.

In the image to the right the original control vertices were attached to the deformed geometry as shown in the above code segment. Notice that the texture bends with the deformed surface.