View Full Version : RiCurves and other Renderman Questions

05 May 2009, 10:10 AM
Hi folks,

i want to make a tree (joschua pine tree) within Renderman, like the ones...seen in the movie "open season"

so far so good... :bounce:

i made a rib file for the tree which has arround 680 kb :beer:

so far so good...then i started modelling the neadles, which look like this:

the pine neadle is a construct of a mel script, which i wrote to build the neadle on a single click, it has 25 to 150 "leaf" triangels itself combined to a single neadle object. rendertime was arround 0.01 seconds for a single 150 object

then i scattered the neadle objects to achieve the tree look as u see on the first picture
i ended up with arround 56.000 neadle objects on one tree made a rib *LOL* and got a
rib at about 620 mb :scream: and a rendertime with ambient oCC turned on of 5h:44min :argh: with the tree (trunk, twigs) at shading rate 1 and the neadles at shading rate 100...

well to decrease the rendertime... i want to build the neadle objects with RiCurves... :curious:

does anyone knows how i could start this task? :curious: i found a script on highend 3d from stabacco which seems to be the right way...but i've yet no clue where to start...

here's the mel script source code:

BESIDE: why the hell RfM does not save any displacement attributes withhin the Maya file? or u always need to reload these damn shaders? :banghead:

thanks for your help ;)

/* This file downloaded from
'' File Information:
'' Script Name: ST_hairGenerator v2.1
'' Author: Stefano Tabacco
'' Last Updated: October 4, 2003
'' Update/Change this file at:
'' Please do not alter any information above this line
'' it is generated dynamically by and will
'' be changed automatically on any updates.

//martedi 3 Giugno 2003
//Mel HAir RibGenerator by Stefano Tabacco (

//note :you must create a directory called "hair" in your "project/rib/" directory

string $shaderPath = ( `workspace -q -rd` +"rmanshader/");
string $RIBPath =( `workspace -q -rd` +"rib/hair/");

global proc getShaderFile(){

string $shaderPath = ( `workspace -q -rd` );
setWorkingDirectory $shaderPath "mayaBinary" "scene";
fileBrowserDialog -m 0 -fc "getShaderFileName" -ft "*.slo" -an "Hair_Shader";

global proc getShaderFileName(string $name, string $type) {

textFieldGrp -e -fi $name shaderNameField;


global proc getRIBFile(){

string $shaderPath = ( `workspace -q -rd` );
setWorkingDirectory $shaderPath "mayaBinary" "scene";
fileBrowserDialog -m 0 -fc "getRIBFileName" -ft "RIB" -an "Select_RIB_File";

global proc getRIBFileName(string $name, string $type) {

textFieldGrp -e -fi $name RIBNameField;


global proc writeRibHeader() //clean RIB File

//string $dirPath = ( `workspace -q -act` + "/rib/hair/" );
string $hairFileName = (`textFieldGrp -q -text RIBNameField`);
//prevent from accidentally erase unwaned files
string $buffer[] ;
int $numTokens = `tokenize $hairFileName "\\" $buffer` ;

string $thisBuf;
int $count =0;
for ($thisBuf in $buffer) {
if ($thisBuf=="rib")
$count++ ;
if ($thisBuf=="hair")
$count++ ;
if ($count !=2 )
error " I won't let you erase any file in a directory different from ' PROJ/rib/hair/ ' ";
//prevent from accidentally erase unwanted files

$fileIdw=`fopen $hairFileName "w"`;
fprint $fileIdw ("");
fclose $fileIdw;
print ("RIB file "+ $hairFileName +" erased");


global proc addST_HairAttribute ( string $sel[],
string $hairNumVal,
string $hairWidthValue,
string $randVal,
string $randTip)


string $thisSel;
float $multiplier = `floatSliderGrp -q -v $hairNumVal`;
float $larg= `floatSliderGrp -q -v $hairWidthValue`;
float $RANDFACTOR= `floatSliderGrp -q -v $randVal`;
float $RANDTIP= `floatSliderGrp -q -v $randTip`;

for ($thisSel in $sel) {

string $thisSrf[] =listRelatives($thisSel) ;
int $isNurbs=`objectType -i "nurbsSurface" $thisSrf[0]`; //works only On Nurbs Patches
string $attrYes[]=`listAttr -st "ST_density" $thisSrf[0]` ; //works only On Nurbs Patches
if ( $isNurbs != 1 ) //works only On Nurbs Patches
warning ("it will only work on NURBS Surfaces ") ; //works only On Nurbs Patches
else {

int $howManyPoints = `getAttr ( $thisSel+".spansV" ) ` +3 ;
float $distance = 0 ;
for ($i=1 ;$i<$howManyPoints;$i++) {
int $j=$i-1 ;
float $posI[] =`pointPosition -w ( $thisSel + ".cv[0][" + $i + "]" )` ;
float $posJ[] =`pointPosition -w ( $thisSel + ".cv[0][" + $j + "]" )` ;

$distance += abs($posI[0]-$posJ[0])+

int $hairNum = $distance * 100;
int $randTemp=rand(0,10);

if ($attrYes[0]=="") {
addAttr -ln ST_crvNum -at long $thisSrf[0];
setAttr -e -keyable false ($thisSrf[0]+".ST_crvNum");
addAttr -ln ST_density -at double $thisSrf[0];
setAttr -e -keyable true ($thisSrf[0]+".ST_density");
addAttr -ln ST_xpctdNum -at long $thisSrf[0];
setAttr -e -keyable true ($thisSrf[0]+".ST_xpctdNum");
addAttr -ln ST_hairWidth -at double $thisSrf[0];
setAttr -e -keyable true ($thisSrf[0]+".ST_hairWidth");
addAttr -ln ST_rndmBase -at double $thisSrf[0];
setAttr -e -keyable true ($thisSrf[0]+".ST_rndmBase");
addAttr -ln ST_rndmTip -at double $thisSrf[0];
setAttr -e -keyable true ($thisSrf[0]+".ST_rndmTip");
addAttr -ln ST_rndmSeed -at long $thisSrf[0];
setAttr -e -keyable false ($thisSrf[0]+".ST_rndmSeed");
setAttr ($thisSrf[0]+".ST_hairWidth") $larg ;
setAttr ($thisSrf[0]+".ST_xpctdNum")$hairNum ;
setAttr ($thisSrf[0]+".ST_density") $multiplier;
setAttr ($thisSrf[0]+".ST_rndmBase") $RANDFACTOR;
setAttr ($thisSrf[0]+".ST_rndmTip") $RANDTIP;
expression -s ($thisSrf[0]+ ".ST_crvNum = " +$thisSrf[0]+ ".ST_xpctdNum*" +$thisSrf[0]+ ".ST_density" ) -o $thisSrf[0] -ae 1 -uc all ;
setAttr ($thisSrf[0]+".ST_rndmSeed") $randTemp ;// a random seed for every surface


global proc overShad()

string $sel[] =listRelatives(`ls -sl -tr `);
string $thisSurf;

for ($thisSurf in $sel) {
setAttr ($thisSurf +".overrideEnabled") 1;
int $status= `getAttr ($thisSurf+".overrideShading") `;
if ($status==0)
setAttr ($thisSurf +".overrideShading") 1;
else setAttr ($thisSurf +".overrideShading") 0;

global proc attrSurf(int $onOff)//RENDER SURF

string $sel[] =listRelatives(`ls -sl -tr `);
string $thisSurf;

for ($thisSurf in $sel) {
setAttr ($thisSurf+".castsShadows") $onOff;
setAttr ($thisSurf+".receiveShadows") $onOff;
setAttr ($thisSurf+".primaryVisibility") $onOff;

//RIB generating code

global proc creaCioccaMEL (string $sel[])


string $thisSurf;
for ($thisSurf in $sel) {
string $Shape[]=listRelatives($thisSurf);
if (`objExists ($Shape[0]+".ST_crvNum")`==0)
error "some of the selected surfaces does not have ST_hair attributes ";
float $uRange[2] =`getAttr ($Shape[0]+".minMaxRangeU")` ;
float $vRange[2] =`getAttr ($Shape[0]+".minMaxRangeV")` ;
if ($vRange[0]+$vRange[1]+$uRange[0]+$vRange[1]!=2)
error ("surfaces must be rebuilt with the 'Parameter range =0-1'");


print "\n";
string $hairFileName = (`textFieldGrp -q -fi RIBNameField`);

string $shaderFileName =(`textFieldGrp -q -fi shaderNameField`);
$shaderFileName = `substitute ".slo" $shaderFileName ""` ;

waitCursor -state on;

$fileIdw=`fopen $hairFileName "a"`;
string $ribHeader ="AttributeBegin\n TransformBegin\n Surface \""+ $shaderFileName +"\" \n TransformEnd\n Attribute \"identifier\" \"name\" [\"cur\"]\n ConcatTransform [1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1]\nShadingInterpolation \"smooth\"\nBasis \"b-spline\" 1 \"b-spline\" 1 \n" ;

string $thisSurf;
for ($thisSurf in $sel) {

string $Shape[]=listRelatives($thisSurf);
int $hairNumValue = `getAttr ($Shape[0]+".ST_crvNum")` ; //a partire dagli attributi della shape crea le curve
float $larg = `getAttr ($Shape[0]+".ST_hairWidth") `; //a partire dagli attributi della shape crea le curve
float $RANDFACTOR = `getAttr ($Shape[0]+".ST_rndmBase") `; //a partire dagli attributi della shape crea le curve
float $RANDTIP = `getAttr ($Shape[0]+".ST_rndmTip") `; //a partire dagli attributi della shape crea le curve
int $rseed= `getAttr ($Shape[0]+".ST_rndmSeed") `;

seed ($rseed) ;
fprint $fileIdw $ribHeader ;
fprint $fileIdw ("Curves \"cubic\" [\n") ;
float $uu,$vv=0;
int $howManyPoints =`getAttr ($thisSurf+".spansU")` + 3;
for ($vv=0;$vv<$hairNumValue;$vv++) {
fprint $fileIdw ( ($howManyPoints+4) +" " ) ;
fprint $fileIdw ("\n] \"nonperiodic\" \"P\" [");
for ($vv=0;$vv<$hairNumValue;$vv++) {

float $vNorm=$vv/$hairNumValue; //****************added
float $pos[]=`pointPosition -w ( $thisSurf + ".uv[ 0 ][" +$vNorm+ "]")`;
$pos[0]=trunc(($pos[0]+ gauss($RANDFACTOR))*10000)/10000 ;
$pos[1]=trunc(($pos[1]+ gauss($RANDFACTOR))*10000)/10000 ;
$pos[2]=trunc(($pos[2]+ gauss($RANDFACTOR))*10000)/10000 ;
fprint $fileIdw ( $pos[0]+" "+$pos[1]+" "+$pos[2]+" ");
fprint $fileIdw ( $pos[0]+" "+$pos[1]+" "+$pos[2]+" ");
fprint $fileIdw ( $pos[0]+" "+$pos[1]+" "+$pos[2]+" ");
for ($uu=1;$uu<$howManyPoints-1;$uu++) {

float $uNorm=$uu/$howManyPoints;
float $pos[]=`pointPosition -w ( $thisSurf + ".uv[" +$uNorm+ "][" +$vNorm+ "]")`;
float $rndm= $uNorm*($RANDTIP-$RANDFACTOR);
$pos[0]=trunc( ($pos[0] + gauss($rndm) )*10000)/10000 ;
$pos[2]=trunc( ($pos[2] + gauss($rndm) )*10000)/10000 ;
$pos[1]=trunc( ($pos[1] + gauss($rndm) )*10000)/10000 ;

fprint $fileIdw ( $pos[0]+" "+$pos[1]+" "+$pos[2]+" ");


float $pos[]=`pointPosition -w ( $thisSurf + ".uv["+($howManyPoints-1)+"][" +$vNorm+ "]")`;
$pos[0]=trunc(($pos[0] + gauss($RANDTIP) )*10000)/10000 ;
$pos[1]=trunc(($pos[1] + gauss($RANDTIP) )*10000)/10000 ;
$pos[2]=trunc(($pos[2] + gauss($RANDTIP) )*10000)/10000 ;

fprint $fileIdw ( $pos[0]+" "+$pos[1]+" "+$pos[2]+" ");
fprint $fileIdw ( $pos[0]+" "+$pos[1]+" "+$pos[2]+" ");
fprint $fileIdw ( $pos[0]+" "+$pos[1]+" "+$pos[2]+" "); //l'ultimo punto si scrive 3 volte come il primo
fprint $fileIdw(" ]\"constantwidth\" [");
fprint $fileIdw( $larg +"] AttributeEnd ");

fclose $fileIdw;

waitCursor -state off;

print ("ReadArchive \""+$hairFileName+"\"");

if ((`window -ex ST_HairWin`) == true) deleteUI ST_HairWin;
window -t " MEL Hair Generator (" -w 400 -h 600
-s true ST_HairWin;

string $form = `formLayout`;
string $tabs= `tabLayout -imw 15 -imh 15`;

formLayout -edit
-attachForm $tabs "top" 0
-attachForm $tabs "left" 0
-attachForm $tabs "bottom" 0
-attachForm $tabs "right" 0

string $child1 = `rowColumnLayout -numberOfRows 9`;
string $hairNumVal=`floatSliderGrp -label "Hair Number Multiplier" -field true
-minValue 1 -maxValue 10
-fieldMinValue 1 -fieldMaxValue 10
-pre 2
-value 1 `;

string $hairWidthVal=` floatSliderGrp -label "Hair Width" -field true
-minValue 0 -maxValue 1
-fieldMinValue .0001 -fieldMaxValue 1
-pre 3
-value .01 `;

string $randVal=` floatSliderGrp -label "base random Value" -field true
-minValue 0 -maxValue 3
-fieldMinValue 0 -fieldMaxValue 10
-pre 2
-value .1 `;

string $randTip=` floatSliderGrp -label "tip random Value" -field true
-minValue 0 -maxValue 3
-fieldMinValue 0 -fieldMaxValue 10
-pre 2
-value .3 `;


button -l "Add Hair Description to surfaces "
-w 400 -align "center"
-c "addST_HairAttribute(`ls -sl -tr`,$hairNumVal,$hairWidthVal,$randVal,$randTip)";

separator -height 20 -style "out";

checkBox -l "wireFrame view" -cc "overShad()" ;

checkBox -l "render surface" -onc "attrSurf(1)" -ofc "attrSurf(0)" -v 1 ;

setParent ..;

string $child2=`rowColumnLayout -numberOfRows 8 `;
button -align "center" -l "Clean RIB File " -c "writeRibHeader" ;
separator -height 10 -style "out";
string $Child2Row1 = `rowColumnLayout -numberOfColumns 3
-columnWidth 1 50
-columnWidth 2 300
-columnWidth 3 50 `;
text -l "ShaderFile";
textFieldGrp -fi $shaderPath -ed true -w 350 shaderNameField;
-enable true
-image "navButtonBrowse.xpm"
-c "getShaderFile" shaderFileBrowseButton;
setParent ..;
string $Child2Row1 = `rowColumnLayout -numberOfColumns 3
-columnWidth 1 50
-columnWidth 2 300
-columnWidth 3 50 `;
text -l "RIB File";
textFieldGrp -fi $RIBPath -ed true -w 350 RIBNameField ;
-enable true
-image "navButtonBrowse.xpm"
-c "getRIBFile" RIBNameFieldBrowseButton;
setParent ..;

separator -height 10 -style "in";
string $newForm = `rowLayout `;
rowLayout -edit
-h 120
-numberOfColumns 2
$newForm ;

button -w 260 -l " Generate hair RIB file " -c "creaCioccaMEL(`ls -sl -tr `)";
setParent .. ;
setParent ..;

string $child3=`rowColumnLayout -numberOfRows 4 `;

button -l "Render with RenderMan" -w 400 -h 60 -align "center" -c "mtor RenderSpool";
button -l "Show Palettes" -w 400 -h 60 -align "center" -c "slimmsg slim WindowCmd Show palettes";
setParent ..;
string $child4=`columnLayout -columnAlign "center" -w 400`;
text -align "center"
"Created by Stefano Tabacco in May/June 2003.\nDetails and a small tutorial at :\n\n\n\nFor any trouble/comment/suggestion please write me at my e-mail address:\n\\n__________________________________________\n\nHome Page :\n\n__________________________________________\n\n:)";
setParent ..;

setParent ..;
tabLayout -edit
-tabLabel $child1 "Surface Attributes " -tabLabel $child2 "RIB File Generation"
-tabLabel $child3 "RenderMan" -tabLabel $child4 "Credits"

showWindow ST_HairWin;

05 May 2009, 03:34 PM
Wow! Not a bad tree though. A large RIB is not too uncommon in the industry, I read some where during the new StarWars movies that ILM would often have 1 GB ribs for the droid armies. But that is ILM.....

Anyways, seems like what you want to do is procedural modeling of the leaves. I wouldn't begin to tell you HOW per se but I do have links... look for Stochastic Simplification of Aggregate Detail (

(you can also check out the rest of for info on that)

You have the right idea but in this case it would be better to have each clump as it's own rib file, instead of one large rib. That way RenderMan can just call on the next one when it needs it as opposed to rendering the entire leaf rib file at once. I cannot say that rendering will be instant but I am pretty sure your render times will decrease this way. Just to clarify it can work with either polygon or curves (hair) - for that matter anything that RenderMan can render can be a procedural model. With RiCurves you can simply use NURBS or Bezier curves, and again using procedural modeling assign objects as the leaf object rib - RMan will then call on the leaf rib (you can have several types for variety), and then render that bucket(s) till it is done, call on the next and so on and so forth. However by using AO, that will greatly increase rendering times no matter what method you use. You can always do fake AO using spotlight depth mapped shadows too though.

Hope that helps

05 May 2009, 11:21 PM
Regarding displacement attributes should be saved in the Maya file, which attributes are you talking about? The displacement bound and displacement space attributes should be saved into the file, if not then it's a bug.

I just tried this with RMS2/RfM3 and it works as expected for me.


05 May 2009, 05:36 PM
You should definitely use some form of delayed geometry reading. How many variations do you have? (apart from scale and rotation which can be added later). If you have just a few then a simple way of doing this is exporting the geometry as RIB archives. Make sure the geometry is centered at the origin and that the transforms are frozen.

You can then read the geometry in with a preShape MEL script which calls RiProcedural "DelayedReadArchive" (run RiProcedural -help for the actual arguments). This can be called from a MEL function so you can choose random archives, add random rotations, or pick up attributes from nodes etc.

You can make this faster by reducing traffic to disk by using inline archives. This doesn't need a lot of extra work. You need a FrameBegin MEL script which adds the lines to to RIB file:

ArchiveBegin "archivename"
##include "filename.rib"

If you keep the inline archive name the same as the name on disk then you do not need to change the ReadArchive calls. The double # is important as this dumps the RIB into the file during the render.

Otherwise if the needles are unique then you would want to use an RiProcedural "RunProgram" to generate the curves at runtime. This only gets expanded on bounding box entry - like Delayed ReadArchives. Fundza is definitely the best resource to work these out.

Whatever you do it is probably worth turning transmission off on the needles. A simple darkening towards the root would probably do fine, or bake the occlusion and read it in during the render. Baking to textures in Mental Ray will give you less headaches if going down this route. RenderMan deep shadows should help too - as already mentioned.


CGTalk Moderation
05 May 2009, 05:37 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.