Introduction
Welcome to the first installment of our Explainer Series on OpenUSD (Universal Scene Description), the foundational technology behind the Alliance for OpenUSD (AOUSD). This series will dive into the technical aspects that make OpenUSD indispensable for developers, artists, and designers, and explore fundamental features like layering, references, and other types of composition arcs.
What is OpenUSD?
Originally invented by Pixar Animation Studios, OpenUSD is a framework and universal interchange for describing, simulating, and collaborating across tools. It is designed to interchange and augment 3D scenes composed of many component assets, facilitating the assembly and organization of these assets into virtual sets, scenes, shots, and worlds. OpenUSD is the focus of the AOUSD, an alliance to promote and standardize this open-source technology.
Why Use OpenUSD?
Interoperability and Collaboration
OpenUSD enhances the interoperability of 3D tools and data, facilitating the exchange of assets and data between diverse software applications. This is crucial for efficient collaboration among diverse teams.
Non-Destructive Workflows
OpenUSD supports non-destructive workflows, allowing artists and designers to make changes and iterations without permanently altering the source data. This enhances productivity and encourages creative exploration and iteration.
Extensibility
OpenUSD is designed to be extensible, allowing developers to support custom schemas, file formats, and more via a rich plug-in system. This adaptability ensures that OpenUSD can meet future industry requirements.
Prims and Properties: The Building Blocks of 3D Scenes in OpenUSD
Understanding the role of Prims and Properties is a great starting point for anyone looking to leverage the power of OpenUSD. These are the building blocks for robust classification, organization, and manipulation of 3D scene elements.
NOTE: All usda examples in this article can be opened in usdview — see the installation instructions for Windows.
What Are Prims?
In OpenUSD, a Prim (short for “Primitive”) is a container that holds various data types and embodies an individual node in the scene hierarchy. Prims can represent simple geometric shapes like a cube or sphere, materials for look development, lights, and more.
Here’s a basic example of defining a Prim representing a cube mesh:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#usda 1.0 def Mesh "CubeMesh" { float3[] extent = [(-50, -50, -50), (50, 50, 50)] int[] faceVertexCounts = [4, 4, 4, 4, 4, 4] int[] faceVertexIndices = [0, 1, 3, 2, 4, 6, 7, 5, 6, 2, 3, 7, 4, 5, 1, 0, 4, 0, 2, 6, 5, 7, 3, 1] point3f[] points = [(-50, -50, 50), (50, -50, 50), (-50, 50, 50), (50, 50, 50), (-50, -50, -50), (50, -50, -50), (-50, 50, -50), (50, 50, -50)] uniform token subdivisionScheme = "none" double3 xformOp:rotateXYZ = (0, 0, 0) double3 xformOp:scale = (1, 1, 1) double3 xformOp:translate = (0, 50, 0) uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ", "xformOp:scale"] } |
In this example, the Mesh Prim CubeMesh holds the geometric data for the cube and can also contain transform operations such as translates, rotates, and scales.
Any Xformable Prim allows you to manipulate the position, orientation, and size of the objects (Prims) nested underneath it in the scene hierarchy.
When you define an Xform Prim, you’re creating a node in the hierarchical scenegraph that can have transformational attributes. These attributes include the position (x, y, z coordinates), rotation angles or quaternions, and scaling factors. The Xform then applies these transformations to all of its descendant Prims, effectively altering how they are displayed or rendered in the 3D scene.
Here’s a simple example to illustrate:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#usda 1.0 def Xform "MyTransform" { double3 xformOp:translate = (2, 3, 0) uniform token[] xformOpOrder = ["xformOp:translate"] def Sphere "MySphere1" { double radius = 1.0 } def Sphere "MySphere2" { double3 xformOp:translate = (-1, -4, -7) uniform token[] xformOpOrder = ["xformOp:translate"] double radius = 2.0 } } |
In this example, the Xform Prim named “MyTransform” contains a translation operation that moves any nested Prims by 2 units along the x-axis and 3 units along the y-axis. Nested within this Xform are 2 Sphere Prims. The spheres will inherit the translation specified in “MyTransform,” meaning their parent space will be positioned at (2, 3, 0) in the 3D space.
“MySphere2” also has an additional local translate of (-1, -4, -7), which offsets its geometry relative to the parent Xform.
Note that the actual transform attributes come from the definition of Xformable Prims. Xform, Mesh, and many other geometric Prim types in OpenUSD subclass Xformable, so they also contain the ability to add and modify transforms.
Understanding Xform and Xformable is key to effectively arranging and manipulating meaningful sub-hierarchies of your 3D scenes.
What Are Properties?
Properties are the data members that reside within Prims. They come in two types: Attributes and Relationships.
- Attributes: These are the typed data fields that can hold values for built-in types like integers, floats, strings, and arrays. For example, the points attribute in the CubeMesh Prim above holds the cube mesh’s vertex coordinates as a point3f[], i.e., an array of floating-point triples. Attributes can also be time-sampled to allow for animation.
- Relationships: Relationships are path-valued Properties; they point at other Prims or Properties in the scene. This enables the creation of semantic associations, such as logical dependencies or material assignments.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
#usda 1.0 def Xform "World" { def Cube "BlueCube" ( # Runtime resolution of material bindings # requires the MaterialBindingAPI schema # to be applied. This is not important # for understanding the general role of # relationships in OpenUSD, but we include # it here so that readers can see the # effects of the bound material in usdview # with this example. # # Future installments of this blog # will dive more into schemas. prepend apiSchemas = ["MaterialBindingAPI"] ) { rel material:binding = </World/Materials/Blue> } def Scope "Materials" { def Material "Blue" { token outputs:surface.connect = </World/Materials/Blue/Shader.outputs:surface> def Shader "Shader" { uniform token info:id = "UsdPreviewSurface" color3f inputs:diffuseColor = (0, 0, 1) token outputs:surface ( renderType = "material" ) } } } } |
In this example, the material:binding Relationship in the BlueCube Prim points to a Material Prim located at the path /Materials/Blue, indicating that the cube should be rendered with this material.
How Do Prims and Properties Interact?
Prims and Properties work in tandem to create a rich, hierarchical structure that can represent a 3D scene in a highly organized manner. You can think of a Prim as a folder that contains various files (Attributes) and shortcuts to other folders and files (Relationships). This hierarchical organization makes it easier to manage complex scenes, as you can easily navigate, expand, or query the structure.
Prims and Properties are the DNA that encodes the complexity and richness of 3D scenes. By understanding their roles, types, and associations, you can unlock the full potential of data modeling in OpenUSD, whether you’re creating a simple 3D object or orchestrating an entire virtual world. Our subsequent blog entry on schema development will further expand on data modeling in OpenUSD.
Layers and Composition: Aggregating 3D Scene Elements in OpenUSD
Layers are individual USD documents that can contain elements of a 3D scene, or an entire 3D scene, and can be of various formats, such as .usda (text), .usdc (binary), or .usdz (packaged). Layers do not need to be physical files on disk or over a network or cloud — any data backends, including databases and IoT streaming, that implement a USD file format plugin can be ingested as native USD. We’ll dive deeper into file format plugins later in this blog series.
When it comes to building intricate 3D scenes, USD offers a rich range of mechanisms to streamline data aggregation and collaborative workflows. Layering allows for stacking multiple scene description documents to create a composite output. Composition in USD formalizes the strength ordering when considering data elements coming from multiple sites in the layer stack.
subLayers: Stacking Scenes Together
Layering is a powerful feature in USD that allows you to stack multiple USD layers together. The subLayers composition arc is used to specify the layers that should be combined.
#usda 1.0
(
subLayers = [@./overrideLayer.usda@, @./baseLayer.usda@]
)
subLayers is often used to separate departmental workflows in a film production pipeline. For example, the layout, animation, simulation, and lighting departments could all be working in respective subLayers of the same scene.
References: Reusing Scene Elements
The references composition arc allows you to reuse scene elements by pointing to other USD layers or specific Prims within those files. In this way, you can leverage USD to represent and organize large scenes in an efficient and modular manner.
1 2 3 4 5 6 7 8 9 10 11 |
#usda 1.0 def Xform "World" { over "RefSphere" ( references = @./sphereAsset.usda@ ) { # Sparse, non-destructive opinions here # can override opinions coming from sphereAsset.usda # without modifying sphere.Asset.usda directly. } } |
More Composition Arcs for Scene Assembly
In addition to subLayers and references, USD offers other types of composition arcs with which to assemble scenes and aggregate datasets. Atomic elements of scene description in USD are called opinions. In USD, LIVRPS (Local, Inherits, VariantSets, References, Payloads, Specializes) provides a canonical strength ordering for the opinions aggregated across all composition arcs. A future installment of this blog will cover these composition arcs in more depth.
Understanding the structure of a composed USD layer stack leverages the full power of the OpenUSD framework. You can create intricate, reusable, and efficient 3D scenes via careful consideration of layering, references, and various composition arcs.
Performance and Other Considerations
Before we go, it’s good to note that understanding performance considerations will go a long way toward defining optimal and manageable asset structures for your large worlds and datasets in OpenUSD. Think carefully about the per-Prim cost for scenes that can grow into the millions and tens of millions in Prim count. Future installments of this blog will walk through more of these considerations, trade-offs, and approaches for various use cases and personas navigating the OpenUSD ecosystem.
Conclusion
OpenUSD is more than just another file format; it’s a comprehensive ecosystem that addresses the complex needs of the 3D graphics industry. Its ability to facilitate interoperability, support non-destructive workflows, and offer an extensible framework makes it a cornerstone technology for anyone involved in 3D content creation.
Stay tuned for the next blog in our Explainer Series, in which we’ll focus on OpenUSD schemas.
Check out the AOUSD website to learn more about the organization. For more resources, visit our forum. Follow AOUSD on LinkedIn, Twitter, Facebook, Instagram, and YouTube.