Slim templates are the preferred way of creating new shading blocks to use inside Slim. Reasons:
In addition to the notes below, see the RAT documentation for more details on writing templates. Also, the <PRManDistribution>/rat/lib/slim directory contains a lot of slim templates for you to study and learn from.
Before you load anything, under Renderman -> Slim -> Preferences -> Expert, turn 'Expert Menus' on. You can also turn expert mode on using this MEL call:
slimcmd SetPref ExpertMenus 1
Here is a really simple but functional .slim template, called basic.slim. Assuming it is in C:/temp, you can use the following MEL command to load it into Slim [note - another way to load your templates is to specify them in a slim.ini configuration file - see the Slim docs for details].
// loads the nodes found in basic.slim - after executing this line of MEL, you will be able to // access these nodes under File -> Create Appearance -> Preloaded in any Slim palette slimcmd slim ReadSlimFile "C:/temp/basic.slim";There is also a third way to load .slim files. Do 'Renderman -> Slim -> Console' to get a Slim console window, and in it, type:
slim ReadSlimFile C:/temp/basic.slim
After you load the basic.slim file, create a 'Const' appearance (File -> Create Appearance -> Preloaded -> Const) contained in the file. Open up the 'Const' appearance just created, then do File -> View RSL Source. You can see the Slim-generated .sl RSL source for this template. When you render, it is this source that gets compiled (using shader.exe) into a .slo. In principle, you could have typed up this RSL by hand.. It looks fancier than a typical human-created .sl. Eg. instead of
surface Ceramic_1() {we see
/* defines ----------------------------------*/ #define SLIM_TYPEID_surface 0 #define SLIM_TYPEID_displacement 1 #define SLIM_TYPEID_volume 2 #define SLIM_TYPEID_light 3 #define SLIM_VERSION 350 #define SLIM_SHADERTYPE surface #define SLIM_SHADERTYPEID SLIM_TYPEID_surface #define SLIM_INSTANCETYPE shadingmodel #define SLIM_INSTANCENAME Ceramic_1 /* shader body ------------------------------*/ SLIM_SHADERTYPE SLIM_INSTANCENAME ( ) {Next, connect a 'Fractal' appearance to our Const's surface color ('Fractal' will feed a color to Const). Now view the RSL source again. You'll see that the code is now expanded to include Fractal's RSL too - Fractal's RSL gets called first (as a function), and its output color is in turn used as an input to Const. So what looks like arrow connections in the Slim palette are really RSL function calls that get chained.
The Const template in basic.slim can be simplified even more as follows:
### verybasic.slim - a minimal template definition ### the foll. template has a single input and two ouputs slim 1 extensions gnomonsr { extensions gnomon gnosl { template shadingmodel Const { description "A barebones 'constant.sl' simplification" parameter color SurfaceColor { label "Surface Color" detail varying default ".5 .5 .5" } parameter color CI { detail varying default ".5 .5 .5" access output display hidden } parameter color OI { detail varying default ".5 .5 .5" access output display hidden } RSLFunction { void gnoslConst( color SurfaceColor; output color CI; output color OI; ) { CI = SurfaceColor; OI = 1; } } } } }
slim 1 extensions gnomonsr { extensions gnomon gnosl {The word 'slim' is a keyword and is always needed. The '1' is a version number for the template (so you can have multiple versions of the same template in use). 'extensions' is also a keyword. The last keyword needs to be <Your_co_nameYour_initials>, so it is 'gnomonsr' in my case. Many Pixar templates contain 'pixardb' since Dana Batali wrote them.
In the second line, 'extensions' is again a keyword, and 'gnomon' is where your company name goes. 'gnosl' is a prefix that is prepended to the Slim-generated functions. You see 'pxsl' in all the Pixar files, I use 'gnosl' analogously. Note - in the function definition section (third section) in a template file, you need to prepend 'gnosl's equivalent to every function you write.
So, in summary, 'gnomonsr', 'gnomon' and 'gnosl' are keywords specific to me, and of those, I'd use 'gnosl' again in function definitions. Also, note the ' {' at the end of the lines. These open braces will get closed at the end of our template files - the last two lines in any template .slim file will therefore have to be
} }
Each template has a header followed by two components. The header looks like this:
template shadingmodel Const { description "Slim version of the constant.sl shader"The word 'template' is a keyword. This is followed by the template's type (what it computes - see the list above), and its name (which will appear in the Slim menu). The single open '{' will have to be closed at the end of the template definition.
The two components in a template are these:
parameter color SurfaceColor { label "Surface Color" detail varying default ".5 .5 .5" }
collection shadingmodel shadingmodel { access output display hidden parameter color CI { detail varying default ".5 .5 .5" access output } parameter color OI { detail varying default "1 1 1" access output } }
parameter float Kd { description "Multiplier for the diffuse light contribution" default .5 subtype slider range {0 1} detail varying }
parameter float Falloff { description "Normally light intensity diminished with distance. This control governs the degree to which this effect is in play. Linear falls off more than None, Squared more than Cubic, etc. For more precise control, try connecting the Kl or LightColor parameter to a spline whose pattern is a distance function." default 0 subtype selector range { None 0 Linear 1 Squared 2 Cubic 3 } }Note that params can be individually declared, or as a collection using the 'collection' keyword. The collection name and type (the two words that follow the word 'collection' need to be identical!). Also, notice the 'access output' specification - these will be our function's output params.
RSLFunction { void gnoslConst( color SurfaceColor; output color CI; output color OI; ) { CI = SurfaceColor; OI = color(1,1,1); } }
RSLMain { output "point from = point \"shader\"(0,0,0);" output "vector axis = normalize(vector \"shader\"(0,0,1));" output "uniform float angle = radians([getvar ConeAngle]);" output "uniform float penumbra = radians([getvar PenumbraAngle]);" output "uniform float cosoutside = cos(angle);" output "uniform float cosinside = cos(angle-penumbra);" output "illuminate(from, axis, angle) {" indent output "varying float atten, cosangle;" output "cosangle = L.axis / length(L);" output "atten = smoothstep( cosoutside, cosinside, cosangle );" output "atten *= 1 / pow(length(L), [getvar Falloff]);" generate if [connected Shadow] { output "__inShadow = [getvar Shadow];" } output "Cl = atten * color([getvar Intensity]);" output "Cl = mix(Cl, color(0), __inShadow);" exdent output "}" }
This is an example of a surface ("shadingmodel") template that creates starburst highlights. This scene illustrates it use. The jewel.slim file highlights another feature - an RSLFunction definition can contain nested function defs.
RSLFunction { void gnoslJewel( color SurfaceColor; color SurfaceOpacity; color Incandescence; float Ka; float Kd; float Ks; float Metalness; float Roughness; color SpecularColor; output color CI; output color OI; ) { color jewel(point pp; normal nf; vector v; float roughness;) { uniform float lt; vector H; float factor1, factor2; float rough = 1.0 / roughness; extern vector L; color csap = color(0,0,0); illuminance(pp, nf, 1.57079632679489661923) { if(L != vector(0) && 0 == lightsource("__nondiffuse", lt) && 0 == lightsource("__nonspecular", lt) ) { H = normalize(normalize(L)+v); factor1 = H.normalize(nf ^ normalize(Du(pp))); factor1 = pow(1.-abs(factor1),20); /* now do the cross direction */ factor2 = H.normalize(nf ^ normalize(Dv(pp))); factor2 = pow(1.-abs(factor2),20); csap += (factor1+factor2) * pow(max(0., nf.H), rough); } } return csap; } color cSpec; normal Nf, Nn; vector V; extern vector I; extern normal N; extern point P; Nn = normalize(N); Nf = faceforward( Nn, I, Nn); V = -normalize(I); cSpec = mix(SpecularColor, SurfaceColor, Metalness); OI = SurfaceOpacity; CI = SurfaceOpacity * ( Incandescence + SurfaceColor * (Ka*ambient() + Kd*diffuse(Nf)) + cSpec * Ks * jewel(P, Nf, V, Roughness) ); } }In the above, jewel() is a nested function that gets used in the last line of the enclosing gnoslJewel() definition.
Commonly used helper functions can also be placed in a separate file and be #included before the RSLFunction block. The syntax looks like this:
RSLInclude { "/path/to/your/file/usefulDefns.h" } RSLFunction { ...
This is an example of a float template. You can hook it up to a 'float' parameter in another appearance (eg. the above jewel shader's 'Kd' parameter).
The following contains both a color template (which would feed into a surface shader) and a light template which are meant to work together (a surface with a 'phospor' color input will only respond to a 'blacklight' light source).
// Call the following 'blacklight.slim', and load it using the foll. MEL: // slimcmd slim ReadSlimFile full_path_to_blacklight.slim ## ## Copyright (c) 1999 PIXAR. All rights reserved. This program or ## documentation contains proprietary confidential information and trade ## secrets of PIXAR. Reverse engineering of object code is prohibited. ## Use of copyright notice is precautionary and does not imply ## publication. ## ## RESTRICTED RIGHTS NOTICE ## ## Use, duplication, or disclosure by the Government is subject to the ## following restrictions: For civilian agencies, subparagraphs (a) through ## (d) of the Commercial Computer Software--Restricted Rights clause at ## 52.227-19 of the FAR; and, for units of the Department of Defense, DoD ## Supplement to the FAR, clause 52.227-7013 (c)(1)(ii), Rights in ## Technical Data and Computer Software. ## ## Pixar ## 1001 West Cutting Blvd. ## Richmond, CA 94804 ## ## ---------------------------------------------------------------------------- # # blacklight.slim # a poor man's blacklight system. Combine the phosphor shader with a # base shading model via the AdditiveFX node. Now, shine a blacklight # into your scene... (groovy, man). # # $Revision: #1 $ # slim 1 extensions pixardb { extensions pixar pxsl { template color Phosphor { description {A poor man's black light poster. This shader collects black light from your scene and adds phosphorescence.} parameter color phosphors { description {The color of the phosphors.} default {.8 1 .8} detail varying; # means we can connect a color function here } parameter color fill { description {The fill color to use when blacklights are detected.} default {.1 0 .2} } parameter color output { access output display hidden } RSLFunction { void pxslPhosphor(color phosphors; color fill; output color result;) { extern point P; /* reference to RenderMan global ok for shading */ extern color Cl; result = color 0; /* look for all blacklights in the scene */ illuminance ("blacklight", P) { result += Cl; } result = result * (phosphors + fill); } } } template light blacklight { description {A poor man's blacklight. Emits light that only visible to surfaces with Phosphor. Attach this to a spotlight in your scene.} lighttype spot parameter float Intensity { detail varying default 1 } parameter float ConeAngle { description {The cone angle in of the spotlight, measured in degrees. You can cause this cone angle to match that of your modeler with an expression like: $CONEANGLE} subtype slider default 30 range {1 180 1} } parameter float PenumbraAngle { description {The softly lit area at the edge of the spotlight, measured in degrees. You can cause this angle to match that of your modeler with an expression like: $PENUMBRAANGLE} default 5 subtype slider range {0 180 1} } parameter float Falloff { description "Normally light intensity diminished with distance. This control governs the degree to which this effect is in play. Linear falls off more than None, Squared more than Cubic, etc. For more precise control, try connecting the Kl or LightColor parameter to a spline whose pattern is a distance function." default 0 subtype selector range { None 0 Linear 1 Squared 2 Cubic 3 } } parameter float Shadow { description "Connect a shadow function here." default 1 detail varying } parameter string __category { access output provider primitive detail uniform display hidden default blacklight } parameter float __nondiffuse { access output provider primitive detail uniform display hidden default 1 } parameter float __nonspecular { access output provider primitive detail uniform display hidden default 1 } parameter float __inShadow { access output display hidden detail varying provider variable default 0 } RSLMain { output "point from = point \"shader\"(0,0,0);" output "vector axis = normalize(vector \"shader\"(0,0,1));" output "uniform float angle = radians([getvar ConeAngle]);" output "uniform float penumbra = radians([getvar PenumbraAngle]);" output "uniform float cosoutside = cos(angle);" output "uniform float cosinside = cos(angle-penumbra);" output "illuminate(from, axis, angle) {" indent output "varying float atten, cosangle;" output "cosangle = L.axis / length(L);" output "atten = smoothstep( cosoutside, cosinside, cosangle );" output "atten *= 1 / pow(length(L), [getvar Falloff]);" generate if [connected Shadow] { output "__inShadow = [getvar Shadow];" } output "Cl = atten * color([getvar Intensity]);" output "Cl = mix(Cl, color(0), __inShadow);" exdent output "}" } } } }Here again is the .slim template and here is a sample Maya scene to test it with.
parameter float Displacement { detail mustvary default 0 }The 'Displacement' float parameter (which will be used as a displacement amount) *has* to come from another 'float' template, eg. noise. The 'Displacement' parameter does not come with a preset float template hooked up to it, the user needs to supply one.
In the 'Warp' manifold template is this block of code:
template manifold Warp { description "Returns points additively warped by vector-value Unless you choose to map the Surface Point parameter, the default behavior is to use ST coordinates." parameter vector warper { label "Warp Function" detail mustvary "pixar,VBrownian" default 0 }Here, the 'warper' vector-valued parameter will be supplied by a vector-valued template. But unlike the 'Displacement' above, here a default vector template is hooked up to get the user started. That is done by appending a template name (of the proper type) right next to 'detail mustvary'. In our example it is the string "pixar,VBrownian".
This way of being able to use pre-existing templates is very cool, since you can use existing vector providers, color providers etc. inside your own template definitions.
Here's a .slim file by ZJ, who's very active in the RMan/MEL community. It contains a bunch of GI-related templates.
This is an autogenerated .slim template from Malcolm Kesson's Cutter.
All of the Slim functionality is available via MEL commands slimcmd and mtor. These two keywords contain dozens of subcommands dealing with all aspects of MTOR and Slim. See the documentation for details. For a quick example, try:
// Do Slim operations without accessing the RenderMan menu! global proc slimCmdsSampler() { file -f -new; setAttr "persp.tx" 4.7; setAttr "persp.ty" 3.5; setAttr "persp.tz" 4.7; setAttr "persp.ry" 45.0; cylinder; directionalLight; string $newPal = `slimcmd slim CreatePalette -new -edit`; slimcmd $newPal SetLabel "FunPalette"; // slimcmd slim ReadSlimFile "/soft/apps/rat/4.5.1m40/lib/slim/shadingmodels.slim"; string $shdrHandle = `slimcmd $newPal CreateInstance -template "pixar,Constant"`; $shdrID = `slimcmd $shdrHandle GetID`; select -ne -replace nurbsCylinderShape1; mtor control attach "surface" $shdrID; mtor control renderspool; }// slimCmdsSampler()
It is often advantageous to call a separate program from a RIB file, to create specialized geometry, etc. This can be done as a DSO (shared object) or as a standalone program. Here's a small standalone program. Note that the program waits in an infinite loop till it receives a line of input containing (at least) a 'detail' parameter and additional params it needs. This input, when received, is used to emit RIB (to stdout). After this the program HAS to print a \377 character as a signal to the caller (PRMan, in our case) that it has completed its RIB outputting.
/* Call the following genRegPoly.c, and ompile it using: gcc -o genRegPoly genRegPoly.c -lm */ #include <stdio.h> #include <stdlib.h> #include <math.h> void doPolyGen(int nSides) { int i; float angStep, currAng; printf("Polygon \"P\" ["); /* printf("Sphere 1 -1 1 360\n"); */ if (nSides<3)nSides=3; angStep= 2.0*M_PI/nSides; for(i=0;i < nSides;i++) { float x,y,z; currAng = 0.5*M_PI + i*angStep; x = cos(currAng); y = sin(currAng); z = 0.0; printf("%f %f %f ",x,y,z); } printf("]\n"); }/* doPolyGen() */ int main() { void doPolyGen(int nSides); char buffer[256]; double detail; int nSides; while(fgets(buffer,256,stdin)) { sscanf(buffer,"%f %d",&detail,&nSides); doPolyGen(nSides); fprintf(stdout,"%c", '\377'); fflush(stdout); } }/* main() */
The compiled 'genRegPoly' program can be invoked (by PRMan) using the following sample RIB:
Display "Procedural.tiff" "framebuffer" "rgb" Imager "background" "color" [0.1 0.15 0.3] Format 1000 1000 -1 ScreenWindow -2 2 -2 2 PixelSamples 3 3 WorldBegin Translate 0 0 1 TransformBegin Translate -1 1 0 Scale 0.9 0.9 0.9 Color [1 0 0] Procedural "RunProgram" ["genRegPoly" "3"] [-1 1 -1 1 -1 1] TransformEnd TransformBegin Translate 1 1 0 Scale 0.9 0.9 0.9 Color [0 1 0] Procedural "RunProgram" ["genRegPoly" "4"] [-1 1 -1 1 -1 1] TransformEnd TransformBegin Translate -1 -1 0 Scale 0.9 0.9 0.9 Color [0 0 1] Procedural "RunProgram" ["genRegPoly" "5"] [-1 1 -1 1 -1 1] TransformEnd TransformBegin Translate 1 -1 0 Scale 0.9 0.9 0.9 Color [1 1 0] Procedural "RunProgram" ["genRegPoly" "6"] [-1 1 -1 1 -1 1] TransformEnd WorldEndYou can test 'genRegPoly' yourself (without a RIB file) like so: