Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents

Introduction

This documentation is intended to instruct developers in the authoring of custom light filters. Developers should also consult the RixLightFilter.h header file for complete details.

...


Will turn on all light filters in group "blockers" for all lights shining on the sphere.  By convention, all of the filters shipped with RenderMan use "linkingGroups" as the name of the parameter.   Both the "linkingGroups" and "lightfilter:subset" strings may be comma-separated lists of group names.  Group name matching is not scoped to a light.  Any filter in a matching group on any light active on a given geometric primitive will be enabled.

...

Implementing the RixLightFilter interface

Light filters implement the RixLightFilter interface found in RixLightFilter.h.  A light filter must implement three methods:  RixLightFilter is a subclass of RixShadingPlugin, and therefore shares the same initializationsynchronization, and parameter table logic as other shading plugins.  Therefore to start developing your ownlLight filter, you can #include "RixLightFilter.h" and make sure your light filter class implements the required methods inherited from the RixShadingPlugin interface: Init()Finalize()Synchronize()GetParamTable(), and CreateInstanceData().  Additionally, your light filter subclass must implement the three methods of the RixLightFilter interface, Filter(), GetProperty(), and GetRadianceModifier().

Generally, there is one shading plugin instance of a RixLightFilter per bound RiLightFilter (RIB) request. This instance may be active in multiple threads simultaneously.

The RIX_LIGHTFILTERPLUGINCREATE() macro defines the CreateRixLightFilter() function, which is called by the renderer to create an instance of the light filter plugin. Generally, the implementation of this method should simply return a new allocated copy of your light filter class. Similarly, the RIX_LIGHTFILTERPLUGINDESTROY() macro defines the DestroyRixLightFilter() function called by the renderer to delete an instance of the light filter plugin; a typical implementation of this method is to delete the passed in light filter pointer:

Code Block
RIX_LIGHTFILTERPLUGINCREATE
{
    return new MyLightFilter();
}

RIX_LIGHTFILTERPLUGINDESTROY
{
    delete ((MyLightFilter*)filter);
}


RixLightFilter::Filter()

Filter is where the work is done.

Code Block
languagecpp
virtual void Filter(
	RixLightFilterContext const *lfCtx,
    RtConstPointer instanceData,
    RtIntint const numSamples,
    RtIntint const * shadingCtxIndex,
    RtVector3 const * toLight,
    RtFloatfloat const * dist,
    RtFloatfloat const * lightPdfIllum,
    RixBXLobeWeights *contribution);

...

The input/output array contribution contains the lighting contribution distributed into some number of diffuse and specular lobes. For each lobe there are numSamples entries. For instance, to modulate the lighting by a filter that passes only the red channel:

Code Block
languagecpp
for(int j = 0; j < contribution->GetNumSpecularLobes(); ++j) {
    for(int i = 0; i < numSamples; ++i) {
       contribution->GetSpecularLobe(j)[i] *= RtColorRGB(1.0,0.0f,0.0f);
    }
}

for(int j = 0; j < contribution->GetNumDiffuseLobes(); ++j) {
    for(int i = 0; i < numSamples; ++i) {
       contribution->GetDiffuseLobe(j)[i] *= RtColorRGB(1.0,0.0f,0.0f);
    }
}

...


RixLightFilter::GetProperty()

Code Block
languagecpp
RixSCDetail PxrGobo::GetProperty(
    RtConstPointer instanceData, LightFilterProperty property, void const** result) const
{
    myData* data = (myData*)instanceData;

    if (RixLightFilter::k_LinkingGroups == property)
    {
        (*result) = &data->m_linkingGroups;
        return k_RixSCUniform;
    }
    return k_RixSCInvalidDetail;
}

GetProperty() is an extensible API through which the renderer may query the plugin.  The list of valid queries are enumerated in LightFilterProperty.  If a plugin can answer the query, it fills in result and returns k_RixSCUniform.  Otherwise GetProperty should return k_RixSCInvalidDetail.  The example given shows how the plugin returns the unique string for the linking groups to which the light filter belongs.


RixLightFilter::GetRadianceModifier()

Code Block
languagecpp
bool PxrMyLightFilter::GetRadianceModifier(
    FilterRadianceModifierProperty property,
    RixLightFilterContext const* lfCtx,
    RtConstPointer instanceData,
    float* result) const
{
    *result = m_attenuation;
    return true;
}

GetRadianceModifier() returns a single float representing how much the filter modulates the light emitted from a light source.  Not all filters attenuate or amplify the light signal; those plugins would return false and not modify result.

Instance Data

For a detailed discussion of instance data, see lightweight instancing services.  When

Instance Data

The optional instance data contains data generated by the plugin when it is first encountered in the RI stream. When the CreateInstanceData routine is called, the plugin has access to the parameter list of the light filter and can create arbitrary data that is stored by the renderer and supplied as the instanceData pointer during filtering. This should be uniform data that can be accessed by multiple threads simultaneously.  Pre-processing the parameter list is an important performance optimization.  A light filter will be invoked for every surface-to-light interaction, and in a production-level shot there can be billions of these events.

As an example, below is the CreateInstanceData method for PxrLightFilterCombiner. Like bxdfs, light filters can be referenced in arguments to other light filters. To run multiple light filters, call EvalParam to get pointers to the upstream light filter and it's instance. These are stored and used later during filtering.

Code Block
languagecpp
struct myData
{
    int arrayLen;
    RixLightFilter** filters;
    RtConstPointer* instances;
};

static

...

 void releaseInstanceData(RtPointer data)
{
    myData* md = (myData*) data;
    delete[] md->filters;
    delete[] md->instances;
    delete md;
}

int

...

 PxrLightFilterCombiner::CreateInstanceData(
    RixContext &ctx,

...

    

...

char const *handle,

...

    

...

RixParameterList const *plist,

...

    RixShadingPlugin::InstanceData *idata)
{
    RixSCType typ;
    bool isconnected;
    int arraylen;
    plist->GetParamInfo(k_mult, &typ, &isconnected, &arraylen);

    myData* mydata = new myData;
    mydata->arrayLen = arraylen;
    mydata->filters = new RixLightFilter* [arraylen];
    mydata->instances = new RtConstPointer [arraylen];

    for (int i=0; i<arraylen; ++i)
        plist->EvalParam(k_mult, i, &mydata->filters[i], &mydata->instances[i]);

    idata->data = (void *) mydata;
    idata->freefunc = releaseInstanceData;
    return 0;
}

...


Running multiple filters

Like bxdfs, light filters can be parameters to other light filters. The last filter given before an AreaLightSource call an Light call (the root filter in a tree of filters) is responsible for delegating to the filters in its parameter list. Furthermore, light filters can be disabled on a gprim basis. To respect this, it is the responsibility of the filter to call RixLightFilterContext::IsEnabled(). If the upstream filter is not enabled, don't run it. IsEnabled() also returns a pointer to the upstream filter's instanceData, which should be passed to its Filter() method.

Code Block
languagecpp
void 

...

PxrLightFilterCombiner::Filter(
    RixLightFilterContext const *lfCtx,

...

    

...

RtConstPointer instanceData,
    

...

int const numSamples,
    

...

int const * shadingCtxIndex,

...

    

...

RtVector3 const * toLight,
    

...

float const * dist,
    

...

float const * lightPdfIllum,

...

    

...

RixBXLobeWeights *contribution

...

)
{
  myData const * mydata = (myData const *) instanceData;

  for (int i=0; i<mydata->arrayLen; ++i) {
      RtConstPointer nextInstanceData;
      if (lfCtx->IsEnabled(mydata->instances[i], &nextInstanceData))
      {
          mydata->filters[i]->Filter(
              lfCtx, nextInstanceData,
              numSamples, shadingCtxIndex,
              toLight, dist, lightPdfIllum, contribution);
      }
  }
}