Overview
While OpenUSD can be used to describe many kinds of data, its primary and most-used domain is describing 3D scenes, and visualizing or rendering those scenes is a key process. Interactive “viewport” or browser renders can often give a reasonable approximation of the scene’s content with minimal instruction to a renderer. But for producing “film quality” or “final quality” renders of complicated scenes with large numbers of interacting elements, special geometric, volumetric, and shading effects, studios routinely need to provide much more detailed instructions to a “final quality” renderer. Such instructions might include complex configuration needed to decompose the scene for a number of different “render passes” that each produce outputs that are then composited, and post-processed into a “final frame”.
UsdRender provides schemas for specifying these instructions such that they can be delivered to renderers in a standardized way. Renderers are responsible for applying your render configurations to the final render to the best of their ability. Renderers that support OpenUSD should have reasonable defaults that will be applied if render configuration isn’t authored in your scene.
UsdRender provides the following schemas.
RenderSettings: Encapsulates all the settings and components that tell a renderer what render settings to use, and what render output to produce (via specified RenderProducts), for a single invocation of rendering the scene.
RenderProduct: Represents a single render output artifact, such as a rendered image file or an output depth buffer. Includes render output information (e.g. artifact type and name) and which output channels (via specified RenderVars) should be incorporated into the artifact.
RenderVar: Represents a quantity or “channel” of computed output data that can vary across an output artifact, sometimes referred to in other rendering pipelines as an arbitrary output variable (AOV).
RenderPass: Represents the renderer and scene configuration for a single render pass in a multi-pass rendering workflow. For example, you might have a workflow that uses separate render passes to render the foreground and background portions of a scene, and a third pass that composites the foreground and background render output into the final frame.
Best Practices
The following are some general best practices for using UsdRender schemas.
Understand Which Schemas to Use
When configuring how to render your scene, it’s important to understand which UsdRender schemas you will need to use. Some schemas rely on other schemas to fully describe the desired configuration. For example, to fully configure the final render output artifact, you need to provide a RenderProduct, and also provide a set of RenderVars that the RenderProduct uses.
Additionally, some schemas might not be needed for your rendering workflow. For example, if your render pipeline does not use multi-pass rendering, you might not need to utilize RenderPass.
When planning out your use of UsdRender schemas, here are some things to keep in mind.
Always provide a ‘default’ RenderSettings. This RenderSettings prim will act as the default global render configuration for your scene. See Provide a Default RenderSettings for how to set the default RenderSettings path in layer metadata.
Determine if you will need to use a multi-pass rendering workflow or not. This will determine if you’ll need to define RenderPass prims. Many animation production workflows benefit from the added flexibility multi-pass workflows provide, or may require multi-pass rendering to apply certain effects. However, multi-pass workflows increase the complexity of the render process and may also increase storage requirements for the output from multiple passes. See Configuring Multi-pass Renders with RenderPass for more details on using RenderPass.
Renderers that support OpenUSD should produce a default output artifact (typically an RGB image) for a renderer invocation if no RenderProduct is specified. Renderers should also provide API schemas that apply to and extend UsdRender schemas such that a single default RenderSettings can be usefully consumed by any renderer. However, you may need to override configuration specified in your RenderSettings for a particular output, or specify output channels (color, normal, alpha, etc.) for your output, or even specify renderer-specific configuration for the output (special output compression settings, etc.). If you need this level of output configuration, you’ll need to provide RenderProduct and RenderVar prims with the appropriate configuration values. See Working with RenderProduct and Understanding RenderVars for more details on using RenderProduct and RenderVar.
Group UsdRender Prims
As a general best practice, group all your UsdRender prims (RenderSettings, RenderProduct, RenderVar, RenderPass) in a scene under a common root prim named “Render”. This encapsulates render-related specifications from the rest of your scene data so that, even in large scenes, render specifications can be accessed efficiently using features like UsdStage masking. The following example has two RenderSettings and a RenderProduct grouped under a Scope “Render” prim.
def Scope "Render"
{
def RenderSettings "PrimarySettings" {
rel products = </Render/PrimaryProduct>
int2 resolution = (512, 512)
}
def RenderSettings "PrimarySettingsRaw" {
rel products = </Render/PrimaryProduct>
int2 resolution = (1024, 1024)
uniform token renderingColorSpace = "raw"
}
def RenderProduct "PrimaryProduct" {
token productName = "/scratch/tmp/render000009.exr"
}
}
Provide a Default RenderSettings
It’s possible to have multiple RenderSettings prims in a given stage. You might have a stage that needs to provide separate RenderSettings for renders for different purposes (preview, final, etc.). As RenderSettings provide the global render configuration for a scene, it’s important to clarify what the “default” RenderSettings is.
Communicate to a renderer what the default RenderSettings is by using the
renderSettingsPrimPath metadata in your stage’s root layer, providing a
path to a RenderSettings prim. The following example sets the default
RenderSettings to “/Render/PrimarySettings”.
#usda 1.0
(
renderSettingsPrimPath = "/Render/PrimarySettings"
)
Designate a Render Camera
RenderProduct and RenderSettings can designate the
render camera used for rendering via setting the
camera relationship to a Camera prim. The Camera prim determines the
visual composition of the scene as an image, and represents creative choices
distinct from the technical render settings used to configure image generation.
This is why some attributes originate from the camera and others (such as pixel
resolution) are expressed separately as render settings, and may vary per
RenderProduct.
The following example has a “PrimarySettings” RenderSetting with /World/main_cam configured as the render camera, and a second RenderSettings, “StereoSettings” (also using /World/main_cam). Additionally, one of the RenderProducts (“StereoProductLeft”) that “StereoSettings” includes overrides the render camera from “StereoSettings” to use /World/stereo_cam/cam_left just for that product output.
def Scope "Render" {
def RenderSettings "PrimarySettings" {
rel camera = </World/main_cam>
# ... other settings ...
}
def RenderSettings "StereoSettings" {
# stereo render products will override camera as needed
rel camera = </World/main_cam>
rel products = [ </Render/StereoProductRight>, </Render/StereoProductLeft> ]
# ... other settings ...
}
def RenderProduct "StereoProductRight" {
rel camera = </World/stereo_cam/cam_right>
# ... other settings ....
}
def RenderProduct "StereoProductLeft" {
rel camera = </World/stereo_cam/cam_left>
# ... other settings ....
}
}
Using UsdRender to Control How Prims Are Rendered
UsdRender schemas provide several properties to control which prims get rendered for a render invocation or a render pass, as well as properties to control how prims get rendered, e.g. controlling which material bindings are used during a render.
Configuring Imageable Purpose
USD provides several ways to control prim visibility
in a scene. One approach specifically designed to be used at render-time is
imageable purpose, which lets you author a
purpose attribute on an imageable prim, and then configure which
purposes are used at render-time.
To configure which purposes to use for a render, use the
includedPurposes attribute on RenderSettings to list the purposes that
will be rendered.
Note
Prims that have no authored or inherited purpose (i.e. the prim has the fallback “default” purpose) are always rendered. Renderers are expected to render default purpose prims for all render situations.
The following example has includedPurposes set to ["proxy", "render"],
so only prims with purpose set to “proxy” or “render”, or prims that
have “inherited” either of those imageable
purposes, will be rendered. Note that prims with no authored or inherited
purpose (i.e. the “default” fallback purpose) will always be rendered.
def RenderSettings "PrimarySettings" {
uniform token[] includedPurposes = ["proxy", "render"]
}
Configuring materialBindingPurposes
Material binding purposes let you specify, for the same Gprim, different material bindings for different purposes, e.g. a material binding for a “full” render, vs a lightweight material binding for a “preview” render. See Using Material Binding Purpose for more details.
Configure which material binding purposes are used for a render via the
materialBindingPurposes RenderSettings attribute. For example, you might
have separate RenderSettings for a final render, using the “full” purpose, and
for your DCC tool, using the “preview” purpose.
def RenderSettings "FinalRenderSettings"
{
uniform token[] materialBindingPurposes = ["full"]
...
}
def RenderSettings "DefaultRenderSettings"
{
uniform token[] materialBindingPurposes = ["preview"]
...
}
In most scenarios, materialBindingPurposes should be set to a single
value. Note that if the specified material binding purpose is not found,
renderers should attempt to fall back to an all purpose binding, if present.
Using RenderPass Collections
If you are using a multi-pass rendering workflow, RenderPass provides additional
properties to control how prims are rendered through the
renderVisibility, cameraVisibility, prune, and
matte collections.
renderVisibility: This collection describes which prims are visible to the renderer for this pass. Use this collection if you have separate passes for different sets of objects in the stage (e.g. separate foreground and background passes), or passes that only apply to specific types of objects. Note that this collection acts as a “mask” and does not override authoredvisibilityvalues – for example, if an imageable prim in the scene has (or inherits)token visibility = "invisible", including this prim in this collection will not cause the prim to be visible.cameraVisibility: Similar torenderVisibility, however this collection describes which prims are visible to the main render camera (set in RenderSettings or RenderProduct) for this pass. Note that objects that are not in this collection should still participate in other light paths such as shadowing, reflections, and refraction. Similar torenderVisibility, this collection does not override authoredImageable.visibilityvalues. Also, note that this collection is applied afterrenderVisibility, so prims in this list, but not inrenderVisibilitywill not be visible to the renderer.prune: This collection defines which prims should be removed during this render pass. This is provided as an alternative to collections likerenderVisibilityandcameraVisibility, to support scenarios such as batch rendering, or when renderers can’t support visibility toggles. The tradeoff is that pruning is likely to be a more expensive operation than visibility toggling and therefore not appropriate for certain renderer scenarios, such as renders for interactive workflows.matte: This collection describes which prims should be rendered as “matte objects” (objects with zero alpha) during this render pass. Note that this list is applied afterrenderVisibility, so prims in this list, but not inrenderVisibilitywill not be visible to the renderer, and therefore not rendered as matte.
Note that all of these collections are
USD collections and therefore can be defined
using relationship-mode or pattern-based collections. The following example
defines two RenderPasses, a “foreground” pass that uses a relationship-mode
collection for renderVisibility, and a “background” pass that uses a
pattern-based collection for renderVisibility.
def RenderPass "foreground"
{
rel renderSource = </Render/PrimarySettings>
uniform bool collection:renderVisibility:includeRoot = false
prepend rel collection:renderVisibility:includes = [
</World/characters>,
</World/sets/Kitchen/Table_grp>,
]
}
def RenderPass "background"
{
rel renderSource = </Render/PrimarySettings>
pathExpression collection:renderVisibility:membershipExpression = "/World/Background* + /World/OLD_Background*"
}
Working with RenderProduct
A RenderProduct represents a single render output artifact, such as a rendered image file or an output depth buffer. RenderProducts can override some of the configuration in a RenderSetting (such as the camera), but also have product-specific settings, such as the output “product name” (for a rendered image, the image filename).
The following example RenderProduct configures productType and
productName, but also overrides any authored RenderSettings values
in use for camera and resolution.
def RenderProduct "PrimaryProduct" {
# --- product-specific ---
uniform token productType = "deepRaster"
uniform token productName = "/output/render000009.png"
# --- overrides ---
# For this product, override our RenderSettings camera and resolution
rel camera = </Cameras/stereo_cam>
uniform int2 resolution = (3840, 2160)
# ...other settings...
}
Note
Overriding settings such as camera and resolution may, for some
renderers, necessitate multiple independent renders. Other renderers may be
able to vary these settings over RenderProducts computed in a single render.
A RenderProduct must also specify one or more RenderVars, each of which represent a channel of computed output data, as described in Understanding RenderVars below.
Understanding RenderVars
A RenderVar represents a quantity or “channel” of computed output data that can vary across an output artifact. A RenderProduct may contain multiple channels of data representing related values or variables sampled by a render process. The RenderVar prim specifies what values the renderer should output and how the renderer should produce them.
Examples of render variables include geometric measurements such as camera-space depth, quantities emitted by material shaders, light path expressions (LPE’s), and quantities intrinsic to the renderer such as computation time per pixel. Note that USD does not yet enforce a set of universal RenderVar names and formats, so renderer-specific RenderVars are expected. In the following example, the “PrimaryProduct” RenderProduct specifies four RenderVars representing channels for color, alpha, directDiffuse, and a general ID value.
def RenderProduct "PrimaryProduct" {
rel camera = </World/main_cam>
token productName = "/scratch/tmp/render000009.exr"
rel orderedVars = [
</Render/Vars/color>,
</Render/Vars/alpha>,
</Render/Vars/directDiffuse>,
</Render/Vars/id>
]
}
def Scope "Vars"
{
def RenderVar "color" {
string sourceName = "Ci"
}
def RenderVar "alpha" {
token dataType = "float"
string sourceName = "a"
}
def RenderVar "directDiffuse" {
string sourceName = "C<RD>[<L.>O]"
token sourceType = "lpe"
}
def RenderVar "id" {
token dataType = "int"
string sourceName = "id"
}
}
Configuring Multi-pass Renders with RenderPass
Use RenderPass prims to configure multi-pass renders. RenderPass prims encapsulate individual passes in a multi-pass rendering workflow, letting you specify a different RenderSetting for each render pass. For example, you might have a workflow that uses separate render passes and settings to render the foreground and background portions of a scene, and a third pass that composites the foreground and background render output into the final frame. RenderPass can point to a RenderSettings for render configuration for the pass, or point to product-specific configuration for external renderers that may not describe a render in terms of RenderSettings (e.g., compositing applications). In addition to organizing the different renders and processes (such as denoising and compositing) that collectively produce a “final frame”, RenderPass codifies the dependencies between passes. A single pass generally represents not just a single set of products, but a sequence of temporally varying frames of outputs that depend on temporally varying inputs.
RenderPass also lets you use collections of prims to control prim visibility or behavior for the given pass. See Using RenderPass Collections for more details on these collections.
The following example shows three RenderPasses. A “foreground” pass and a
“background” pass are specified that use RenderMan and the “PrimarySettings”
RenderSettings configuration, but specify different parts of the stage to render
using the RenderPass renderVisibility collection. A final “composite”
pass is also specified that uses Nuke and takes the results from the other two
passes as inputPasses. Note that the nuke:writeNode attribute
and Nuke renderSource are hypothetical examples that would be associated
with a Nuke-supplied API schema applied to the “composite” RenderPass prim –
USD does not provide any default Nuke render configuration support.
def Scope "Render"
{
...settings and products...
def Scope "Passes"
{
def RenderPass "foreground"
{
token passType = "prman"
rel renderSource = <Render/PrimarySettings>
string[] command = ["prman"]
uniform bool collection:renderVisibility:includeRoot = false
prepend rel collection:renderVisibility:includes = [
</World/characters>,
</World/sets/Kitchen/Table_grp>,
]
}
def RenderPass "background"
{
token passType = "prman"
rel renderSource = <Render/PrimarySettings>
string[] command = ["prman"]
uniform bool collection:renderVisibility:includeRoot = true
prepend rel collection:renderVisibility:excludes = [
</World/characters>,
</World/sets/Kitchen/Table_grp>,
]
}
def RenderPass "composite"
{
token passType = "nuke"
asset fileName = @composite.nk@
# this nuke-namespaced property might come from a hypothetical Nuke-supplied API schema
string nuke:writeNode = "WriteFinalComposite"
rel renderSource = </Render/Passes/composite.nuke:writeNode>
string[] command = ["nuke", "-x", "-c", "32G"]
rel inputPasses = [
</Render/Passes/foreground>,
</Render/Passes/background>
]
}
}
}