Adding a Pass Class

Adding a Pass Class

Adding a Pass Class to RfM

Sooner or later you might find yourself with an uncontrollable urge to ask the RfM to do something that it hasn't done before. Quite possibly, this will mean creating a new type of pass. This is something that needs to be done by RfM developers regularly, as new features added to the renderer require corrolating workflows in RfM; per-face texture (Ptex) support is a perfect example and, enumerated below, serves as a tidy little case study...


Some Background

Passes in RfM come in two varieties: render passes and command passes. Render passes produce data, like images or point clouds. Sometimes this data is intermediary and needs to be further processed by a command, like ptfilter, or in our particular example, ptxmake, in order to be made useable by subsequent passes. The simplest sort of pass consists of just one task; it may just render or just perform a conversion. The pass you're probably most familiar with is a Final rendering pass, which produces the final image that you're after. Final is treated like a proper name there in the last sentence, because that's the official name of a kind of pass.

When RfM is operating, each pass it deals with is an instance of a "class" of pass. So, when you extend the pass system, you're really defining a new pass class, which can then be instantiated, tweaked, and used by RfM users. A Final pass uses shaders that may refer to data that needed to be generated beforehand. A simple example of one such pass is a shadow map, which just renders a map. That map gets used later on by other passes. The shadow pass just has one step. A txmake pass is an example of a simple command pass that takes an input image file and converts it into a Pixar texture. Somtimes what is refered to as a pass is really a short sequence of passes. For example, a subsurface scattering pass needs to render to produce a point cloud, and then the point cloud needs to be processed by ptfilter. The result of these steps is a point cloud that can be used by another pass, like a Final rendering pass or maybe before that, by a global illumination pass.


Creating a Ptex Pass

The thing we want to generate with a Ptex pass is a Ptex-formatted file. The Ptex file is a form of baked data that will use less memory than an organized point cloud when referenced by shaders in subsequent render passes. To generate the Ptex file, we want to first render a point cloud and then convert that into a Ptex file using the ptxmake utility. So, our Ptex pass will involve two steps: a render pass followed by a command pass.

Good news, we already have a kind of render pass that's capable of generating the sort of point cloud needed by our ptxmake command. It's called an SBRender pass. So, we only need to define one new pass class for Ptex, let's call it SBMakePtex (by the way, SB is short for "Slim bake"). Our new pass will require an SBRender pass to execute, and then process the resulting point cloud using a ptxmake command. It'd also be nice if arguments to the ptxmake command would be adjustable via controls in the UI, rather than having our SBMakePtex use a hardcoded ptxmake call.

RfM defines all of its pass classes in ".rman" files that begin with node_*, and are stored in $RMSTREE/lib/rfm. These are broken up into broad categories just for clarity, like nodes_shadow.rman, nodes_subsurf.rman, etc. The SBRender pass that we'll make use of is defined in nodes_slimbake.rman. These .rman files all contain pass descriptions in a format based on the Tcl language.

nodes_slimbake.rman

Let's start with the definition of the new SBMakePtex pass and then look at it in detail below:

NodeType pass:command:SBMakePtex {
    reference Collection RequiredPassSettings
    reference NodeType {pass:render:SBRender}
    torattr phase {
        subtype selector
        range {
            "Once Per Job" {/Job/Preflight/Maps/SlimBake}
            "Every Frame" {/Job/Frames/Maps/SlimBake}
        }
        default {/Job/Frames/Maps/SlimBake}
    }
    # our job is to run ptxmake -filter none input output
    param ptxmake:depth {
        default half
    }
    param ptxmake:splat {
        default diffusion
    }
    param ptxmake:geom {
        default quad
    }
    param ptxmake:__inputfile {
        default {[passinfo this/0 filename]}
        uistate hidden
    }
    param ptxmake:__channel {
        default {$BAKECHAN}
        uistate hidden
    }
    param ptxmake:__outputfile {
        default {[passinfo this filename]}
        uistate hidden
    }
    torattr passCommand {
        default {[GetCmdPassCmdList \"\\${RMANTREE}/bin/ptxmake\"]}
    }
}

The first line indicates that we're defining a command pass, of class SBMakePtex. In the case of the SBRender pass, the word command is replaced with render, like "NodeType pass:render:SBRender".

The first line in the body of the pass definition, "reference Collection RequiredPassSettings", is just a shorthand way of including settings that might be common to several kinds of passes, so that we don't have to duplicate them, and so that it's easy to make general changes in one central place.

The next line, "reference NodeType {pass:render:SBRender}" indicates that our new pass must always be preceeded by an instance of an SBRenderPass, which we happen to know will produce the required point cloud.

Next, there are a series of pass settings. Each setting has a default value, and it is also possible to override any of the attributes from the definition of the setting, like the label, subtype, etc. The declarations of our new ptxmake settings are described in the next section.

The "phase" is a setting that is necessary for all passes, and indicates where in the "JobPhaseTree" the pass should reside. The JobPhaseTree defines general ordering of passes. For example, shadows can all occur together as a group (in parallel, if possible) that needs to preceed subsurface passes, etc. The phase setting has UI that allows the user to choose whether the pass should occur once per job or once per frame.

Next, we see a series of "param ptxmake:*" settings. The namespace "ptxmake" refers to the command that we'll execute, and is how we indicate that these params are all arguments to the ptxmake command. RfM will take care of turning these into an arg list to pass to the command.

Some of the param names begin with underscores, like "ptxmake:__inputfile", and "ptxmake:__outputfile". These are special keywords that indicate that these parameterss will be tacked on to the end of the arg list, without arg flags. The value "[passinfo this/0 filename]" is a Tcl expression that evaluates to the name of the output of the preceeding pass. And "[passinfo this filename]" evaluates to the name of the output of the current pass.

Finally, the passCommand is the setting that holds the actual ptxmake command. In this case, the value is a Tcl expression. This may seem a bit mysterious, but the idea is that by evaluating the expression we can produce an arbitrary list of ptxmake commands, depending on how many point cloud files were produced by the SBRender pass. The point clouds need to be converted one at a time. The Tcl command "GetCmdPassCmdList" is defined in RfMPassExpressions.tcl.

Since this pass description is used to define the interface that you will see in Maya, it is important to remember that these parameters are created as attributes on a Maya node. If you change this file and then re-open the Maya file you will find that the defaults and the parameters are the same as they were prior to editing the file, because they were burned into the Maya node. To see the new changes, be sure to delete the old node, and create a new pass class node.

decl_cmdparams.rman

decl_cmdparams.rman is an existing file that we edit to define the parameters referred to in the pass definiton. Since we are adding a ptxmake pass class, we need to declare the appropriate new parameters. These are added via Tcl commands that are evaluated to define the parameters.

The declaration is formatted like so:

Declare param <arg1> <arg2>

Where arg1 defines the type (string, float, int, or color) and the name, often with a namespace qualifier, such as ptxmake:<parameterName>. arg2 is a newline seperated list of commands:

label {label}
subtype {slider, selector, ifselector(imagefile),
    connection, file,
    masterswitch(don't know what this does),
    flag}
description {popup info for parameter}
range {start end increment}

The syntax for range above is for numbers; range can also be set for selectors:

range { label value
        label value}

or for files:

range {*.extension}

The example below shows how we handled the declaration for our test case.

# ptxmake controls ---------------------------------------------------------
Declare param {string ptxmake:__inputfile} {
    label "Input File"
    subtype file
}
Declare param {string ptxmake:newer} {
    label "Reuse"
    subtype flag
    description {
        If output file already exists, only perform ptxmake if input file
        is newer.
    }
}
Declare param {string ptxmake:depth} {
    label "Depth"
    subtype selector
    range {
        "byte" {byte}
        "short" {short}
        "half" {half}
        "float" {float}
    }
}
Declare param {string ptxmake:splat} {
    label "Splat"
    subtype selector
    range {
        "none" {none}
        "diffusion" {diffusion}
        "smooth" {smooth}
        "area" {area}
    }
}
Declare param {string ptxmake:geom} {
    label "Geom"
    subtype selector
    range {
        "quad" {quad}
        "tri"   {tri}
    }
}
Declare param {string ptxmake:__inputfile} {
    label "Point Cloud File"
}
Declare param {string ptxmake:__channel} {
    label "Channel"
}
Declare param {string ptxmake:__outputfile} {
    label "Output File"
}
  • Note

    Since this is Tcl, make sure that you have a space between your parameter name and the '{' that starts the rest of the arguments, i.e. Declare param {int foo:bar}{ will cause the parameter to not load, because }{ is a syntax error.

    Remember that in Tcl "" and {} mean almost the same thing, except inside of "" expressions will be evaluated, and inside {} they won't, so "byte" {byte} is the same as byte byte or {byte} {byte} or "byte" "byte". However, the "" and {} will group words as a single element, so

    range {
           A B A B
    }
    

    is wrong but

    range {
           "A B" "A B"
    }
    

    will work.


RenderMan_for_Maya.ini

Your new pass type needs to be added to the "pass ref validity table" in your site-sepecific RenderMan_for_Maya.ini, to tell RfM which pass references are valid. For example, when rendering a deep shadow pass, you may have a shader with a parameter that references a subsurface scattering pass, but since the subsurface pass hasn't run yet, you don't want to allow the reference to the subsurface pass to be emitted into the shadow pass. Each entry in the table consists of one pass class followed by a list of pass classes in which that pass class is valid.

In the example below we have defined SBMakePtex as a valid pass type that exists downstream of SBRender and SBPtRender:

SBRender {SBRender SBPtRender SBMakePtCloud SBMakePtex SBMakeBrickmap}
SBPtRender {SBRender SBPtRender SBMakePtCloud SBMakePtex SBMakeBrickmap \
                Final BakeRender ReferenceRender}
  • Important

    As always, we highly recommend that you do not edit any .ini files in your installation directory ($RMSTREE/etc). Instead, create a duplicate file containing your site-specific overrides and place it in a separate directory, referenced via an $RMS_SCRIPT_PATHS environment variable.

SlimClientExpression.tcl

In our test case, since we are adding ptcToPtex to the bake node in Slim, we also need to register it as a possible bake pass class in SlimClientExpression.tcl. This is done like so:

ptcToPtex {
           set passclass SBMakePtex
}

This code serves to tell RfM what to do when ptcToPtex is passed to the pass generation code.


Summary

To summarize, adding the SBMakePtex pass class requires that you create and/or edit these files:

Because our example interacts with Slim as well, we also needed to edit SlimClientExpression.tcl.