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:
rmanF
yourvar - constant or vertex floats
rmanP
yourvar - constant or vertex points
rmanV
yourvar - constant or vertex vectors
rmanN
yourvar - constant or vertex normals
rmanC
yourvar - constant or vertex colors
rmanS
yourvar - constant string
rmanM
yourvar - vertex mpoint (for blobs)
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:
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;
break;
case 1: // closed
case 2: // periodic
$uverts = $uspans;
break;
}
switch ($vform)
{
case 0: // open
$vverts = $nv;
break;
case 1: // closed
case 2: // periodic
$vverts = $vspans;
break;
}
$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;
switch($style)
{
case "zero":
for($i=0;$i<$dim[0]*$dim[1];$i++)
{
$val[$i] = 0;
}
break;
case "one":
for($i=0;$i<$dim[0]*$dim[1];$i++)
{
$val[$i] = 1;
}
break;
case "uramp":
$iv = 0;
for($i=0;$i<$dim[0];$i++)
{
for($j=0;$j<$dim[1];$j++)
{
$val[$iv] = $i / (float) ($dim[0] - 1);
$iv++;
}
}
break;
case "vramp":
$iv = 0;
for($i=0;$i<$dim[0];$i++)
{
for($j=0;$j<$dim[1];$j++)
{
$val[$iv] = $j / (float) ($dim[1] - 1);
$iv++;
}
}
break;
}
}
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);
}
else
{
// 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
|