Generated banner for Omniverse Realtime Viewer
Generated logo for Omniverse Realtime Viewer

Omniverse Realtime Viewer

NVIDIAOfficialCreated by NVIDIA Omniverse

  • Skill
  • Tool integration
  • Software development
  • Apache-2.0
  • Medium risk

From the author

Use as the top-level router for Omniverse Realtime Viewer USD apps, XR delivery, SPG GPU processing, stylized rendering, and focused viewer reference documents.
— the description by NVIDIA Omniverse

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

The Omniverse Realtime Viewer skill acts as a central routing and architectural framework for developing USD-based 3D viewer applications. It provides a structured approach to composing rendering, scene state, and delivery mechanisms, ensuring that developers can integrate OVRTX rendering, OVStage scene management, and various UI or streaming adapters effectively. The skill emphasizes a modular architecture where the viewer runtime, UI/input adapters, and delivery systems are decoupled, allowing for consistent behavior across local and streamed environments.

What it does

  • Provides a routing architecture for USD viewer apps, XR delivery, and GPU-accelerated rendering.
  • Defines the separation of concerns between viewer runtime, UI adapters, and delivery systems.
  • Offers guidance on integrating OVRTX for high-fidelity 3D rendering.
  • Establishes conventions for camera interaction, scene state management, and input handling.
  • Supports the implementation of specialized features like stylized rendering and CAE/CFD visualization.
  • Provides protocols for agent-to-UI communication and scene control.

When to use it

  • When starting the development of a new USD-based realtime viewer application.
  • When integrating advanced rendering features like stylized visualization or AOV switching.
  • When building XR or spatial editing capabilities into an existing Omniverse application.
  • When you need to standardize the architecture for local and streamed viewer deployments.
  • When implementing chat-driven scene control or agent-based inspection of a 3D scene.

Advantages

  • Promotes a clean, modular architecture that separates rendering logic from UI and transport.
  • Ensures consistent behavior across different delivery methods, such as local windows or WebRTC streaming.
  • Provides clear paths for extending functionality without disrupting the core viewer runtime.
  • Facilitates the integration of complex simulation and visualization data into the viewer.
  • Standardizes conventions for scene state, selection, and camera control.

Usage examples

“How do I set up a basic USD viewer application using the Omniverse Realtime Viewer architecture?”
The agent will guide you to start with the viewer app reference, resolve dependencies, and implement the core runtime owner to manage the OVStage and OVRTX renderer.
“I need to add stylized rendering to my existing viewer. Where should I start?”
The agent will direct you to the stylized visualization references to implement the required USD/Lua/CUDA nodes within your existing runtime owner.
“How can I enable an AI agent to control the viewer scene?”
The agent will point to the A2UI Scene Copilot reference to implement an operation interface that allows the agent to send commands through the existing runtime owner.
“How do I switch between different render outputs like beauty and depth in my viewer?”
The agent will explain how to use the AOV switching reference to map RenderVars and update the RenderProduct's orderedVars through the runtime owner.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/nvidia/omniverse-realtime-viewer/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip omniverse-realtime-viewer.zip -d .agents/skills

Documentation by NVIDIA Omniverse

<!-- SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -->

<!-- SPDX-License-Identifier: Apache-2.0 -->

Omniverse Realtime Viewer

Build responsive USD viewers by composing OVRTX rendering, OVStage scene state, and the delivery/UI libraries appropriate to the app. Use this package for viewer architecture and interaction; use each selected SDK's upstream guidance for installation and exact release-specific APIs.

Start With The App

  1. When delivery is explicit, select its recipe through references/routing.md and follow its current phase. Otherwise start with references/usd-viewer-app/README.md. For an existing app, keep its architecture and read only the references for the requested change. Broad app, conventions and UI catalogs are lookups as needed.

  2. Resolve the selected libraries through references/dependencies/README.md. Preserve the project's manifest unless an upgrade is requested.

  3. Build a working frame and camera interaction in the chosen shell, then add the requested capabilities. Read references/conventions.md for shared input, camera, selection, and protocol conventions.

  4. Use references/viewer-ux-workflow/README.md for broader interface design. Loading, selection feedback, errors, and input responsiveness belong in the first working app, alongside rendering.

  5. Validate the actual delivery path with references/validation.md. A browser screenshot does not validate a native window or a different transport.

Architecture

OwnerResponsibility
Viewer runtimeOwn the live OVStage, publication clock, OVRTX renderer, scene changes, and render stepping.
UI and input adapterDisplay frames and copied app state; send user intent to the runtime owner.
Storage/query/simulation workersPerform their assigned work and return results through the selected integration boundary.
Delivery adapterPresent locally or stream through ovstream; respect frame ownership and backpressure.

Use OVRTX for USD/3D rendering. Browser apps display its WebRTC video; local apps present its output through their native shell. A missing GPU means rendering is unverified, not a reason to substitute a browser 3D renderer.

Keep viewer cameras, settings, effects, and transient edits in app-owned session or runtime state so the source USD stays intact. Serialize scene mutation and render stepping through the runtime owner. Read references/ovstage-ovrtx-integration/README.md for that loop, then the population or data-plane references when implementing scene loading or attribute writes.

Keep expensive loading and metadata work off the UI and render threads. Keep transport details behind a delivery adapter so camera, selection, properties, and loading state can be reused across local and streamed shells.

Add Capabilities As Needed

Use references/routing.md to select camera/picking, scene management, render settings, cloud assets, simulation, editor tools, deployment, or CAE/CFD visualization. Read the selected capability's implementation flow and its linked contract; the full reference catalog is not a prerequisite for a first frame.

For simulation, use the routing reference's Simulation Ecosystem section plus references/realtime-sim-integration/README.md. For UI-driven forces or control, add references/interaction-round-trip/README.md.

For example scenes, use references/usd-sample-data/README.md. When a runtime failure needs diagnosis, use references/troubleshooting/README.md and the selected SDK's matching release guidance before changing the architecture.

Solid Modeling: Upstream First

For usd-brep, exact BRep, solid modeling, kernel failures, CAD topology, or BrepArray questions, first read references/dependencies/README.md → USD BRep. Follow the owning repository's current agent guidance, skills, operation docs, and package API before selecting local integration references. Discover the upstream router rather than assuming skill names or directory layouts remain fixed. Upstream owns modeling semantics, schema rules, tuning, and diagnostics; this package owns OVRTX display, worker handoff, selection mapping, and publication timing.

GPU Processing And Stylized Viewers

For custom GPU processing, chained outputs and lens sampling, read references/spg-processing/README.md. For toon, ink, technical/X-Ray styles, geometry strokes or full wireframe, continue to references/stylized-visualization/implementation.md. Its USD/Lua/CUDA starting node and geometry/depth/composite build sequence work with the selected viewer recipe; use references/stylized-visualization/style-recipes.md for the preset math and references/stylized-visualization/README.md for supporting contracts. Implement the result inside the existing OVRTX/runtime owner and deliver the published processed output. These instructions do not require a sample checkout or a prior stylized-rendering implementation.

For an optional usd-brep geometry source, first read references/dependencies/README.md → USD BRep and the owning repository's current agent guidance, skills, and operation docs; then read references/stylized-visualization/usd-brep-source.md for the display boundary.

XR Viewers

For headsets, immersive viewers, or spatial editors, start with references/xr-viewer-recipe/README.md. Resolve SDK setup and contracts through references/dependencies/ovxr.md; the library owns OpenXR lifecycle and frame submission. For controllers, hands, navigation, or scene tools, add references/xr-spatial-input/README.md and references/ovxr-python-interactions/README.md. For projection-integrated UI, read references/ovxr-composition-ui/README.md and references/ovxr-composition-ui-validation/README.md.

Files

references/cae-cfd-visualization/usd-authoring-and-materials.md

<!-- SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -->

<!-- SPDX-License-Identifier: Apache-2.0 -->

CAE/CFD USD Authoring And Materials

Triggers

Use this reference when turning CAE/CFD operator output (a surface, iso-surface, slice, point cloud, or streamlines plus a scalar field) into a renderable, colored USD prim for an ovrtx viewer: composing the viewer-root USDA, authoring UsdGeomMesh/UsdGeomPoints/UsdGeomBasisCurves geometry, baking a colormap into primvars:displayColor, wiring a UsdPreviewSurface, and adding the lights the path tracer needs. Read references/conventions.md and references/ovrtx-rendering/README.md first for the render loop and import order.

This document covers exactly one step: operator output (points + a field) → a renderable, colored USD prim inside a composed viewer stage. It does not cover solving, meshing, or field extraction; those produce the points, faceVertexCounts/faceVertexIndices (or curveVertexCounts), and per-vertex scalar field this document consumes.

Author The Viewer USDA As A Text String

Compose the whole viewer-root stage as a USDA text string and hand it to OVStage with population.open_usd_from_string(stage, usda, ordinal=N, domains=PopulationDomain.RENDERING). Do not import pxr / usd-core in the render process to build the stage in memory. Mixing usd-core with the ovrtx bundled USD in the wrong order causes duplicate-symbol crashes and MDL resolver failures (see references/ovrtx-rendering/README.md import-order notes). Text authoring keeps this process pxr-free and lets OVStage own composition and population.

Implement a small application-owned USDA builder from numpy arrays. Keep it pxr-free and split it into block builders for the session header, render/camera, lights/materials, and each geometry prim. Its structure, top to bottom under one session scope:

/Session (Scope, defaultPrim, upAxis="Y")
  Cameras/Main         UsdGeomCamera   (resolution-derived aperture, xform)
  Render/Viewport      RenderProduct   (uniform int2 resolution)
  Render/Vars/LdrColor RenderVar       (sourceName="LdrColor")
  Render/Settings      RenderSettings
  Lights/Dome, Lights/Key   DomeLight + DistantLight
  Looks/FieldMat       Material        (UsdPreviewSurface + PrimvarReader)
  Geometry/<prim>      Mesh | Points | BasisCurves  (+ displayColor primvar)

Header and root scope:

#usda 1.0
(
    defaultPrim = "Session"
    doc = "cae viewer root (colormap=viridis, field=Pressure)"
    upAxis = "Y"
)

def Scope "Session"
{
    # ... camera, render, lights, looks, geometry blocks ...
}
Camera And Render Blocks

The RenderProduct resolution is viewer-owned and must be set in this composed layer before the stage loads; the RenderProduct path is the same path passed to renderer.step(render_products={...}). Keep it fixed for the session (typically 1920x1080 for streaming). Camera matrices are row-major with translation in row 3 (right, up, -forward, eye).

    def Xform "Cameras"
    {
        def Camera "Main"
        {
            float2 clippingRange = (0.1, 100000)
            float focalLength = 24
            float horizontalAperture = 20.955
            float verticalAperture = 11.7872
            matrix4d xformOp:transform = ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1) )
            uniform token[] xformOpOrder = ["xformOp:transform"]
        }
    }
    def Scope "Render"
    {
        def RenderProduct "Viewport"
        {
            rel camera = </Session/Cameras/Main>
            uniform int2 resolution = (1920, 1080)
            rel orderedVars = [ </Session/Render/Vars/LdrColor> ]
        }
        def Scope "Vars"
        {
            def RenderVar "LdrColor"
            {
                string sourceName = "LdrColor"
                token sourceType = "raw"
            }
        }
        def RenderSettings "Settings"
        {
            rel products = [ </Session/Render/Viewport> ]
            rel camera = </Session/Cameras/Main>
            uniform int2 resolution = (1920, 1080)
        }
    }

Request only LdrColor for a basic viewer. verticalAperture is derived from horizontalAperture * height / width so pixels stay square.

Geometry Prims By Representation

Pick the prim type from the operator representation:

RepresentationUSD prim
external faces / iso-surface / sliceUsdGeomMesh
point cloudUsdGeomPoints
streamlinesUsdGeomBasisCurves

All three carry the same two field primvars (see next section): primvars:displayColor (baked color) and primvars:<field> (raw scalar).

Surface / Iso / Slice — UsdGeomMesh
    def Xform "Geometry"
    {
        def Mesh "Surface"
        {
            point3f[] points = [ (0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0) ]
            int[] faceVertexCounts = [ 3, 3 ]
            int[] faceVertexIndices = [ 0, 1, 2, 0, 2, 3 ]
            uniform token subdivisionScheme = "none"
            rel material:binding = </Session/Looks/FieldMat>
            float[] primvars:Pressure = [ 0.1, 0.4, 0.9, 0.5 ] (interpolation = "vertex")
            color3f[] primvars:displayColor = [ (0.27, 0, 0.33), (0.16, 0.47, 0.56), (0.99, 0.9, 0.14), (0.13, 0.57, 0.55) ] (interpolation = "vertex")
        }
    }

subdivisionScheme = "none" renders the authored triangles/polygons directly (no Catmull-Clark). faceVertexCounts is one count per face (3 per triangle); faceVertexIndices indexes into points. In the app's USD-authoring module, map NODE-associated fields to vertex interpolation (one value per output point) and ELEMENT/cell fields to uniform (one value per output face). When an operator changes topology, use its output field association and count; do not attach the original cell array to a triangulated or extracted mesh without remapping it to the output faces. See data-and-operators.md for the geometry/field handoff.

Point Cloud — UsdGeomPoints

Use when the dataset has points and fields but no cells. widths is per-point (world units); estimate it from the bbox diagonal over sqrt(N) when the operator does not supply one.

        def Points "Surface"
        {
            point3f[] points = [ (0, 0, 0), (1, 0, 0), (1, 1, 0) ]
            float[] widths = [ 0.02, 0.02, 0.02 ]
            rel material:binding = </Session/Looks/FieldMat>
            float[] primvars:Pressure = [ 0.1, 0.4, 0.9 ] (interpolation = "vertex")
            color3f[] primvars:displayColor = [ (0.27, 0, 0.33), (0.16, 0.47, 0.56), (0.99, 0.9, 0.14) ] (interpolation = "vertex")
        }
Streamlines — UsdGeomBasisCurves

type = "linear" gives polyline curves. curveVertexCounts is the vertex count per curve; points is the concatenation of all curve vertices in order. widths is per-vertex here (interpolation = "vertex").

        def BasisCurves "Streamlines"
        {
            uniform token type = "linear"
            int[] curveVertexCounts = [ 3, 2 ]
            point3f[] points = [ (0,0,0), (1,0,0), (2,0,0), (0,1,0), (1,1,0) ]
            float[] widths = [ 0.02, 0.02, 0.02, 0.02, 0.02 ] (interpolation = "vertex")
            rel material:binding = </Session/Looks/FieldMat>
            float[] primvars:Pressure = [ 0.1, 0.4, 0.9, 0.2, 0.6 ] (interpolation = "vertex")
            color3f[] primvars:displayColor = [ (0.27,0,0.33), (0.16,0.47,0.56), (0.99,0.9,0.14), (0.28,0.14,0.46), (0.13,0.57,0.55) ] (interpolation = "vertex")
        }

Field → Color: Bake A Colormap LUT

The simplest guaranteed-to-render color path is to bake the colormap into per-vertex color and let a UsdPreviewSurface read it. Two things are authored per prim:

  1. color3f[] primvars:displayColor (interpolation = "vertex") — the baked RGB.

  2. float[] primvars:<field> (interpolation = "vertex") — the raw scalar, kept so downstream tools (legends, thresholds, re-coloring, probing) still have the physical values.

Implement the colormap in the generated app. Store a small RGB control-point table for each supported name, interpolate it to a 256-entry LUT, then normalize and index it:

import numpy as np

def apply_lut(values, vmin, vmax, lut):
    values = np.asarray(values, dtype=np.float32)
    if not np.isfinite(vmax) or not np.isfinite(vmin) or vmax <= vmin:
        return np.full((values.size, 3), 0.5, dtype=np.float32)
    unit = np.nan_to_num((values - vmin) / (vmax - vmin), nan=0.0, posinf=1.0, neginf=0.0)
    index = np.rint(np.clip(unit, 0.0, 1.0) * (len(lut) - 1)).astype(np.intp)
    return np.asarray(lut, dtype=np.float32)[index, :3]

# `lut` is a generated `(256, 3)` float32 table for the selected named colormap.
colors = apply_lut(surface.field_values, *surface.field_range, lut)

Use viridis as the default and document any additional maps that the app ships. Preserve raw field values alongside the RGB result so legends, thresholds, and recoloring remain possible.

The (N,3) colors array becomes the primvars:displayColor values and the raw surface.field_values becomes primvars:<field>.

Material And Lights

UsdPreviewSurface Reading displayColor

Author one Material under Looks whose surface shader's diffuse input is connected to a UsdPrimvarReader_float3 that reads the displayColor primvar. Bind it on each geometry prim with rel material:binding.

    def Scope "Looks"
    {
        def Material "FieldMat"
        {
            token outputs:surface.connect = </Session/Looks/FieldMat/Surface.outputs:surface>
            def Shader "Surface"
            {
                uniform token info:id = "UsdPreviewSurface"
                color3f inputs:diffuseColor.connect = </Session/Looks/FieldMat/PvReader.outputs:result>
                color3f inputs:emissiveColor = (0, 0, 0)
                float inputs:roughness = 0.65
                float inputs:metallic = 0
                token outputs:surface
            }
            def Shader "PvReader"
            {
                uniform token info:id = "UsdPrimvarReader_float3"
                string inputs:varname = "displayColor"
                float3 inputs:fallback = (0.55, 0.55, 0.55)
                float3 outputs:result
            }
        }
    }

The example authoring code also drives emissiveColor at low weight from the same reader as insurance so the colormap stays visible under weak lighting, while diffuse still gives lit shading form; plain emissiveColor = (0,0,0) with real lights is the baseline shown above.

Lights — CRITICAL GOTCHA

A path tracer renders unlit diffuse geometry BLACK. A UsdPreviewSurface with only a diffuse color contributes nothing until light hits it, so a scene with no light produces a black frame that looks like a broken render. You MUST author at least one light. Use a DomeLight (soft fill so the whole surface is lit) plus a DistantLight (key, for shading form):

    def Scope "Lights"
    {
        def DomeLight "Dome"
        {
            float inputs:intensity = 550
            float inputs:exposure = 0
            color3f inputs:color = (1, 1, 1)
        }
        def DistantLight "Key"
        {
            float inputs:intensity = 3000
            float inputs:angle = 1.5
            double3 xformOp:rotateXYZ = (-45, 25, 0)
            uniform token[] xformOpOrder = ["xformOp:rotateXYZ"]
        }
    }

If a rendered CAE frame is black despite valid geometry and colors, check for a missing light before anything else.

Beauty-Grade Fidelity: MDL Materials + HDRI Lighting

The baked-displayColor + UsdPreviewSurface path above is the right choice for the CAE scalar field itself — the streamlines/iso/slice/point-cloud geometry whose color is the data. For those prims the color must map the field faithfully and must not be reshaped by a physically-based lighting response, so keep them on the baked path.

A realtime CAE viewer often also carries non-data "beauty" geometry in the same frame: the car body under the streamlines, a wind-tunnel floor, chrome trim, a glass canopy. Those surfaces should look physically real — reflect the environment, show clearcoat and metallic flake — not read as a flat lit color. ovrtx has full USD/MDL support and bundles a large MDL material library, so bind a real MDL material to that geometry while the scientific field stays on the colormap path. The two coexist in one stage: bake displayColor for the field, bind MDL for the props. This section is the fidelity upgrade layered on top of the baseline above.

MDL Materials Via UsdShade

MDL resolution depends on OVRTX_BIN_PATH pointing at ovrtx/bin (the bundled MDL library lives under ovrtx/bin/library/mdl/). Without it, MDL surfaces render magenta — this is the single most common MDL failure. See the MDL resolution and magenta-material rows in references/ovrtx-rendering/README.md; that document owns the OVRTX_BIN_PATH / LD_LIBRARY_PATH / import-order rules and this reference does not repeat them.

An MDL material is authored with UsdShade, but its wiring differs from the UsdPreviewSurface block above: the surface output is outputs:mdl:surface (not outputs:surface), the shader's info:implementationSource is "sourceAsset", the .mdl module is named by info:mdl:sourceAsset, and the specific material inside that module is selected by info:mdl:sourceAsset:subIdentifier:

    def Material "CarMat"
    {
        token outputs:mdl:surface.connect = </Session/Looks/CarMat/S.outputs:out>
        def Shader "S"
        {
            uniform token info:implementationSource = "sourceAsset"
            uniform asset info:mdl:sourceAsset = @OmniSurfacePresets.mdl@
            uniform token info:mdl:sourceAsset:subIdentifier = "OmniSurface_CarPaintMetallic"
            token outputs:out
        }
    }

Useful bundled modules and their subIdentifier materials (any MDL in the library works — these are convenient beauty presets):

.mdl modulesubIdentifier (material)Use
OmniSurfacePresets.mdlOmniSurface_CarPaintflat automotive paint
OmniSurfacePresets.mdlOmniSurface_CarPaintMetallicmetallic-flake car paint
OmniSurfacePresets.mdlOmniSurface_TwoToneCarPaintflip/two-tone paint
OmniSurfacePresets.mdlOmniSurface_GlossyPaintglossy non-metallic paint
OmniSurfacePresets.mdlChromepolished chrome trim
OmniPBR_ClearCoat.mdl(module material)clearcoat over a PBR base
OmniSurface.mdl / OmniPBR.mdl(general)general PBR body / props
OmniGlass.mdl(general)glass canopy / transparent parts

Bind the material with MaterialBindingAPI + rel material:binding, exactly like the field material. The key rule when mixing bound and inherited materials:

A material:binding authored on a prim overrides a binding inherited from an ancestor Xform or reference container.

So the streamlines/field prims inherit FieldMat from the /Session/Geometry container (no binding of their own), while the car mesh carries its own rel material:binding = </Session/Looks/CarMat> and overrides that inherited colormap material for just that prim:

        def Mesh "Car"
        {
            # ... points / faceVertexCounts / faceVertexIndices / normals ...
            uniform token subdivisionScheme = "none"
            rel material:binding = </Session/Looks/CarMat>
        }

When to use which: MDL for beauty surfaces (car body, floor, chrome, glass) that should reflect the environment and read as physical materials; the baked displayColor colormap for the CAE scalar field (streamlines/iso/slice/points), where the color encodes the data and must survive unchanged.

HDRI DomeLight For Real Reflections

A plain color3f DomeLight lights the scene, but it gives a metallic or clearcoat surface nothing to reflect — the car paint looks flat and dead, defeating the MDL upgrade. Load an equirectangular HDRI into the DomeLight so reflective MDL materials mirror a real environment. For metallic/clearcoat/glass this is the single biggest fidelity jump in the whole scene — without it the reflective lobe has no content:

        def DomeLight "Env"
        {
            float inputs:intensity = 900
            asset inputs:texture:file = @environment.hdr@  # app-supplied asset URI
            token inputs:texture:format = "latlong"
            double3 xformOp:rotateXYZ = (-90, 0, 0)
            uniform token[] xformOpOrder = ["xformOp:rotateXYZ"]
        }

Use an application-supplied or user-selected equirectangular .hdr/.exr asset URI. Do not rely on a package-layout-dependent bundled texture path.

CRITICAL — orient the HDRI to the scene's up axis. A latlong (equirectangular) HDRI wraps around a specific up axis; loaded raw it can arrive rolled 90°, so reflections and the horizon come from the wrong direction (e.g. the "ground" of the environment appears overhead). The DomeLight needs a rotation to level the horizon for the scene's up axis. For the Y-up viewer scene used throughout this reference, double3 xformOp:rotateXYZ = (-90, 0, 0) levels it (this is a suitable starting value for Y-up scenes). Match the rotation to whichever up axis your geometry uses — see coordinate-systems-and-up-axis.md for the Z-up vs Y-up handling; the dome rotation must track the same up-axis choice as the geometry, or the reflected world and the model disagree.

Smooth Normals For Imported Meshes

Imported CAE/prop surfaces are usually faceted — an STL, or a vertex-clustered decimation, ships triangles with no authored normals. The path tracer then shades each triangle flat, and MDL car paint reads visibly low-poly: every facet edge catches a different highlight. Author computed area-weighted vertex normals so the faceted body shades as one smooth surface. Each vertex normal is the (area-weighted) sum of its incident face normals, normalized, authored per vertex:

        def Mesh "Car"
        {
            point3f[] points = [ ... ]
            int[] faceVertexCounts = [ 3, 3, ... ]
            int[] faceVertexIndices = [ ... ]
            normal3f[] normals = [ (0, 1, 0), ... ] (interpolation = "vertex")
            uniform token subdivisionScheme = "none"
            rel material:binding = </Session/Looks/CarMat>
        }

Compute face normals with a cross product, accumulate them onto each vertex, then normalize. If you also rotate the mesh between up axes (a Z-up STL into a Y-up scene), rotate the normals with the same rotation — a pure rotation maps points and direction vectors identically, so the smooth shading survives the reorientation.

Curve Widths: Prefer Constant Interpolation For Live Geometry

The streamlines example earlier authors per-vertex widths (interpolation = "vertex"), which is fine for a static curve set. But when the streamlines are updated live via the data plane — an emitter move recomputes the curves and the total point count changes — a per-vertex widths array goes stale the instant the point count changes: its length no longer matches points, so the write fails or renders wrong. Prefer constant interpolation (one width for the whole prim) so a point-count change never invalidates the widths array:

        def BasisCurves "Streamlines"
        {
            uniform token type = "linear"
            int[] curveVertexCounts = [ ... ]
            point3f[] points = [ ... ]
            float[] widths = [ 0.02 ] (interpolation = "constant")
            color3f[] primvars:displayColor = [ ... ] (interpolation = "vertex")
        }

displayColor stays per-vertex (it is the field, one color per point). Only widths moves to constant. See the live data-plane array-write path in driving-cae-viz-via-ovstage.md, which rewrites points / curveVertexCounts / primvars:displayColor in place as the emitter moves — a constant widths is what lets that fast path skip re-authoring the curve every move.

USDA Authoring Gotchas

These are common USDA parser constraints:

  • Attribute metadata goes AFTER the value. Interpolation is trailing metadata: float[] primvars:x = [ ... ] (interpolation = "vertex"), not before the attribute. Same for other primvar metadata.

  • Use multi-line brace bodies. Compact one-line prim bodies can trip the parser. Author each prim with { and } on their own lines and one attribute per line. Long arrays wrap across lines (the example authoring code wraps ~6 elements per line for readability and parser safety).

  • float2 clippingRange = (0.1, 100000) — author the camera clipping range as a float2 tuple. A far plane large enough to contain CAE bounds avoids clipped geometry.

  • matrix4d xformOp:transform is row-major with translation in row 3 (M[0]=right, M[1]=up, M[2]=-forward, M[3]=eye), matching the viewer camera convention in references/conventions.md.

  • Numeric literals should be finite; substitute 0.0 for any NaN/inf, and emit compact but valid floats (e.g. %.6g).

Supported Colormap Boundary

This package supports the baked displayColor path above. Do not depend on applied schemas, dynamic texture services, or undocumented MDL LUT conventions for a generated app. If the requested result requires a dynamic opacity transfer function or a runtime-managed texture LUT, describe that as an unsupported extension rather than implying it is available here.

See also: references/conventions.md, references/ovrtx-rendering/README.md, references/ovstage-population/README.md, references/render-settings/README.md, references/usd-sample-data/README.md.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs Python, Needs Node.js

The author says: Orchestrator skill. Downstream focused references may require NVIDIA GPUs, ovrtx, ovstream, ovui, OpenUSD, Python, Node/React, Tauri, Electron, C++, or cloud GPU deployment access depending on the selected viewer path.

Security

What our scanner noticed· Medium risk

  • Large base64-encoded blobs
  • Instructions that use the network

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
NVIDIA Omniverse
Publisher
NVIDIA (Official)
Source
NVIDIA/skills · skills/omniverse-realtime-viewer · commit fce3cca
Licence
Apache License 2.0 (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE-APACHE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by NVIDIA Omniverse or NVIDIA.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download omniverse-realtime-viewer.zip