Writing Camera Shaders

This page explains how to write new types of Cameras for MoonRay.

Overview

Each Camera plugin reguires you to:

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

Creating the Camera scene object

Every existing camera requires a class in moonray/dso/camera. Here’s what SphericalCamera looks like:

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

#include "attributes.cc"

using namespace scene_rdl2;

RDL2_DSO_CLASS_BEGIN(SphericalCamera, rdl2::Camera)

public:
    RDL2_DSO_DEFAULT_CTOR(SphericalCamera)

RDL2_DSO_CLASS_END(SphericalCamera)

Differences appear when authoring the cameras’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 number of common attributes are handled by the parent scene object class, rdl2::Camera. This includes (but is not limited to) near/far clipping planes, shutter open/close times, and an initial medium (e.g. a water material).

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

Camera 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::Camera)

    // 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 Camera Class

The actual work is done in the libraries. Cameras are all written in moonray/lib/rendering/pbr/camera.

There are typically 2 files that make up a Camera’s source:

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

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

Cameras are instantiated and updated in C++. There is no vectorized ispc Camera code.

Instantiating Attributes in the Camera

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

In the header:

class MyCamera : public Camera
{
...

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                                                    MyCamera::sAttributeKeyInitialized;
scene_rdl2::rdl2::AttributeKey<scene_rdl2::rdl2::Float> MyCamera::sMyAttributeKey;

...
void
MyCamera::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 Camera.

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

    virtual bool getIsDofEnabledImpl() const = 0;
    virtual bool hasFrustumImpl() const { return false; }
    virtual void computeFrustumImpl(mcrt_common::Frustum *frust, float t, bool useRenderRegion) const;
    virtual void bakeUvMapsImpl();
    virtual void getRequiredPrimAttributesImpl(shading::PerGeometryAttributeKeySet &keys) const;
    virtual float computeZDistanceImpl(const scene_rdl2::math::Vec3f &p, const scene_rdl2::math::Vec3f &o,
                                       float time) const;
    virtual void updateImpl(const scene_rdl2::math::Mat4d& world2render) = 0;

    virtual void createRayImpl(mcrt_common::RayDifferential* dstRay,
                           float x,
                           float y,
                           float time,
                           float lensU,
                           float lensV) const = 0;

    virtual StereoView getStereoViewImpl() const { return StereoView::CENTER; }

Instantiating the Camera in the Renderer

There’s a function in moonray/lib/rendering/pbr/core/Scene.cc that checks the name of the rdl scene object and matches it with the actual class. Include the header of your custom Camera in this file, and modify this function to add your camera:

namespace {
std::unique_ptr<Camera> cameraFactory(const rdl2::Camera* rdlCamera)
{
    const rdl2::SceneClass& cameraClass = rdlCamera->getSceneClass();
    const std::string& className = cameraClass.getName();
    if (className == "PerspectiveCamera") {
        return std::unique_ptr<Camera>(new PerspectiveCamera(rdlCamera));
    } else if (className == "OrthographicCamera") {
        return std::unique_ptr<Camera>(new OrthographicCamera(rdlCamera));
    } else if (className == "SphericalCamera") {
        return std::unique_ptr<Camera>(new SphericalCamera(rdlCamera));
    } else if (className == "BakeCamera") {
        return std::unique_ptr<Camera>(new BakeCamera(rdlCamera));
    } else if (className == "MyCamera") {
        return std::unique_ptr<Camera>(new MyCamera(rdlCamera));
    } else {
        MNRY_ASSERT(!"Should not get here");
        throw std::runtime_error("No valid camera type specified");
    }
}
} // namespace