Writing パターン

Writing パターン

Pattern Plugins

RiPattern is used to connect textures and procedurally generated patterns to RiBxdf parameters. The pattern behavior is controlled through a plugin written in C++. For example, a pattern plugin can be written to generate a fractal or noise pattern, or it can read a new texture file format and pass the output to Bxdfs. There are numerous pattern plugins included with RenderMan Pro Server and RenderMan for Maya, but if none of the included plugins generate the pattern you want, then this guide will help you write your own pattern plugin.

For pattern generation using the Open シェーディング Language (OSL), see the PxrOSL plugin documentation on Working with PxrOSL.

Source code for pattern plugin examples can be found in the /lib/examples/RIS/plugins/pattern/ directory of your RenderMan Pro Server installation.


Implementing the RixPattern Interface

RixPattern.h defines the interface that all pattern plugins must implement. To start developing your own pattern, you can include RixPattern.h and make sure your pattern class implements the four methods from the RixPattern interface: Init, GetParamTable, Finalize, and ComputeOutputParams. It can also be helpful to include RixシェーディングUtils.h if your pattern will use any of the Rixシェーディング utility functions. The beginning structure of an example PxrCustomNoise plugin is described below, which you can use to model your own pattern plugin:

#include "RixPattern.h"
#include "RixシェーディングUtils.h"

class PxrCustomNoise : public RixPattern
{
public:

    PxrCustomNoise();
    virtual ~PxrCustomNoise();

    virtual int Init(RixContext &, char const *pluginpath);
    virtual RixSCParamInfo const *GetParamTable();
    virtual void Finalize(RixContext &);

    virtual int ComputeOutputParams(RixシェーディングContext const*,
                                    RtInt *noutputs,
                                    OutputSpec **outputs,
                                    RixSCParamInfo const *);
private:
    // member variables
}

PxrCustomNoise::PxrCustomNoise() :
{
}

PxrCustomNoise::~PxrCustomNoise()
{
}

int
PxrCustomNoise::Init(RixContext &ctx, char const *pluginpath)
{
    return 0;
}

RixSCParamInfo const *
PxrCustomNoise::GetParamTable()
{
    static RixSCParamInfo s_ptable[] =
    {
        // Define the input and output parameters of the pattern
        // using the RixSCParamInfo struct for each one.
    }
    return &s_ptable[0];
}

void
PxrCustomNoise::Finalize(RixContext &ctx)
{
}

int
PxrCustomNoise::ComputeOutputParams(RixシェーディングContext const *sctx,
                                    RtInt *noutputs, OutputSpec **outputs,
                                    RixSCParamInfo const *ignored)
{
    // read each parameter value and compute the output

    return 0;
}

RIX_PATTERNCREATE
{
    return new PxrCustomNoise();
}

RIX_PATTERNDESTROY
{
    delete ((PxrCustomNoise*)pattern);
}

The methods and functions in the code above are defined as:

  • Init: Called when the plugin is first loaded by the renderer. The plugin will remain loaded for the lifetime of the render. Any global work that would be shared by all instances of a plugin should be done here. Init returns 0 if there was no error initializing the plugin.
  • GetParamTable: Creates an array (table) of RixSCParamInfo objects, where each object in the array defines an input or output parameter for the pattern plugin. Guidance for defining these objects is provided in the Defining Inputs and 出力 section below.
  • Finalize: Called when the plugin is unloaded from memory by the renderer.
  • ComputeOutputParams: This is the heart of a pattern plugin; it reads the input parameters and computes the output parameters. It is called once per graph execution. All outputs must be computed during this one call. The renderer provides a list of the outputs it expects the plugin to compute. Most often, this is exactly the same as the outputs declared in the parameter table.
  • RIX_PATTERNCREATE: Called by the renderer to create an instance of the pattern plugin.
  • RIX_PATTERNDESTROY: Called by the renderer to delete an instance of the pattern plugin.

Defining Inputs and 出力

Now that the basic structure of your pattern plugin is defined, we can define the plugin input and output parameters in the GetParamTable method. An example of this can be found in PxrTextureAtlas.cpp:

RixSCParamInfo const *
PxrTextureAtlas::GetParamTable()
{
    static RixSCParamInfo s_ptable[] =
    {
        // outputs
        RixSCParamInfo("resultC", k_RixSCカラー, k_RixSCOutput),
        RixSCParamInfo("resultF", k_RixSCFloat, k_RixSCOutput),

        // inputs
        RixSCParamInfo("atlas", k_RixSCString),
            //  Filename containing the special character pattern,
            // _MAPID_, which is replaced to produce the unique
            // filename associated with that region of u-v space
        RixSCParamInfo("style", k_RixSCInteger),
            // Either 0-mari or 1-mudbox
        RixSCParamInfo("channelOffset", k_RixSCInteger),
            // Which channel to start lookups
        RixSCParamInfo("linearize", k_RixSCInteger),
            // Set to 1 to always convert 8 textures from srgb
            // to linear rgb. 2 detects conversion automatically
        RixSCParamInfo("filter", k_RixSCInteger),
            //  The Rixテクスチャ::TxParam index of the filter to use
            //  for filtering over the texture.

        // inputs - connectable
        RixSCParamInfo("blur", k_RixSCFloat),

        RixSCParamInfo(), // end of table
    };
    return &s_ptable[0];
}

Each input and output parameter is defined as a RixSCParamInfo object. The RixSCParamInfo struct is defined in Rixシェーディング.h:

struct RixSCParamInfo
{
    // most common constructor of POD parameters.
    RixSCParamInfo(char const *nm, RixSCType t,
                   RixSCAccess a = k_RixSCInput,
                   int len = -1) :
        name(nm),
        customtype(NULL),
        type(t),
        access(a),
        arraylen(len)
    {
    }

    // full constructor
    RixSCParamInfo(char const *structnm, char const *nm,
                  RixSCType t, RixSCAccess a = k_RixSCInput,
                  int len = -1) :
        name(nm),
        customtype(structnm),
        type(t),
        access(a),
        arraylen(len)
    {
    }

    // default constructor, useful to signal end of paraminfo table
    RixSCParamInfo() :
        name(NULL),
        customtype(NULL),
        type(k_RixSCInvalidType)
    {
    }

    char const *name;
    char const *customtype; // NULL unless struct
    RixSCType type;
    RixSCAccess access;
    int arraylen; // -1 means no array, 0 means empty
    bool IsArray() const { return (arraylen != -1); }
};

In the PxrTextureAtlas::GetParamTable() method example, the resultC output parameter is a color, so it is defined as:

RixSCParamInfo("resultC", k_RixSCカラー, k_RixSCOutput)

A float input parameter named density can be defined as:

RixSCParamInfo("density", k_RixSCFloat)

While a float[16] input parameter named placementMatrix can be defined as:

RixSCParamInfo("placementMatrix", k_RixSCFloat, k_RixSCInput, 16)

Every parameter table must be null-terminated by an empty RixSCParamInfo object, which is created by adding RixSCParamInfo() to the end of the array returned by GetParamTable.

After you define the parameter table in GetParamTable it is recommended that you create a parameter enumeration to keep track of the order that your parameters were created in the table. The order will be used later on when you are reading in the parameters in the ComputeOutputParams method. An example of this can be found in PxrTextureAtlas.cpp:

enum paramId
{
    k_resultC=0, // output
    k_resultF,   // output
    k_atlas,
    k_style,
    k_channelOffset,
    k_linearize,
    k_filter,
    k_blur,
    k_numParams
};

Reading Inputs in ComputeOutputParams()

Now that the plugin input and output parameters are defined, it is time to read the inputs and compute the output values in the ComputOutputParams method. To read an input value, use the RixシェーディングContext::EvalParam method, which is defined in Rixシェーディング.h. EvalParam takes the following arguments:

  • id: The integer value that defines the order the parameter was defined in the table from GetParamTableA. This should correspond to one of the paramId enum values mentioned earlier.
  • arrayIndex: If the parameter is an array of values, then this defines the array index from which to start reading values. If the parameter is not an array, then set arrayIndex to -1.
  • result: The result buffer to store the parameter values in.
  • dflt: The default value to use for the parameter if it was not specified in the RiPattern call.
  • promoteToVarying: A boolean value that tells the plugin to evaluate the input as a varying or uniform value. Varying values can have a separate value for each shaded point, and they can be connected to the output of other Pattern plugins. A uniform value will only have one value for each shaded point, so it will use less memory than varying inputs.

Some examples for reading different types of input parameters are listed below:

int
PxrCustomNoise::ComputeOutputParams(RixシェーディングContext const *sctx,
                                RtInt *noutputs, OutputSpec **outputs,
                                RixSCParamInfo const *ignored)
{
    bool varying = true;
    bool uniform = false;
    RixSCType type;
    bool isconnected;

    // read a uniform integer value, and store the result in the
    // RtInt noiseType variable. m_noiseType is a PxrCustomNoise
    // member variable that contains the default noiseType value.
    RtInt *noiseTypePtr;
    sctx->EvalParam(k_noiseType, -1, &noiseTypePtr,
        &m_noiseType, uniform);
    RtInt const noiseType(*noiseTypePtr);

    // read a varying float value for the threshold input parameter.
    // m_threshold is a PxrCustomNoise member variable that contains
    // the default value.
    RtFloat *threshold;
    sctx->EvalParam(k_threshold, -1, &threshold, &m_threshold, varying);

    // read one value from a varying float[2] array
    RtFloat *repeatUV0, *repeatUV1;
    sctx->EvalParam(k_repeatUV, 0, &repeatUV0, &m_repreatUV, varying);
    // read the other value from a varying float[2] array. Note that
    // the arrayIndex parameter is set to 1 to read the second value.
    sctx->EvalParam(k_repeatUV, 1, &repeatUV1, &m_repreatUV, varying);

    // read in a float[3] array of values into a RtFloat3 variable.
    RtFloat3 *scale;
    sctx->EvalParam(k_scale, -1, &scale, &m_scale, varying);

    // check for manifold input
    RtPoint3 *Q = (RtPoint3*)RixAlloca(sizeof(RtPoint3)*sctx->numPts);
    RtFloat *Qradius = (RtFloat*)RixAlloca(sizeof(RtFloat)*sctx->numPts);
    sctx->GetParamInfo(k_manifoldBegin, &type, &isconnected);
    if(isconnected)
    {
        sctx->EvalParam(k_manifoldQ, -1, &Q);
        sctx->EvalParam(k_manifoldQradius, -1, &Qradius);
    }
    else
    {
        // allocate space for our remapped P
        RtFloat const *pvWidth;
        RtPoint3 const *pv;
        char const *primvarNm = "P";
        RtFloat3 fill(0.f, 0.f, 0.f);
        sctx->GetPrimVar(primvarNm, fill, &pv, &pvWidth);
        *Qradius = *pvWidth;
        memcpy(Q, pv, sizeof(RtPoint3) * sctx->numPts);
    }

    // read in the placementMatrix float values and
    // default the to the identity matrix
    RtFloat *placementMatrix = (RtFloat*)RixAlloca(sizeof(RtFloat)*16);
    const RtFloat zero = 0.0f;
    const RtFloat one = 1.0f;
    RtFloat* placementInput;
    for (int i=0; i< 16; i++)
    {
        if ( i % 5 == 0)
            sctx->EvalParam(k_placementMatrix, i,
                &placementInput, &one, uniform);
        else
            sctx->EvalParam(k_placementMatrix, i,
                &placementInput, &zero, uniform);
        placementMatrix[i] = *placementInput;
    }

    // read in a varying color value.
    RtカラーRGB* defaultカラー;
    sctx->EvalParam(k_defaultカラー, -1, &defaultカラー,
        &m_defaultカラー, varying);

Writing the Output

Now that you have read in the input parameter values, you can start to write the output values in ComputeOutputParams. First, you will need to allocate memory for the outputs using a RixシェーディングContext::Allocator object, and then bind the output parameters to the outputs parameter of ComputeOutputParams:

int
PxrCustomNoise::ComputeOutputParams(RixシェーディングContext const *sctx,
                                RtInt *noutputs, OutputSpec **outputs,
                                RixSCParamInfo const *ignored)
{
    // read in the inputs
    // ...

    // Allocate and bind the output parameters.
    // In this example, there are two output parameters: outカラー and outAlpha
    RixシェーディングContext::Allocator pool(sctx);
    OutputSpec *o = pool.AllocForPattern<OutputSpec>(2);
    *outputs = o;
    *noutputs = 2;
    RtカラーRGB* outカラー = NULL;
    RtFloat* outAlpha = NULL;

    outカラー = pool.AllocForPattern<RtカラーRGB>(sctx->numPts);
    outAlpha = pool.AllocForPattern<RtFloat>(sctx->numPts);

    // define the param ID, detail, and value for each
    // output parameter.
    o[0].paramId = k_outカラー;
    o[0].detail = k_RixSCVarying;
    o[0].value = (RtPointer) outカラー;

    o[1].paramId = k_outAlpha;
    o[1].detail = k_RixSCVarying;
    o[1].value = (RtPointer) outAlpha;

The next step is to start calculating your output. This is done by looping through the number of shaded points given by the RixシェーディングContext parameter of ComputeOutputParams so that you are assigning an output value for each shaded point.

for (int i=0; i<sctx->numPts; i++)
{
    // Compute some output values based on your input.
    if (style == 1)
    {
        outカラー[i] = defaultカラー[i];
    }
}

In the simple example above, the style variable was a uniform RtInt input value, so there is only one value for all the points in the shading context. The defaultカラー variable was varying instead of uniform, so it a pointer to an array of RtカラーRGB values (one for each shaded point in the shading context).

The ComputeOutputParams method should return 0 if no error occurred while calculating the output, otherwise it should return a non-zero integer value.

For more examples of how input and output parameters are handled in the ComputeOutputParams method, see the pattern plugin examples in the /lib/examples/RIS/plugins/pattern/ directory of your RenderMan Pro Server installation.


Testing Your Pattern Plugin

After you have implemented the code for your pattern plugin, you can build it using the commands listed in the Compiling Plugins page. The next step is to test your plugin. To test it, you'll need to make sure prman can find your plugin in the standardrixpluginpath list of directories, which is defined in $RMANTREE/etc/rendermn.ini as:

/standardrixpluginpath          ${RMANTREE}/lib/RIS/pattern:${RMANTREE}/lib/RIS/bxdf:${RMANTREE}/lib/RIS/integrator:${RMANTREE}/lib/RIS/projection

You can add a rendermn.ini file to your HOME directory and modify the standardrixpluginpath value to contain the directory where your pattern plugin is located.

Then you can try to render this RIB file after you have replaced "PxrCustomPattern" with the name of your pattern plugin and connect your pattern's output parameter to one of the input parameters of the PxrDiffuse Bxdf:

Display "patternTest" "framebuffer" "rgba"
Quantize "rgba" 255 0 255 0
Format 128 128 1
Projection "perspective" "fov" [45]
Hider "raytrace" "string integrationmode" ["path"]
Integrator "PxrPathTracer" "integrator"

WorldBegin

    AttributeBegin
        Attribute "identifier" "name" ["sphere1"]
        Translate 0 0 2.75

        Pattern "PxrCustomPattern" "customPattern"

        Bxdf "PxrDiffuse" "smooth"
            "reference color diffuseカラー" "customPattern:outカラー"
        Sphere 1.0 -1.0 1.0 360.0
    AttributeEnd

WorldEnd

Creating a Pattern args File

If you would like RenderMan for Maya or Katana to recognize your pattern plugin and provide a user interface for changing input parameters and connecting output parameters to other nodes, then you will need to create an args file for your pattern. The args file defines the input and output parameters in XML so that tools like RMS or Katana can easily read them, discover their type, default values, and other information used while creating the user interface for the pattern node.

The args file format is described in the Katana Technical Guide and there are also example args files for the RPS pattern plugins in the /lib/RIS/pattern/Args/ directory of your RenderMan Pro Server installation.