Specifying Primitive Variables


RenderMan supports a rich mechanism for associating arbitrary data with geometric primitives. The basic idea is that you need to access your data within custom shaders and require that the data be automatically interpolated through the same mathematics that surface points undergo. Below we assume that you are familiar with this mechanism as discussed in the RenderMan Specification and in RenderMan AppNote #22.

MTOR supports RenderMan primitive variables by transmitting data from arbitrary Maya attributes associated with your geometric primitives that obey the following naming convention:

When naming your variable, be sure that the name doesn't terminate with the string "Cache" or the number '0'. Maya automatically creates variants of your data by storing cached and preroll versions of your data. So if your data is named rmanFmyS, Maya may create rmanFmyS0 and rmanFmySCache. MTOR ignores variables whose names obey this convention.

By simply adding a custom Maya attribute to a shape node and naming the attribute according to this convention, you signal to MTOR your desire to transmit data associated with the attribute though the RenderMan interface. Now you can wire the maya attribute to other data sources, like MEL expression, procedural ramps or other primitives and access that data within your RenderMan shaders.

MTOR analyzes the dimensions of your attributes and the associated primitive to determine the exact RenderMan declaration. Remember that geometric vertex variable types are subject to transformations before being made available to your shaders. To avoid this transformation, just designate your point, vector or normal data with the rmanF prefix and MTOR will transfer the data either as a vertex or uniform float array.

Here's a recipe:

  1. Select your primitive
  2. Use MEL's addAttr command or the Maya Attribute Editor window to add a custom attribute.
    1. Select the attribute type. To obtain the effect of a data value at every primitive vertex, make sure to create an attribute of type array. Note well: the Add Attribute Window appears to enable the Array control only when working with particle systems. If you'd like to add vertex variables to NURBS or polygon meshes, you'll either need to write a mel script (like the one below) or use Maya Artisan (which currently supports only NURBS).
    2. Provide a datatype hint to MTOR by prepending the appropriate datatype key (as above) to your variable name.
  3. Connect the attribute to a datasource, perhaps by writing a MEL expression or using the connectAttr command.
  4. Write and attach a custom RenderMan shader that has a reference to your custom variable name in the parameter list.
  5. Render

If you have created and associated 2 dynamic maya attributes named rmanFmyS and rmanFmyT, here's a trivial shader that uses them:

surface test(string texturename="";
        varying float myS = 0;
        varying float myT = 0; )
    if (texturename != "") {
        Ci = texture(texturename, myS, myT);
    } else {
        Ci = Cs;


// mtorMapNurbsColor.mel   $Revision: #3 $
//   a nigh-barroque example of setting up RenderMan vertex variables on NURBS
//   several of the utility procedures may be useful.
//   this example:
//	* creates two attributes on your nurbs which loosely represent
//	  the S and T coordinates.
//	* creates a ramp texture node
//	* creates a color RenderMan vertex variable on our nurbs object
//	* creates an arrayMapper node
//	* wires the scalar value through the arrayMapper to
//	    the color vertex variable
//   which enables you to:
//	* use the ramp UI in the multilister/attribute editor to modify
//	    color vertex variables on your surface.
//      * cause colors of arbitrary RenderMan surface shaders (which use Cs)
//	    to be controlled through the Ramp.
//  limitations:
//	* when vertex variables are created, their size is determined
//	    by your NURBS.  If you change the number of CVS in your NURBS
//	    after running this example you'll end up with a mismatch between
//	    the sizes of the arrays and MTOR will not output the vertex variables.
//	* there's no error checking, executing this script repeatedly
//	    will cause unexpected results.
//	* there appears to be a problem in Maya's dependency
//	    Graph caching: changes to the ramp don't immediately
//	    flow through to our rmanCCs.  You can "kick"
//	    the graph recalc by deleting and re-establishing the
//	    connection between the arrayMapper and the rmanFmyS
//	    attribute.  In order to implement this example we had
//	    to reverse engineer the arrayMapper/ramp interaction.
//	    It's entirely possible that we're misusing the node
//	    (suggestions on how to improve this example are encouraged).
//	    On the other hand, it's entirely possible that Maya has a bug.
//	* this technique is *not* a replacement for texture mapping.  The
//	    resolution of the vertex variables is low compared to the number
//	    of pixels, so your resulting images may appear fuzzy.
//  to try this out:
//	* in a clean scene, create a nurbs sphere with 16 sections, 16 spans.
//	* add a light and Render to establish the correct lighting before we
//	    do the test.
//	* in the Maya Script editor window:
//	    - paste this script into the window and execute (alternatively,
//		save this script to a file and "source" it.
//	    - type: mtorMapNurbsColor nurbsSphereShape1;
//	* now re-Render - you should see ramp-like colors across your surface.
//	    If not, type: mtorKickit nurbsSphereShape1 arrayMapper1;
//	    and re-Render.
//	    (This is where you'll run into the problem with dependency
//	    graph updates. In order to cause the ramp's colors to flow
//	    through the arrayMapper node, you may need to "kick" the dependancy
//	    graph. One way to do this is to disconnect, then reconnect the
//	    rmanFmyS attribute.  You can accomplish this either through a trivial
//	    script or via the Connection Editor Window.)
//	* now attach a RenderMan shaders and re-Render - you should see your
//	    colors reflected in the shader.
//  left to the reader:
//	* the variables rmanFmyS and rmanFmyT are output to the RIB file 
//	    and this allows custom shaders to access their values.  If
//	    your custom or standard shader doesn't need access to their
//	    values, it'd be more optimal if the values weren't in the
//	    RIB.  To accomplish this, simply replace all references
//	    to rmanFmyS and rmanFmyT with names like: myS and myT.
//	    Now that they don't start with rmanX, they won't appear
//	    in the RIB file (of course we still need this values for the
//	    purposes of computing the ramp).

global proc string addVertexAttr (string $attr, string $obj, string $attrType)
// description:
//  create a dynamic attribute on the object given by $obj.
//  the attribute will be of type $attrtype (typically "doubleArray")
//  the attribute will be named: $attr (typically something like "rmanFtest")
    string $attrs[] = `listAttr -st $attr $obj`;
    if ($attrs[0] == "")
	// doesn't exist, so create attribute
	addAttr -ln $attr -dt $attrType $obj;
    return ($obj + "." + $attr);

global proc getNurbsDimensions (string $nurbShape, int $dim[])
// description:
//  get the number of control points in the u and v dimensions
//  note that this is dependend on the form of the nurbs.
//  $nurbShape is the name of the shape node of your nurbs,
//  $dim will contain two values on return: ucvs and vcvs,
//  the product of which should equal the number of cvs
    if ( `nodeType $nurbShape` == "nurbsSurface" ) {
	int $ncvs, $uverts, $vverts;
	int $uspans, $vspans, $uorder, $vorder;
	int $udegree, $vdegree, $uform, $vform;
	int $nu, $nv;
	string $nurbattr;
	$nurbattr = ($nurbShape + ".controlPoints");
	$ncvs = `getAttr -s $nurbattr`;
	$nurbattr = ($nurbShape + ".spansU");
	$uspans = `getAttr $nurbattr`;
	$nurbattr = ($nurbShape + ".spansV");
	$vspans = `getAttr $nurbattr`;
	$nurbattr = ($nurbShape + ".degreeU");
	$udegree = `getAttr $nurbattr`;
	$nurbattr = ($nurbShape + ".degreeV");
	$vdegree = `getAttr $nurbattr`;
	$nurbattr = ($nurbShape + ".formU");
	$uform = `getAttr $nurbattr`;
	$nurbattr = ($nurbShape + ".formV");
	$vform = `getAttr $nurbattr`;
	$nu = $uspans + $udegree;
	$nv = $vspans + $vdegree;
	switch ($uform)
	case 0: // open
	    $uverts = $nu;
	case 1: // closed
	case 2: // periodic
	    $uverts = $uspans;
	switch ($vform)
	case 0: // open
	    $vverts = $nv;
	case 1: // closed
	case 2: // periodic
	    $vverts = $vspans;
	$dim[0] = $uverts;
	$dim[1] = $vverts;

global proc initScalarVertexArray(string $style, int $dim[], float $val[])
// description:
//  initialize an array of floats given by $val whose implied dimensionality
//  is given by $dim.  In the case of NURBS surfaces, $dim should contain the
//  results given by a call to getNurbsDimensions above. supported values for
//  $style are: "zero", "one", "uramp" and "vramp".
    int $i, $j, $iv;
	case "zero":
		$val[$i] = 0;
	case "one":
		$val[$i] = 1;
	case "uramp":
	    $iv = 0;
		    $val[$iv] = $i / (float) ($dim[0] - 1);
	case "vramp":
	    $iv = 0;
		    $val[$iv] = $j / (float) ($dim[1] - 1);

global proc setScalarVertexAttr(string $obj, string $attr, float $val[])
// description:
//   Cause the values associated with $val to be stuffed into
//   the maya attribute associated with the node: $obj and whose
//   name is given by $attr.
    string $fullAttr = ($obj + "." + $attr);
    int $size = `size $val`;
    if (0)
	// this seems like it should work, but doesn't (as of Maya 1.5).
	setAttr $fullAttr -type "doubleArray" $size ($val);
	// initialize the array to linear ramp,
	// here we initialize the array as a string
	// since setAttr doesn't seem to want to be passed
	// a float array (as of Maya 1.5).
	int $i;
	string $str = "";
	for($i=0;$i<$size; $i++)
	    $str = ($str + $val[$i] + " ");
	eval setAttr $fullAttr -type "doubleArray" $size $str;

global proc mtorKickit(string $nurbsShape, string $mappernode)
    // there's probably a better way to kick the dependancy graph
    disconnectAttr ($nurbsShape + ".rmanFmyS") ($mappernode + ".uCoordPP");
    connectAttr -f ($nurbsShape + ".rmanFmyS") ($mappernode + ".uCoordPP");

global proc mtorMapNurbsColor(string $nurbsShape)
// description:
//  ... the main entrypoint to this example. $nurbsShape must
//  be the name of an existing nurbs shape node.
//  ex:  mtorMapNurbsColor nurbsSphereShape1;
    int $dim[2];
    float $mys[], $myt[];
    getNurbsDimensions($nurbsShape, $dim);
    addVertexAttr("rmanFmyS", $nurbsShape, "doubleArray");
    initScalarVertexArray("uramp", $dim, $mys);
    setScalarVertexAttr($nurbsShape, "rmanFmyS", $mys);
    addVertexAttr("rmanFmyT", $nurbsShape, "doubleArray");
    initScalarVertexArray("vramp", $dim, $myt);
    setScalarVertexAttr($nurbsShape, "rmanFmyT", $myt);
    addVertexAttr("rmanCCs", $nurbsShape, "vectorArray");
    // create and properly initialize a ramp node
    string $rampnode = `shadingNode -asTexture ramp`;
    string $tx = `shadingNode -asUtility place2dTexture`;
    connectAttr ($tx + ".outUV") ($rampnode + ".uv");
    connectAttr ($tx + ".outUvFilterSize") ($rampnode + ".uvFilterSize");

    // create an arrayMapper node and connect inputs
    string $mappernode = `createNode "arrayMapper"`;
    connectAttr -f ($nurbsShape + ".rmanFmyS") ($mappernode + ".uCoordPP");
    connectAttr -f ($nurbsShape + ".rmanFmyT") ($mappernode + ".vCoordPP");

    // connect the arraymapper to the ramp
    connectAttr -f ($rampnode + ".outColor") ($mappernode + ".computeNode");    
    // now connect arrayMapper's other input
    connectAttr -f ($mappernode + ".outColorPP") ($nurbsShape + ".rmanCCs");
    // we're done! (whew)


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