Writing Light Filters

This page covers how to write new types of light filters for MoonRay. Unlike other categories in the Developer’s Guide, there is no formalized API for designing and writing a plugin in one location yet. As such, light filters must currently be added at the MoonRay layer, not the MoonShine layer. Adding a new type of light filter is still relatively straightforward.

Overview

Each light filter plugin reguires you to:

  • Create a default scene object class in moonray/dso/lightfilter to read and store attributes
  • Create a light filter class in moonray/lib/rendering/pbr/lightfilter to do the actual work
  • Change the factory in moonray/lib/rendering/pbr/core to instantiate the light filter when it finds the scene class

Creating the Light Filter scene object

Every existing light filter requires a “default” class in moonray/dso/lightfilter. Each one is identical, outside of the name. Here’s what DecayLightFilter looks like:

#include <scene_rdl2/scene/rdl2/rdl2.h>

#include "attributes.cc"

using namespace scene_rdl2;

RDL2_DSO_CLASS_BEGIN(DecayLightFilter, rdl2::LightFilter)

public:
    RDL2_DSO_DEFAULT_CTOR(DecayLightFilter)

RDL2_DSO_CLASS_END(DecayLightFilter)

Differences appear when authoring the light filter’s attributes. Unlike maps or materials where you author a .json that is converted into an associated attributes.cc at compile time, you must directly author attributes.cc here.

A single common attribute is handled by the parent scene object class, rdl2::LightFilter. This is the on/off toggle for the light filter.

This file has a simple call to instantiate the common LightFilter attributes, and the attributes authored here afterward are unique to this light filter.

LightFilter attribute files are structured like this:


#include <scene_rdl2/scene/rdl2/rdl2.h>

using namespace scene_rdl2;

RDL2_DSO_ATTR_DECLARE

    // declare all specific attributes here
    rdl2::AttributeKey<rdl2::Float>     attrMyAttribute;

// instantiate parent attributes
RDL2_DSO_ATTR_DEFINE(rdl2::LightFilter)

    // define all declared attributes here
    attrMyAttribute = sceneClass.declareAttribute<rdl2::Float>("my_attribute", 1.0f);
    sceneClass.setMetadata(attrMyAttribute, rdl2::SceneClass::sComment,
        "This is the mouseover text a user would read in a DCC for my_attribute");

    // apply grouping and labels, and add to the scene. creates subfolders in DCC
    sceneClass.setGroup("Properties", attrMyAttribute);

RDL2_DSO_ATTR_END

Creating the Light Filter Class

The actual work is done in the libraries. Light filters are all written in moonray/lib/rendering/pbr/lightfilter.

There are typically 3 files that make up a light filter’s source:

  • <ClassName>.cc
  • <ClassName>.h
  • <ClassName>.ispc

Notice that .isph is not necessary– setup for the ispc struct and members is handled in the header, often using macros found in the shared header LightFilter.hh.

LightFilter.h is the header that defines the parent class: LightFilter.

Light filters are instantiated and updated in C++ and the relevant information is passed over to ispc during vectorized rendering.

Instantiating Attributes in the Light Filter

Every light filter requires static keys for looking up each attribute defined in its scene object class. It is expected to initialize them once when the first light filter of this type is instantiated.

In the header:

class MyLightFilter : public LightFilter
{
...

private:
    void initAttributeKeys(const scene_rdl2::rdl2::SceneClass &sc);
...
    static bool sAttributeKeyInitialized;
    static scene_rdl2::rdl2::AttributeKey<scene_rdl2::rdl2::Float> sMyAttributeKey;
}

In the .cc:

bool                                                    MyLightFilter::sAttributeKeyInitialized;
scene_rdl2::rdl2::AttributeKey<scene_rdl2::rdl2::Float> MyLightFilter::sMyAttributeKey;

...
void
MyLightFilter::initAttributeKeys(const scene_rdl2::rdl2::SceneClass &sc)
{
    if (sAttributeKeyInitialized) {
        return;
    }

    MOONRAY_START_NON_THREADSAFE_STATIC_WRITE

    sAttributeKeyInitialized = true;

    // The string here MUST match the one defined in the dso
    sMyAttributeKey = sc.getAttributeKey<scene_rdl2::rdl2::Float>("my_attribute");

    ...

    MOONRAY_FINISH_NON_THREADSAFE_STATIC_WRITE
}

Required Functions

The following public functions need to be implemented in a custom light filter.

Detailed comments for each can be found in LightFilter.h.

// C++ only.

    // Handle parent attributes, transformations, precalculations
    virtual void update(const LightFilterMap& lightFilters,
                        const scene_rdl2::math::Mat4d& world2render) = 0;

    virtual bool canIlluminate(const CanIlluminateData& data) const = 0;
    virtual scene_rdl2::math::Color eval(const EvalData& data) const = 0;
    virtual bool needsLightXform() const { return false; }
    virtual bool needsSamples() const { return false; }

Instantiating the Light Filters in the Renderer

There’s a function in moonray/lib/rendering/pbr/core/Scene.cc that creates and updates the light filters in the Scene. Include the header of your custom light filter in this file, and modify this function to add your light filter:

void
Scene::updateLightFilters()
{
...
        if (className == "ColorRampLightFilter") { // Construct filter
            lightFilter = new ColorRampLightFilter(rdlLightFilter);
        } else if (className == "CombineLightFilter") {
            lightFilter = new CombineLightFilter(rdlLightFilter);
        } else if (className == "CookieLightFilter") {
            lightFilter = new CookieLightFilter(rdlLightFilter);
        } else if (className == "CookieLightFilter_v2") {
            lightFilter = new CookieLightFilter_v2(rdlLightFilter);
        } else if (className == "BarnDoorLightFilter") {
            lightFilter = new BarnDoorLightFilter(rdlLightFilter);
        } else if (className == "DecayLightFilter") {
            lightFilter = new DecayLightFilter(rdlLightFilter);
        } else if (className == "IntensityLightFilter") {
            lightFilter = new IntensityLightFilter(rdlLightFilter);
        } else if (className == "RodLightFilter") {
            lightFilter = new RodLightFilter(rdlLightFilter);
        } else if (className == "VdbLightFilter") {
            lightFilter = new VdbLightFilter(rdlLightFilter);
        } else if (className == "MyLightFilter") {
            lightFilter = new MyLightFilter(rdlLightFilter);
        } else {
            MNRY_ASSERT(!"Unknown light filter type.");
        }
...
}