Scene Partitions#

Overview#

Scene partitions let different cameras render isolated environments from one stage. You can use them to simulate independent robot environments while keeping shared, unpartitioned content visible to every camera.

ovrtx enables scene partitioning when it creates the renderer.

Three matching camera views showing the left orange cube, right blue torus, and both objects

The cameras render the left orange cube, the right blue torus, and both objects from the same viewpoint. The ground and studio environment remain visible in all three views.#

Creating Scene Partitions#

Select a camera’s partition with omni:scenePartition. Assign geometry with the primvars:omni:scenePartition token primvar. Primvars are inherited, so you can assign an entire environment on its top-level Xform.

USD declaration

Description

custom token omni:scenePartition

Author on a Camera to select the named partition that it renders.

token primvars:omni:scenePartition

Author on geometry or an ancestor Xform to assign that content to a named partition.

These are custom Omni attributes. They do not currently have generated schema accessors, so author them with their exact names and token types.

  • A camera sees geometry assigned to its selected partition.

  • A camera does not see geometry assigned to another partition.

  • Unpartitioned geometry remains visible to every camera.

This excerpt from the example assigns the two objects to separate partitions and selects them with the left and right cameras. The third camera has no partition selector.

Both tabs author the same scene objects. Asset paths are relative to the example directory; the Python snippet assumes an existing USD stage.

def Xform "World"
{
    def Xform "Left" (
        prepend references = @../../../tests/docs/data/cube.usd@
    )
    {
        token primvars:omni:scenePartition = "left" (interpolation = "constant")
        double3 xformOp:translate = (-115, 0, 0)
        uniform token[] xformOpOrder = ["xformOp:translate"]
        over "Cube"
        {
            color3f[] primvars:displayColor = [(0.9, 0.12, 0.025)]
        }
    }

    def Xform "Right" (
        prepend references = @../../../tests/docs/data/torus.usd@
    )
    {
        token primvars:omni:scenePartition = "right" (interpolation = "constant")
        double3 xformOp:translate = (115, 75, 0)
        float3 xformOp:rotateXYZ = (90, 0, 0)
        uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ"]
        over "Torus"
        {
            color3f[] primvars:displayColor = [(0.025, 0.3, 0.85)]
        }
    }

    def Xform "Ground" (
        prepend references = @../../../tests/docs/data/ground.usd@
    )
    {
        double3 xformOp:scale = (20, 1, 20)
        uniform token[] xformOpOrder = ["xformOp:scale"]
    }

    def DomeLight "Environment"
    {
        float inputs:intensity = 1000
        asset inputs:texture:file = @../../../tests/docs/data/studio.exr@
        token inputs:texture:format = "latlong"
    }

    def Camera "LeftCamera"
    {
        custom token omni:scenePartition = "left"
        float focalLength = 38
        float2 clippingRange = (1, 100000)
        double3 xformOp:translate = (0, 230, 850)
        float3 xformOp:rotateXYZ = (-12, 0, 0)
        uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ"]
    }

    def Camera "RightCamera"
    {
        custom token omni:scenePartition = "right"
        float focalLength = 38
        float2 clippingRange = (1, 100000)
        double3 xformOp:translate = (0, 230, 850)
        float3 xformOp:rotateXYZ = (-12, 0, 0)
        uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ"]
    }

    def Camera "BothCamera"
    {
        float focalLength = 38
        float2 clippingRange = (1, 100000)
        double3 xformOp:translate = (0, 230, 850)
        float3 xformOp:rotateXYZ = (-12, 0, 0)
        uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ"]
    }
}
UsdGeom.Xform.Define(stage, "/World")

left = UsdGeom.Xform.Define(stage, "/World/Left")
left.GetPrim().GetReferences().AddReference("../../../tests/docs/data/cube.usd")
UsdGeom.PrimvarsAPI(left).CreatePrimvar(
    "omni:scenePartition", Sdf.ValueTypeNames.Token, UsdGeom.Tokens.constant
).Set("left")
left.AddTranslateOp().Set(Gf.Vec3d(-115, 0, 0))
UsdGeom.Mesh(stage.GetPrimAtPath("/World/Left/Cube")).CreateDisplayColorAttr(
    [(0.9, 0.12, 0.025)]
)

right = UsdGeom.Xform.Define(stage, "/World/Right")
right.GetPrim().GetReferences().AddReference("../../../tests/docs/data/torus.usd")
UsdGeom.PrimvarsAPI(right).CreatePrimvar(
    "omni:scenePartition", Sdf.ValueTypeNames.Token, UsdGeom.Tokens.constant
).Set("right")
right.AddTranslateOp().Set(Gf.Vec3d(115, 75, 0))
right.AddRotateXYZOp().Set(Gf.Vec3f(90, 0, 0))
UsdGeom.Mesh(stage.GetPrimAtPath("/World/Right/Torus")).CreateDisplayColorAttr(
    [(0.025, 0.3, 0.85)]
)

ground = UsdGeom.Xform.Define(stage, "/World/Ground")
ground.GetPrim().GetReferences().AddReference("../../../tests/docs/data/ground.usd")
ground.AddScaleOp(UsdGeom.XformOp.PrecisionDouble).Set(Gf.Vec3d(20, 1, 20))

environment = UsdLux.DomeLight.Define(stage, "/World/Environment")
environment.CreateIntensityAttr(1000)
environment.CreateTextureFileAttr("../../../tests/docs/data/studio.exr")
environment.CreateTextureFormatAttr("latlong")

for name, partition in (("Left", "left"), ("Right", "right"), ("Both", None)):
    camera = UsdGeom.Camera.Define(stage, f"/World/{name}Camera")
    if partition is not None:
        camera.GetPrim().CreateAttribute(
            "omni:scenePartition", Sdf.ValueTypeNames.Token, custom=True
        ).Set(partition)
    camera.CreateFocalLengthAttr(38)
    camera.CreateClippingRangeAttr(Gf.Vec2f(1, 100000))
    camera.AddTranslateOp().Set(Gf.Vec3d(0, 230, 850))
    camera.AddRotateXYZOp().Set(Gf.Vec3f(-12, 0, 0))

To let the third camera see both objects, the example sets omni:rtx:scenePartitioning:showAllPartitionsByDefault to true on /Render/OmniverseGlobalRenderSettings. It also sets omni:rtx:scenePartitioning:excludeBackgroundDomeLightInPartition to false on each RenderProduct to retain shared dome lighting.

Supported Object Types#

Meshes, curves, points, OpenVDB volumes, and ray-traced Gaussian splats support scene partitioning through primvars:omni:scenePartition. Geometry instances also carry their own partition assignment.

The following limitations apply:

  • Neural-reconstruction volumes (NuRec/NRE): their separate compositing paths do not filter volumes by the camera’s scene partition. Assigning a partition does not isolate these volumes.

Warning

Deforming geometry may be clipped to its undeformed bounds.

Example#

See Python: Camera Scene Partitions for a runnable example.

Further Reading#