Overview ======== The Renderer API allows the integration of renderers into Katana. .. _plugin-intro: What is a render plug-in? ------------------------- A render plug-in is a collection of modules which provide Katana with renderer-specific information that can be configured and declared to the renderer in the UI as well as handling the corresponding Katana recipes at render time. The modules used to implement a render plug-in are the following: - `Render::RenderBase `_: Translates a Katana recipe into the renderer's language using a scene graph iterator (:doc:`FnScenegraphIterator <../Utilities/SceneGraphIterator>`), where the rendered content is typically visualised in Katana's **Monitor** tab. A render plug-in represents a single render process, which is instantiated with the Katana recipe and render arguments when a render is launched, and which is destroyed when the render is complete or cancelled. The render plug-in can delegate the handling of scene graph location types to delegate plug-ins (see `Render::ScenegraphLocationDelegate `_). This enables the registering and handling of new renderer-specific location types without changing the render plug-in. See :ref:`Recipe utility classes `. - `RendererInfo::RendererInfoBase `_: Provides Katana with renderer-specific information needed to configure a renderable scene, e.g. shaders, render outputs. If applicable, this plug-in also advertises and configures nodes such as ShadingNode and OutputChannelDefine. It is also responsible for :ref:`advertising supported render methods ` such as preview and disk renders. - :ref:`Configuration nodes `: Renderer-specific nodes are useful for configuring renderable properties such as global and object settings. .. _render-methods: Advertising render methods to start a render process ---------------------------------------------------- A render plug-in must advertise one or more supported render methods which become available in the UI (unless explicitly hidden) and script mode. Render methods are created by adding instances of render method classes to the vector passed into `RendererInfo::RendererInfoBase::fillRenderMethods `_. The base render method classes represent different ways of handling a render process in Katana: - `RendererInfo::DiskRenderMethod `_: The render plug-in can query all the render outputs which have been declared using RenderOutputDefine nodes. Each output has a target location where it is intended to be written to disk. The primary output will be loaded and displayed in the **Monitor** tab. - See :ref:`Rendering one or more render outputs to disk `. - `RendererInfo::PreviewRenderMethod `_: Preview renders are sent directly from the renderer to the **Monitor** tab using an inter-process communication protocol (IPC) where each bucket can be sent interactively (and progressively). Preview renders can display multiple AOVs in the **Monitor** tab but only one view. - See :ref:`Sending rendered images to Katana using the Display Driver API `. - `RendererInfo::LiveRenderMethod `_: A live render follows the same principles as a preview render in terms of how the render is launched and how the rendered content is delivered to Katana. The difference is that the render process is kept alive to allow live attribute updates, which are sent to the render plug-in using IPC. - See the :ref:`key stages of the render process ` and how they affect live rendering. - See `Render::RenderBase::queueDataUpdates `_. Each render method can :ref:`advertise and write render debug outputs ` to a file which are displayed in an external editor. Render methods can be hidden from the UI using `RendererInfo::RenderMethod::setVisible `_ where it can still be used in script mode when launching a render process with StartRender. A render method can customise the way a render is launched in Katana. By default, a render process will create a catalog item, register the render so that it may be cancelled etc., and report any render messages in the render log. These properties can be configured to launch a detached render where Katana will absolve itself from any involvement beyond launching the render process with the recipe. This is done by passing false into the following functions: - `RendererInfo::RenderMethod::setCreateCatalogItem `_ - `RendererInfo::RenderMethod::setReportRenderMessages `_ - `RendererInfo::RenderMethod::setRegisterRender `_ .. _batch-rendering: Batch rendering ``````````````` Katana requires a batch render method so the ``RendererInfoBase`` automatically creates a ``DiskRenderMethod`` with default parameters which will be used for batch rendering. The parameters can be overridden in `RendererInfoBase::configureBatchRenderMethod `_. .. _render-methods-ui: Launching render methods in the UI `````````````````````````````````` The render methods are grouped in Katana's **Render** menu based on their type. The method name is passed as an argument to the render process where the render plug-in can retrieve it using `Render::RenderBase::getRenderMethodName `_. :Tip: It is useful to use the default name and label provided by the render method classes for the most common cases as the render menu collapses items whose corresponding methods share the same name and label. This is done to allow simplifying the render menu where multiple renderers have equivalent render methods. Furthermore, standardising the user experience across renderers makes it easier for users to find the render method they want to launch. .. _starting-renders: Starting a render ----------------- A render plug-in must implement the `Render::RenderBase::start `_ function which is called at the start of Katana's render process. The plug-in has immediate access to: - The Katana recipe which is traversed using a :doc:`FnScenegraphIterator <../Utilities/SceneGraphIterator>`, where the root iterator is retrieved using `Render::RenderBase::getRootIterator `_. Utility classes and functions are available which assist with the scene graph traversal and attribute handling (see :ref:`Processing the Katana recipe `). - Render arguments consisting of the render method name which was used to launch the render process, render time (frame), etc. These arguments can be retrieved either directly as strings using `Render::RenderBase::findArgument `_, or alternatively using explicit wrapper functions such as `Render::RenderBase::getRenderMethodName `_ and `Render::RenderBase::getRenderTime `_ which returns the render time as a float value. .. _render-process: Key stages of the render process -------------------------------- Implementing only the start function is sufficient for most renders. More flexibility is needed for live renders where the render process is kept alive and updates/commands can be sent to the render plug-in until the render is either stopped by the user, or the Katana session ends. This flexibility is provided by supporting optional functions which are called at different points during the render process: - `Render::RenderBase::start `_: Called to start the render process. This is the only function that must be implemented by the render plug-in. - `Render::RenderBase::pause `_: Pause the render/live render process. Currently it is only called during a live render when updating regions of interest. - `Render::RenderBase::stop `_: Always called at the end of the render process. - `Render::RenderBase::queueDataUpdates `_: Called asynchronously in a separate thread during a live render with the attributes that have changed. The updates can either be applied directly or queued here and applied later in the main thread. When and how often this function is called depends on the **3D Update Mode**: - **Manual**: The ``queueDataUpdates()`` function is called when the user explicitly clicks the **Trigger 3D Update** button. - **Pen-Up**: The ``queueDataUpdates()`` function is called after an attribute value has changed anywhere in the scene graph. - **Continuous**: The ``queueDataUpdates()`` function is called with a fixed frequency (every 10ms) if attribute values have changed. Currently this only applies to camera and light transform updates in the viewer. - `Render::RenderBase::hasPendingDataUpdates `_: Called after live update data or command has been processed asynchronously to inform the render process of whether some or all of the data updates have not been applied in the queueDataUpdates function. - `Render::RenderBase::applyPendingDataUpdates `_: Called if hasPendingDataUpdates returns true. This is used to facilitate a workflow where a set of updates are queued in the update thread and then flushed in the main thread. - `Render::RenderBase::processControlCommand `_: Called when a custom live render command is executed. - `Render::RenderBase::stopLiveEditing `_: Called when a live render is stopped by the user. .. _recipe-traversal: Processing the Katana recipe and utility classes ------------------------------------------------ Interpreting the Katana recipe into the renderer's language involves a deferred evaluation of the scene graph. This is done by traversing the scene graph using a :doc:`FnScenegraphIterator <../Utilities/SceneGraphIterator>` starting from the root. The render plug-in can get the root iterator using `Render::RenderBase::getRootIterator `_. Each iterator corresponds to a scene graph location which contains a set of corresponding attributes. Scene graph location types and attributes are based on conventions where the render plug-in can selectively handle location types and attributes that are applicable to the renderer. .. _sg-delegates: Scene graph delegates ````````````````````` The handling of scene graph locations and their attributes can be dynamically delegated to `Render::ScenegraphLocationDelegate `_ plug-ins. Given an arbitrary scene graph iterator (e.g. :doc:`FnScenegraphIterator <../Utilities/SceneGraphIterator>` sgi), we can check if a corresponding delegate plug-in exists and invoke it using `RenderOutputUtils::processLocation `_ .. code-block:: c++ bool pluginFound = RenderOutputUtils::processLocation(sgi, "myrenderer", sgi.getType(), 0x0, 0x0); .. _recipe-utility-classes: Scene graph utility classes ``````````````````````````` Utility classes are provided to parse standardised attribute conventions where they can be extended to provide renderer-specific behaviour through overrides: - `Render::RenderSettings `_: Parses **renderSettings** at **/root**. - `Render::CameraSettings `_: Parses **camera.geometry** at **/root/world/cam/**. - `Render::GlobalSettings `_: Parses **GlobalStatements** at **/root** (see :ref:`Configuring global settings `). - RenderOutputUtils: A collection of useful utility functions. :Tip: Avoid using the iterator function getByPath to get an iterator for a location that has already been traversed as it will traverse the scene graph again from the root. It is generally preferable to use a cached iterator class unless the memory footprint is a critical issue. .. _disk-images-to-katana: Disk Render: Rendering one or more render outputs to disk --------------------------------------------------------- Disk renders can only be launched from a render node and the renderer has to provide a target filename for each render output (port). The rendered target file for the primary render output is read and displayed in the **Catalog** and **Monitor** tabs when the render is complete (if the file type is supported). There are two steps required to configure and perform a disk render: - Configure how the render outputs are handled. This includes where they are rendered to, whether they should be displayed in the **Monitor** tab, what pre- and post-processes are required, etc. This is done in `Render::RenderBase::configureDiskRenderOutputProcess `_ which is called for each output where the type of `Render::RenderAction `_ and its properties governs the workflow for the render output. - Loop over and process each render output in the render plug-in. The render outputs are parsed in `Render::RenderSettings `_ where they are retrieved using e.g. `Render::RenderSettings::getRenderOutputs `_. Following these steps, a simple workflow for a **color** pass which renders to a temporary location where the file is then copied to the target location could be as follows (note that the following code snippet omits the ``Foundry::Katana`` namespace for simplicity): **configureDiskRenderOutputProcess** .. code-block:: c++ // Get the render outputs from the render settings RenderSettings renderSettings(getRootIterator()); RenderSettings::RenderOutput output = renderSettings.getRenderOutputByName(outputName); // Generate a unique temporary local output location used by the renderer std::string tempRenderLocation = RenderOutputUtils::buildTempRenderLocation( rootIterator, outputName, "render", "exr", frameTime); // Use the path from the location plug-in as a final target location which // is displayed in the Monitor tab std::string targetRenderLocation = outputPath; // Declare the render action used for this render output std::auto_ptr renderAction; // Determine the rendering behaviour based on the output type if (output.type == "color") { // Here we render to a temporary location and then convert and copy it to the final location renderAction.reset(new Render::CopyAndConvertRenderAction(targetRenderLocation, tempRenderLocation, output.clampOutput, output.colorConvert, output.computeStats, output.convertSettings)); } The attributes for each output are found at the root of the scene graph under **renderSettings.outputs**. The easiest way to get the attribute values for a particular render output is to use `Render::RenderSettings::getRenderOutputByName `_. **start (or any function which processes the render outputs)** .. code-block:: c++ ... // Get all render outputs for a disk render RenderOutputs outputs = renderSettings.getRenderOutputs(); // Iterate through and process each output ... if (output.type == "color") { // Get the render location which in this case is a temporary location // as defined by the render action std::string renderLocation = output.renderLocation; ... } .. note:: Disk renders do not support displaying arbitrary output variables (AOVs) in the **Monitor** tab. .. note:: Katana's **Monitor** tab currently only supports images of type EXR, CIN, RLA, and OIIO. .. seealso:: - `Render::RenderBase::configureDiskRenderOutputProcess `_ - `Render::DiskRenderOutputProcess `_ - `Render::RenderAction `_ - `Render::CopyRenderAction `_ - `Render::CopyAndConvertRenderAction `_ - `Render::TemporaryRenderAction `_ - `Render::NoOutputRenderAction `_ - `Render::RenderSettings::RenderOutputs `_ - `Render::RenderSettings::getRenderOutputs `_ .. _preview-images-to-katana: Preview Render: Rendering directly to Katana's **Monitor** tab -------------------------------------------------------------- A preview render can be launched from any node in the UI where the renderer sends the rendered content (e.g. buckets) interactively to the **Monitor** tab as soon as they are ready using the :doc:`Display Driver API <../DisplayDriver>`. Katana starts a listener when the UI is launched which retrieves image and ID data over a communication protocol. Preview renders have no concept of render outputs but instead use channel buffers that contain the names of the selected outputs and the corresponding buffer IDs. The IDs represent buffers that are reserved in Katana's **Catalog** tab and they are used by the display driver to make sure the image channel data goes to the right buffer. The following exmample demonstrates how the channel buffers are queried from the render settings (note that the following code snippet omits the ``Foundry::Katana`` namespace for simplicity): .. code-block:: c++ RenderSettings::ChannelBuffers buffers; renderSettings.getChannelBuffers( buffers ); RenderSettings::ChannelBuffers::const_iterator it; for( it = buffers.begin(); it != buffers.end(); ++it ) { RenderSettings::ChannelBuffer buffer = it->second; // Use buffer.bufferId and buffer.channelName with the Display Driver API ... } .. note:: Katana's **Monitor** tab currently only supports images of type EXR, CIN, RLA, and OIIO. .. seealso:: - `Render::RenderSettings::ChannelBuffer `_ - `Render::RenderSettings::getChannelBuffers `_ - `RendererInfo::RenderMethod `_ .. _debug-output: Generating a render debug output -------------------------------- There are two steps involved in creating a render debug output: - Advertise that a render method supports debug outputs. This creates the necessary UI hooks to allow the user to ask for a debug output. - Check for specific render arguments in the render plug-in which indicate that the render process was triggered with the expectation that a render debug output file is created. .. _debug-advertise: Advertising debug output support ```````````````````````````````` The most common way of requesting render debug output in Katana is through the **Debug** submenu in a node's context menu. In order to add a menu item for the renderer to the **Debug** submenu, a batch render method has to be defined with debug output supported. This is done using `RendererInfo::RenderMethod::setDebugOutputSupported `_. The output file extension is set using `RendererInfo::RenderMethod::setDebugOutputFileType `_. The text of the menu item is formatted as **Open .[fileExtension] Output in External Editor**, for example **Open dl .nsi Output in External Editor**. The menu item launches a render process with a render argument which specifies a target location for the render debug output (this is discussed more in the next section). Disk/batch renders do not support a partial scene graph, so a preview render method is used if **Render Only Selected Objects** is turned on in the **Scene Graph** tab. A partial scene graph here will still include the **/root** and **/root/world** locations, so it is conceptually identical to a full scene graph. A different type of partial scene graph output can be obtained using a scene graph location's context menu, where a **Debug** submenu contains menu items that follow the same signature as the one above. These menu items can be made available using `RendererInfo::RenderMethod::setSceneGraphDebugOutputSupported `_ and `RendererInfo::RenderMethod::setDebugOutputFileType `_ as before. A current limitation is that only one render method can support this feature. Debug output can be created from Python using the following functions in the ``NodeDebugOutput`` module: .. code-block:: python NodeDebugOutput.WriteRenderOutput(node, renderer, filename=None, expandProcedural=True, openInEditor=False, customEditor=None, log=None) NodeDebugOutput.WriteRenderOutputForRenderMethod(renderMethodName, node, renderer, filename=None, expandProcedural=True, log=None, openInEditor=True, customEditor=None, sceneGraphPath=None, printToConsole=False) The former method always uses the batch render method and runs synchronously (blocking). The latter one supports any render method and runs asynchronously (non-blocking). .. _debug-arguments: Creating a debug output in the render plug-in ````````````````````````````````````````````` We know that our render plug-in should create a debug output if ``renderOutputFile`` was passed as an argument to the render process (``renderboot``). The argument's value can be retrieved using `Render::RenderBase::getRenderOutputFile `_ where the value specifies the target location of the debug output. Katana will by default open the debug output file when the render process has finished. Another render argument is passed which indicates whether the bounded procedurals should be expanded which is queried using `Render::RenderBase::isExpandProceduralActive `_. .. note:: A debug output will not cancel a preview render which is in progress. However, a non-concurrent preview render will cancel a debug output process. A full debug output runs a synchronous disk render by default and therefore blocks the UI. A partial debug output using **Render Only Selected Objects** runs an asynchronous preview render which does not block the UI. .. _default-renderer: Setting the plug-in as the default renderer in Katana ----------------------------------------------------- Setting the default renderer in Katana is done using the environment variable ``DEFAULT_RENDERER=``. .. _linking-plugins: Linking the plug-in to its corresponding renderer info plug-in -------------------------------------------------------------- The name used to register the render plug-in should be returned in the renderer info plug-in through `RendererInfoBase::getRegisteredRendererName `_. This tells Katana how to namespace attribute data provided by the renderer info plug-in (e.g. shaders), in such as way that it can be easily retrieved by the render plug-in. .. _configuration-nodes: Adding configuration nodes -------------------------- GenericAssign nodes are useful to expose a set of scoped attributes which can be used to specify renderer-specific properties such as global settings. These nodes are created using an XML file which contains the attributes and corresponding UI hints, as well as the scene graph scope. The XML file has to reside in a directory called ``GenericAssign`` where it will be picked up and used to automatically register a node whose name matches the XML filename. .. _global-settings: Configuring global settings (GlobalStatements) ```````````````````````````````````````````````````````````` A typical use for a configuration node is to declare global options which apply during the render process. The convention in Katana is to scope these global settings at the root of the scene graph under a group attribute named **GlobalStatements**. A fixed CEL statement is used to prevent users from being able to scope the global attributes arbitrarily. The following example shows how the GenericAssign node is configured to get this behaviour, as well as providing a simple attribute setup which utilises widget hints and conditional visibility: .. code-block:: xml Some help text ... Here, we declared a closed group which encapsulates all bucket settings where a popup list offers a choice of three bucket orders and a conditional property is shown if the **spiral** value is selected. The `Render::GlobalSettings `_ utility class can be extended where it retrieves the global statements based on Katana's naming convention. The class can then freely be used to parse the attribute data and encapsulate global settings related functions. .. _object-settings: Configuring object settings (Statements) `````````````````````````````````````````````````````` GenericAssign nodes can also be used to configure attributes for arbitrary scene graph locations based on a CEL statement. This allows assigning attribute data on a per object basis while traversing the scene graph. By convention, these attributes can be assigned to any scene graph location below (and including) the world location, where the attributes are grouped under **Statements** and can be inherited. The following example shows how to configure the scope as well as setting up a page which encloses a set of attributes (differently to a group attribute): .. code-block:: xml Some help text. ... ... .. _id-passes: Sending ID pass data to Katana ------------------------------ There are two aspects in terms of getting ID pass data to Katana: - Assign an integer value to a renderable scene graph location and advertise the mapping between the two to Katana. - Render the ID pass and send the single channel image to Katana. The latter is naturally expressed as a channel in the :doc:`Display Driver API <../DisplayDriver>` so this section will focus on the former. `Render::IdSenderInterface `_ is a utility class which handles the ID communication with Katana. It is used to both get a unique integer ID from Katana as well as sending the ID values and their corresponding scene graph location names to Katana. Example: .. code-block:: c++ int64_t nextId; int64_t maxId; // Get an instance of the ID sender interface from the factory Render::IdSenderInterface* idSender; idSender = Render::IdSenderFactory::getNewInstance(getKatanaHost(), frameID); // Get the next unique ID integer value idSender->getIds(&nextId, &maxId); // Use nextId with the scene graph iterator (sgi) location // Send the ID and scene graph location path idSender->send(nextId, sgi.getFullPath().c_str()); A ``frameID`` as used above can be retrieved from a `RenderSettings::ChannelBuffer `_ object, e.g.: .. code-block:: c++ RenderSettings::ChannelBuffers channelBuffers; renderSettings.getChannelBuffers(channelBuffers); // Here we assume the first channel buffer is valid and used int64_t frameID = convertToInt(channelBuffers[0].bufferId); .. _render-threads: Setting the number of render threads ------------------------------------ There are two conventions used in Katana to declare the number of render threads for a launched render process: - Using a **renderSettings.renderThreads** attribute. The function `Render::RenderSettings::applyRenderThreads `_ can be used to apply the value if it has been set. - UI preferences and batch command line argument. Both override values are sent to the render process as a ``threads`` argument where it can be applied using `Render::RenderBase::applyRenderThreadsOverride `_. Note that this value should always override every other thread value as the user has explicitly requested it. - UI mode: The **interactiveRenderThreads3D** value is sent to the render process if **interactiveRenderThreadOverride** is set to **Yes** in the **Preferences** dialog. - Batch mode: The thread count is set using the ``--threads3d`` argument when launching Katana where the value is sent to the render process. Customizing the Network Material Group Context Layered Menu ----------------------------------------------------------- A user, while viewing the node graph from within a NetworkMaterialCreate context, can press the **Tab** key to open a layered menu with an entry for each shading node type of the currently selected render plug-in. The user can switch between which of the render plug-ins is currently selected by pressing **Shift+Tab** which opens an additional layered menu populated with one entry per installed render plugin. By default, the **Tab** menu is populated with the node types of the selected render plug-in and a subset of Katana node types. Customization of the entries in the **Tab** menu can be achieved by first instantiating and then registering a ``LayeredMenu`` as described in the `LayeredMenuAPI <../../Scripting/CustomizingUserInterface/LayeredMenu.html>`_. Functions have been provided to get and set the display name and color for a render plug-in. The name and color can be set using: `RenderingAPI.RenderPlugins.SetRendererPluginDisplayName() <../../Scripting/RenderingAScene/RenderingAPI.html#RenderingAPI.RenderPlugins.SetRendererPluginDisplayName>`_ `RenderingAPI.RenderPlugins.SetRendererPluginDisplayColor() <../../Scripting/RenderingAScene/RenderingAPI.html#RenderingAPI.RenderPlugins.SetRendererPluginDisplayColor>`_ The name and color can be retrieved using: `RenderingAPI.RenderPlugins.GetRendererPluginDisplayName() <../../Scripting/RenderingAScene/RenderingAPI.html#RenderingAPI.RenderPlugins.GetRendererPluginDisplayName>`_ `RenderingAPI.RenderPlugins.GetRendererPluginDisplayColor() <../../Scripting/RenderingAScene/RenderingAPI.html#RenderingAPI.RenderPlugins.GetRendererPluginDisplayColor>`_ An example script is included in the Katana distribution: ``plugins/Src/Resources/Examples/UIPlugins/RenderPluginsExamples.py``