Houdini 18.0 Solaris

USD Basics

Introduces a number of USD concepts, and how they relate to Houdini’s USD support.

On this page

Thanks to Chris Rydalch and Blue Sky for use of their excellent Solaris Jumpstart.

Overview

USD is a system of software and file formats for describing 3D scenes by composing layers. For example, a master USD file describing a kitchen scene (kitchen.usd)might reference layer files containing props (chair.usd, table.usd), lighting, characters, and so on, composing them into a single stage.

The main power of the layer paradigm is non-destructive editing. You can start with an existing scene and create a new layer containing your edits. These edits won’t affect other people using the same scene unless they choose to include your new layer. You can also replace one of the layers (for example a newer version of an asset or updated lighting) and automatically re-apply all existing changes on top of it. This allows multiple departments to collaborate, share data, and update assets without interfering with each other.

Houdini’s tooling and support for USD is collectively known as Solaris. Solaris includes USD support in the view and a new network type, LOPs (Light Operators). LOP networks are slightly similar to SOPs, where each node accepts incoming geometry, modifies it, and outputs new geometry. In LOPs, each node accepts an incoming USD scene, modifies it, and outputs a new scene.

Because USD is a very comprehensive framework, it is not simple. LOPs is designed so you don’t need to know about the underlying USD structure. However, wherever possible LOP parameters use USD terminology, and are sometimes a thin wrapper around functionality built into USD. A basic understanding of USD concepts and features will make it much easier to understand LOPs.

The following page explains the basics of USD, and relates them to how Solaris supports working with USD in Houdini. For a look at how LOP nodes generate USD, see how LOPs work.

Quick example

Normally .usd (or .usdc) files are binary encoded for efficiency, however the USD scene description format has an plain text equivalent (.usda) that is human-readable. Here is a very simple .usda example to introduce some USD concepts we’ll explain in this document:

box.usda

#usda 1.0
def Cube "box" {
    double size = 4.0
}

In this example, def is a keyword defining a new prim (USD also has an over operator that is like def but defines how a new prim overrides an existing prim). Cube is the prim’s type. What "being a Cube" means and what properties a Cube has are defined by a schema. box is the name of the primitive (this prim will appear in the scene tree as /box). The Cube prim type is defined by the built-in USD Geometry schema. There are additional built-in schema for volumes, shading, lighting, skeletal animation, and rendering. You can extend USD by creating a custom schema to define your own prim types.

The code inside curly braces { } defines the new prim’s child primitives and properties (the Cube has many properties but those we don’t specify will be filled in with defaults from the type schema).

In USD jargon, this is bit of code may be said to "author an opinion about the size of /box". When composing layers, if a layer contains an "opinion" about the value of a property, it will be overridden if a layer with a stronger opinion has a different opinion (value) for the same property.

Now consider a second .usda file:

two_boxes.usda

#usda 1.0
def "box1" ( references = @box.usda@ ) {

}
def "box2" ( references = @box.usda@ ) {
    float size = 2.0
}

In this file, we import the contents of the box.usda file above twice as two prims: /box1 and /box2. /box1 will inherit its size attribute from the referenced layer. /box2 overrides size with a new value.

In Houdini, you generally won’t directly manipulate the values of attributes on primitives (although there are nodes for doing low-level edits). Instead, LOP nodes specify higher-level operations (such as "bring in this geometry", "point this light at this object", "place this prop on this table with physics") and the LOPs author the corresponding USD automatically.

You can actually inject plain text USD code into the stage built by a LOP network using the Inline USD LOP. This can sometimes be useful for debugging, but is not how you should work normally!

Tip

A file with the extension .usd may be either the binary format or the plain text format. This to make it easy to switch a file from one representation to another without messing up asset tracking or version control. A file with the extension .usda should always be the plain text format, and .usdc should always be binary.

Concepts

Primitives (prims)

Primitives are the basic "unit" of USD. "Primitive" is often shortened to "prim" when talking about USD. Similar to an "element" in HTML, prims have a name, zero or more named properties, and zero or more children.

For example, a polygonal mesh is a prim, a light is a prim, a material is a prim. An "xform" prim stores a transform that applies to it’s child prims.

Prims are the "nodes" in the tree of objects that define a stage.

Properties, attributes, relationships

"Property" is the generic name for two types of named data you can attach to a prim:

Attributes

Attributes are typed values (such as integers, colors, transform matrices, arrays, and so on) that describe the prim (for example, float size = 1.0 defines a Cube prim’s uniform size).

This is by far the most common form of property.

Relationships

Relationships establish a link between one prim and another (for example, specifying the material to use when shading a mesh prim, or specifying the primitives in a collection).

Metadata

Properties themselves can have named values attached to them, which can modify how they work. For example, if specify a point color primvar for a mesh, you can add interpolation metadata that specifies how the renderer should blend between the colors on the points.

Metadata is also used to attach "extra" data that may be specific to a particular DCC, or may be useful for debugging or building a user interface. For example, documentation for different attributes is stored in metadata.

Layers

A single .usd file is a layer (a .usdz file, on the other hand, can contain multiple layers in a single archive file). A layer can represent a "piece" of a scene such as a prop, a character, part of a rig, a lighting setup, and so on. It can also represent "defaults" that you can reference in to create a baseline, for example a layer containing settings for a shot sequence, which you reference in the files representing each shot in the sequence.

See how to organize layers and stages for more information.

In Houdini, various nodes create new in-memory layers to override existing data. You can also create a new layer "manually" with the Layer Break LOP. A typical LOP network will involve many layers, some imported from disk and some created in-memory by nodes, that are composed together by the end. (An optional visualization in the network editor shows you which nodes in the network work on different layers.)

Composition arcs

References from one layer to another. For example, a scene layer might bring in layers containing props, lighting, and so on. Those layers might reference their own sub-layers, those sub-layers may bring in sub-sub-layers, and so on.

Layers are composited together, merging the prims in each layer. Properties in higher layers override the values

For example, a scene file may reference a lighting layer, but the lighting department may have composed that layer itself from three sub-layers representing "base defaults", "finished lighting", and "in-progress lighting". Other departments don’t need to know that’s how the lighting layer is composed, they just need to reference the top-level "lighting" layer.

You can use the Reference LOP to add references to layer you are building in-memory using nodes in the LOP network. You can use the Sublayer LOP to load an existing sub-layer (a .usd file) into the layer stack of the current stage.

Stage

When you load a USD file and the system composes all the layers together to compute the final scene graph of prims, that result is called the stage.

Note that stage is just a name for the result. There is no difference between a "stage file" and a "layer file"… if you load a .usd file as a "top-level" file, it creates a stage. In another circumstance, you might reference that same file as a layer in a different stage.

In Houdini, the LOP node at the top of the network creates a stage, and each subsequent LOP modifies the contents of that stage procedurally, creating a new stage. So for any LOP node you can use hou.LopNode.stage() to get the stage object output by that node. You can view the stage output by a node as usda syntax by right-clicking the LOP node and choosing LOP Actions ▸ Inspect Flattened Stage.

Attributes and primvars

In USD, attributes are often scalar values that contain a prim’s "settings". For example, on a Sphere prim, the radius attribute controls the size of the sphere. In this sense, attributes are similar to node parameters in Houdini, or attributes in Maya.

In USD, geometry settings, such as per-point variables, are also stored as attributes on the prim. They are stored as arrays, with one element for each component. For example, the points attribute on a mesh prim stores the point positions in an array of coordinates. This is the same as Houdini geometry attributes.

USD also recognizes a specific type of attribute called a primvar. (The name primvar comes from Renderman, and stands for "primitive variable".)

At the basic tree level, primvars are just attributes with the primvars: schema namespace prefix. What makes them special is how Hydra treats them during rendering. Their main purpose is to override material parameters. However they have useful behaviors that mean they're also used to represent per-object properties that may affect geometry rendering aside from materials. Karma, for example, lets you use primvars to specify the number of motion blur frames per-object.

Primvar interpolation

A primvar is even more like a Houdini geometry attribute, which can also override material parameters in traditional Houdini rendering, than plain USD attributes. Like Houdini’s geometry attributes, if there is no primvar data available for a certain part of a curve/surface, the system will interpolate between the nearest available primvars.

How the system interpolates primvars essentially defines the "level" of the primvar, like how Houdini has vertex attributes, point attributes, primitive attributes, and global (detail) attributes. In USD, Constant interpolation is equivalent to a Houdini detail attribute. Uniform interpolation is equivalent to a Houdini primitive attribute. FaceVarying interpolation is equivalent to a Houdini vertex attribute. And Vertex interpolation is (confusingly) equivalent to a Houdini point attribute. The interpolation method is stored as metadata on the primvar attribute.

The number of values in the primvar’s array should correspond to the number of components corresponding to the chosen interpolation method. For example, a Uniform (per-face) primvar on a cube should have 6 values, but a Vertex (per-point) primvar on a cube should have 8 values. It is up to the renderer to apply the primvar values to override the material parameters as specified.

Interpolation between available values is the main feature of primvars. This is why on a Mesh primitive, the points attribute is a plain attribute, but primvars:displayColor is a primvar. Point positions cannot be "interpolated". The length of that array defines the number of points in the mesh. But the display color could be constant across the entire primitive, or have a value for each polygon, point, or vertex.

Primvar Inheritance

The other feature that separates primvars from plain attributes is that "Constant" primvars are inherited down the scene graph tree

For example, if a prim /Geometry/props has a constant value for primvars:displayColor, all descendants of /Geometry/props will use that same color unless it’s overridden.

This inheritance is why primvars are used for per-object render settings. You can set a value on a high-level primitive (for example decreasing the dicing quality), and have it affect large parts of the scene.

Primvars and instancing

When a point instancer has a "Vertex" (per-point) array primvar, for each instance it looks up the corresponding value in the array, and treats that as a "Constant" primvar on the instance. This is how per-point-instance material overrides work in USD.

Similarly, you can set a "Constant" primvar on an instanceable prim, that primvar will be inherited and affect the material parameters of primitives inside the instance (which can otherwise not be edited). This is the only way data from outside an instanceable prim can affect the appearance of primitives under the instance.

Composition

Every USD file contains a complete "scene". USD files can import the contents of other USD files as layers.

The USD libraries include software for composing/overlaying multiple layers into the full scene (stage) created by the top-level file. Composition includes merging the following differences between layers:

  • New prims.

  • Prim ordering.

  • Whether a prim is active or inactive.

  • Variants.

  • New properties (attributes and relationships).

  • Attribute values. (Higher layers can even "block" (unset) the value of an attribute so it appears to be unassigned).

  • Metadata values.

Houdini automatically composes the various on-disk and in-memory layers created as you edit USD. You can also use tools to understand what’s on each layer and how layers are composed.

Sublayers, references

USD has two different approaches to "importing" the contents of another USD file: sub-layering overlays the tree of the imported file over the current tree, while referencing attaches the contents of the imported tree as a branch on the existing tree.

You can think of sublayering as composing different versions of the whole scene (for example, overlaying the lighting department’s version of the scene with final lighting over the layout department’s version of the scene with scratch lighting), and referencing as adding a part to the scene (for example, importing props to begin layout).

Sub-layering

Sublayering overlays the contents of the imported file over the existing contents. Each tree’s "opinions" about prims with the same path, and (on those prims) properties/metadata with the same name, are merged based on opinion strength (see below).

This is useful when combining files that are each meant to contribute a portion of a complete scene. For example, composing separate layers representing set layout, props, characters, effects,and lights.

An inherent feature of sublayering is that "everything stays in the same place". Prims have the same path in the new file as they had in their source file.

Because each of these departments is working in a separate sublayer, they don’t need to worry about sharing a single file and having conflicting edits. But because they are all working in the same scene graph hierarchy, each department can apply changes to any of the content created by other departments.

The Sublayer LOP loads new sublayers into the scene. LOP networks in general are always editing the strongest sublayer of an empty root layer. Some LOP nodes may start a new, stronger sublayer which is then modified by all following LOP nodes. These network output is the composition of this sublayer stack.

Top-level file that specifies sublayers

#usda 1.0
(
    subLayers = [
            @shotLighting.usd@,
            @shotFX.usd@,
            @shotAnimation.usd@,
            @shotSetDressing.usd@,
            @sequence.usd@
    ]
)

Referencing

Referencing takes the tree in the referenced file and "grafts" it onto a branch in the current tree.

For example, if you start with this tree:

Scene tree before reference


/Lights/
    light1
    light2
/Models/
    tableside_lamp
        

If /Models/tableside_lamp references lamp.usd and that file contains:

Contents of lamp.usd


/lamp/
    base
    bulb
    shade
    socket
    switch

Then after referencing the file, the tree will look like this

Scene tree after reference


/Lights/
    light1
    light2
/Models/
    tableside_lamp/
        base
        bulb
        shade
        socket
        switch
  • The prim containing the reference (/Models/tableside_lamp) and the referenced prim (/lamp from lamp.usd) are composed together (with overlapping property values decided by opinion strength), but the prim keeps the name of the original referencing prim.

  • When you reference in content, you must specify a prim in the referenced file (/lamp in the example above), and that prim cannot be the root (/). So when you're creating a file that is meant to be referenced in by other layers, you should organize the contents under a single root primitive.

    (It’s possible to specify the default primitive of a file. If you reference a file and don’t specify a specific prim in the file, you get the file’s "default primitive".)

Referencing is useful to graft smaller individual assets into a larger scene. In particular, referencing is the only way to load the same layer file more than once at different locations. (Because sublayering works on the entire tree, sublayering the same file more than once would have no effect.)

Simple trash can asset

#usda 1.0
(
   defaultPrim = "TrashCan"
)

def Xform "TrashCan" (
   kind = "component"
)
{
   def Cylinder "Can"
   {
       token axis = "Y"
       bool doubleSided = 0
       double height = 2
       double radius = 1
   }
}

File which references the trash can asset 3 times, for 3 different rooms in a set

#usda 1.0
()

def Xform "Scene"
{
   def Xform "Set"
   {
       def "BathroomTrashCan" (
           append references = @./trashcan.usda@
       )
       {
           double3 xformOp:translate = (2, 0, 1.4)
           uniform token[] xformOpOrder = ["xformOp:translate"]
       }

       def "KitchenTrashCan" (
           append references = @./trashcan.usda@
       )
       {
           double3 xformOp:translate = (16.01, 5, -43.072)
           uniform token[] xformOpOrder = ["xformOp:translate"]
       }

       def "OfficeTrashCan" (
           append references = @./trashcan.usda@
       )
       {
           double3 xformOp:translate = (-7.12, 0, 11.9)
           uniform token[] xformOpOrder = ["xformOp:translate"]
       }
   }
}

The Reference LOP and Stage Manager LOP create references. The Graft LOP is similar, but instead of referencing from a file, it inserts a branch into the scene graph tree from elsewhere in the LOP network.

Keeping track of how multiple levels of sublayering and referencing work together to generate the final opinion on a primitive at a single scene graph location can become complicated. You can use the Layer Stack and Composition tabs of the Scene Graph Details pane to examine which layers contribute to the primitive, and how those different layers were composed together.

Payloads

A payload is essentially a reference, but it’s possible to have USD not load payloads until they are specifically requested. This lets you control which parts of a scene are loaded into memory, keeping memory use and processing time down by focusing on the parts of a scene you care about.

In Houdini, you specify whether a referenced file is a plain reference or a payload in the parameters of the node that references the file.

By default Houdini loads all payloads (and so they behave exactly like references). But the Configure Stage LOP and the Load Masks controls in the scene graph tree allow you to disable default loading of payloads, and then choose which specific payloads you want to load.

Activation and visibility

Because it is designed for non-destructive editing, USD does not allow deleting primitives. However, you can override primitives with new values, or deactivate primitives so they have no effect.

You can also edit the visibility of objects in the scene. By default, USD prims are visible. The visibility option only has two settings: "inherit" and "invisible". So marking a prim "invisible" always makes all its descendants invisible as well.

In Houdini you can interactively toggle a primitive between active and inactive, and visible/invisible in the Scene Graph Tree pane to allow you to see the effects of the primitive. Houdini performs these edits in an in-memory, unsaved layer so they don’t affect the final generated USD.

Opinion strength

Attributes resolve according to "strongest wins" rules, so all values for any given attribute will be fetched from the strongest PrimSpec. For example, a "weak layer" with a default value will be masked by a stronger layer with animation. For example, the layout department places a prop in the scene, and later the animation department adds a layer that animates that prop.

The most common way you decide opinion strength is the ordering of the layer stack.

For example, the weakest layer could be the layout layer, where assets are combined and positioned. The next layer may add characters to the scene from the animation department (which may move or otherwise adjust the layout if necessary by applying stronger opinions to some of those existing primitives). Then the FX department may want to add new geometry to the scene graph, or apply an RBD simulation to some of the existing primitives added by the layout or animation departments.

The ordering of opinions follows the mnemonic LIVRPS ("liver peas"). These are the ways, from strongest to weakest, one layer can override another during composition:

Local

A layer that has its own opinion for a property overrides any opinions for the same property in any sub-layers it references in.

If no local opinion exists, then USD looks for:

Inherits

"Inherits" can be used to re-define default values of a prim, but only in the context of a layer. Inherits are similar to a local reference, but remains "live" through multiple levels of references. Materials are the primary use case for "inherits" and "specializes". See inherits and specializes below.

Further reading: inherits in Pixar’s USD glossary.

If no inherited opinion exists, then USD looks for:

Variants

See variants below. The current variant overrides attributes on the prim containing the variant set.

If no variant opinion exists, then USD looks for:

References

A reference "imports" smaller units into a scene. For example, a dressed set file might reference a bookshelf prop file, and the bookshelf file might reference several different book prop files.

Further reading: references in Pixar’s USD glossary.

If no reference exists, then USD looks for:

Payloads

Just like a reference, but can be optionally unloaded to speed up viewing/rendering the scene, and reloaded later. Heavy parts of assets (such as high-res polygonal mesh skins) are referenced as payloads so they can be unloaded.

When a payload is loaded, it overrides the "unloaded" version.

Further reading: payload in Pixar’s USD glossary.

If no payload reference exists, then USD looks for:

Specializes

Similar to inherits (see above), redefines default values of a prim in a layer, without changing the original source. Specializes are a way to broadcast these changes, but without overriding any "inherits" opinions. Materials are the primary use case for "inherits" and "specializes". See inherits and specializes below.

Further reading: specializes in Pixar’s USD glossary.

In Houdini, you are always authoring in the strongest opinion, so you don’t usually have to think about override strengths.

Further reading: LIVRPS ordering in Pixar’s USD glossary.

Understanding "specializes" and "inherits"

To illustrate how speicalizes and inherits composition works, imagine the following USD layer file:

Contents of props.usd

# props.usd
/Materials/
    plastic1
/Things/
    Toy/
        geom
        toy_shader (references and overrides /Materials/plastic1)
  • The Toy prop carries its own shader with it.

  • The toy_shader shader is a reference to /Materials/plastic1, with its own overrides on top.

Now imagine you reference the Toy prim from above USD layer into the following USD layer file:

Contents of scene.usd

# scene.usd
/Materials/
    plastic1
/Models/
    Props/
        Toy/  <– referenced onto this prim
            geom
            toy_shader
  • Note that this scene.usd layer has its own /Materials/plastic1 prim in its namespace.

  • Which /Materials/plastic1 prim does the reference in Toy/toy_shader get its data from?

    The answer is it comes from the /Materials/plastic1 in props.usd, because the reference on the shader is resolved and the data composed onto Toy/toy_shader before the prim is referenced into scene.usd

Given that this is how references work, you can probably see that it would be useful to somehow allow the /Materials/plastic1 prim in scene.usd to also affect the toy_shader. This would allow the referencing file to customize the incoming data.

The way to do this is to use an "inherits" or "specializes" composition arc instead of a plain reference.

  • When a prim "inherits" or "specializes" another prim, it resolves the references within its own layer (just like with reference), but as layers are referenced within layers, USD does additional checks for prims at the specified primitive path, and composes them onto the prim if they exist.

  • So in the above example, if toy_shader has an "inherits" or "specializes" relationship with /Materals/plastic1, it would get data from both the /Materials/plastic1 in props.usd, and the /Materials/plastic1 in scene.usd.

  • The difference between "inherits" and "specializes" is that with "inherits", the opinions in "higher" layers are stronger than the opinions in the inheriting prim’s own layer. With "specializes", the opinions in higher layers are weaker than the opinions in the specializing prim’s own layer.

Default primitive

As part of its metadata, each layer can specify a "default primitive". This is the primitive that is referenced in if you reference in a file but don’t explicitly specify what root-level prim from the file to attach.

  • You can specify the default primitive (along with other layer-level metadata) for a layer using the Configure Layer LOP.

  • When you write out USD files, the USD render node has a parameter to set the default prim for the "top-level" layer file.

  • The USD render node has an option (Error Saving Layer With No Default Primitive) to cause an error if you haven’t specified a default primitive for all of the layers being written out.

Low-level USD access in Houdini

  • You can write usda format code and evaluate it and insert the resulting prims directly into the in-memory scene graph tree using the Inline USD node.

  • You can use the Pytbon Usd API to manipulate the in-memory stage directly using the Python Script LOP.

Extensibility

File format plugins

The open source libraries for USD provide for plugins to load data from different file formats.

In Houdini you will generally use a LOP node to import data such as geometry directly into USD from another network or from disk, without using USD’s file import code. Houdini also includes USD plugins for Houdini file formats so you can use them in a pipeline outside Houdini (these plugins consume a batch license).

Asset resolvers

In addition to explicit file paths, USD lets you encode file references using opaque asset IDs (in usda format, these look like @my_asset_name@). A plug-in called an asset resolver is then responsible for converting an asset ID into a loadable object.

For example, if a studio stores their assets in a central Git server, they might store file references as asset IDs that contain path and revision information that is meaningful in relation to that server (for example, @/models/tintoy#bf452ac@).

For USD generated in Houdini, output processors provide for some customization, but only for file locations and file path strings.

Custom schemas

See schemas below. Schemas add custom types, attributes, and/or APIs to USD and define their behavior.

Render delegate

See Hydra and rendering below. Software uses an API (called Hydra) to direct a render delegate to render USD. As long as a render delegate exists for a renderer, any software can render USD through that renderer.

Scene delegate

Just as a program directs rendering through a render delegate, when a renderer needs information from the scene, it uses an API to request it from a scene delegate. This can provide a level of indirection to alter the scene at render time.

Prim adapters

Each type of USD primitive is translated into a Hydra-compatible renderable representation by a prim adapter type.

You can use your custom file format plugins and asset resolvers with Houdini in one of two ways:

  • Build your plugins using the USD headers and libraries shipped with Houdini. (These headers and libraries are included as part of the HDK.)

  • Replace Houdini’s version of the USD library with your own version. To do this you must download the HoudiniUsdBridge repository from the SideFX GitHub. This repository contains all the Houdini source code that calls into USD. Rebuild that code using your own USD library, and use the results to replace the equivalent libraries in the Houdini install (either by copying them in or manipulating LD_LIBRARY_PATH).

Solaris output processors provide a place to customize how files are saved (such as taking paths that are relative to $HIP and making them relative to the root USD file, and tweaking file extensions).

Geometry in USD

USD refers to prims that represent geometry as gprims (these are prims that, when rendered, will actually cause something to be drawn in the image, as opposed to, say, a transform prim). The schema for these prim types (UsdGeomGprim) contains convenient methods such as finding bounding boxes.

USD includes a set of prims representing mathematically defined shapes (capsule, cone, cube, cylinder) as well as more complex prims that hold geometry such as polygonal meshes or subdivision surfaces.

Do not put gprims under other grims in the scene graph tree. Many key features of USD apply hierarchically, such as activation, visibility, and purpose, and it can be frustrating and counter-productive to be unable to apply these operations to individual gprims.

Kinds

Kinds are one of the harder-to-understand concepts in USD. Every prim that has geometry under should have a "kind".

"Model kinds" are sub-classes of an "abstract" kind called model. You should never assign model as a kind. There are three model kinds:

assembly (subclass of group)

"An important group model, often a published asset or reference to a pusblished asset"

group

A group of other models (components, assemblies, or groups). The "branch" item of a model hierarchy.

component

The "leaf" items of a model hierarchy, prims of this kind cannot contain other models (groups or assemblies or other components).

Instead, components can contain prims with kind set to subcomponent. This kind is not a subclass of model.

Kinds support organizing the geometry in the scene into a model hierarchy. This is a "table of contents" of the models in the scene. Maintaining this hierarchy properly allows software packages to present better UI, such as for selection, and allows more efficient traversal of the models in the scene. (For example, if software wants to show the user all the models in the scene, it only needs to start searching the tree in roots that have a kind, and can stop descending in the tree when it hits a component kind.)

A model hierarchy in the scene graph tree might look like the following:

/Models ← group
    /Characters ← group
        /Lady ← assembly
            /Skin ← component
                /mesh
            /Purse ← component
                /mesh
        /Dog ← assembly
            /Skin ← component
                /mesh
            /Collar ← component
                /mesh
    /Props ← group
        ...

To maintain the model hierarchy, your scene should have the following:

  • All prims that deal with geometry under (descending from) them should have a kind, starting, including the root prim (for example, /Models).

  • Only group/assembly kinds should contain other model kinds. Component kinds cannot contain groups, assemblies, or models. (They can contain subcomponent kinds).

In Houdini, nodes that create/edit primitives have parameters to let you set the prim’s kind. It does not allow you to use model as a kind, in accordance with model hierarchy rules. Currently Houdini doesn’t do anything to help or force the user to maintain the model hierarchy.

Instances

There are various forms of "instancing" (efficient copying) in USD.

USD inherently has the concept of one prim in the tree being a light-weight reference to another. While this is more efficient than actually copying all the prims in the branch, to preserve the ability to override sub-prims, USD must also create references for each sub-prim under the reference.

To get more efficient ways of populating the scene with large numbers of copies/variations of things, USD has two separate methods of instancing.

"Instance-able" primitives (sometimes called "native" instancing)

Prims have a bit of metadata that specifies that the prim is instanceable (that is, "can be considered for instancing").

Whenever USD loads a prim that has instanceable = true, it creates a virtual "master" copy of the primitive (and its descendants), and makes the instanceable prim a reference to this master primitive. Then, whenever USD encounters identical instanceable primitives in the scene graph, it automatically makes them pointers to the same master primitive, making "duplicates" take very little actual space.

The trade-off is that you can’t change any primitives under an instanceable prim. However, you can modify attributes on the instanceable prim itself. This lets you, for example, give each instance a unique transform, or set primvars used for shading.

Further reading: [instancing|https://graphics.pixar.com/usd/docs/USD-Glossary.html|#USDGlossary-Instancing] in the Pixar USD glossary.

Point instancers

In a point instancer prim, each point in a geometry is replaced at view or render time by an instance of the geometry of one of the prims with a "prototype" relationship to the instancer. The point instancer prim stores mappings of points to primitives very efficiently in arrays.

Houdini’s instancing nodes support additional functionality beyond what USD’s basic point instancer provides, similar to Houdini’s own point instancing. These "extra" features are computed in the node and then "baked" into the USD.

Further reading: https://graphics.pixar.com/usd/docs/api/class_usd_geom_point_instancer.html#details in the USD API docs.

Instanceable prims are easier to work with than point instancers. However, while each "alias" consumes a very small amount of memory compared to copying the prim, it can add up over a very large number of instances. Point instancing is designed to handle billions of instances, with constant memory usage.

Both types of instances support transforming the top-level of each primitive and they also allow primvars (on each native instance individually, or on the point instancer primitive) to override material parameters per-instance.

Note

Do not directly instance geometry prims. Instead, instance a prim above the actual geometry (such as its parent transform).

Houdini supports both kinds of instancing with the Instancer and various Tab menu tools that configure the instancer node for different ways of instancing, so you will choose the method of instancing mostly based on the number of instances needed. Houdini also supports nested point instancing (one or more nested levels of instancers instancing prototypes containing instancers), and heroing an instance to become a "real" model.

Variants

USD allows storing multiple named variants of a primitive on the primitive. Each variant can have different attributes, relationships, and children. You can switch the primitive between different variants in different layers. Each primitive can store multiple groups of variants in named variant sets.

The following are some common use cases for variants:

  • A family of different geometry (for example, a primitive that can switch between different types of trees).

  • Levels of detail (storing progressively lower resolution versions of the same geometry as variants, and switching based on distance to the camera).

    Houdini includes the Create LOD and Auto Select LOD nodes for automating this workflow.

  • Material assignments (storing variants with different materials assigned to them to allow switching the look of the primitive easily).

Here is an example of a layer which contains a variant set called owner, where each variant’s color corresponds to which character in the story owns that ball:

#usda 1.0
()

def Sphere "toyBall" (
    variants = {
        string owner = "blake"
    }
    append variantSets = "owner"
)
{
    double radius = 1
    variantSet "owner" = {
        "andy" (
        ) {
            color3f[] primvars:displayColor = [(1, 0, 0)] (
                interpolation = "constant"
            )


        }
        "blake" (
        ) {
            color3f[] primvars:displayColor = [(0, 0, 1)] (
                interpolation = "constant"
            )


        }
        "sally" (
        ) {
            color3f[] primvars:displayColor = [(0, 1, 0)] (
                interpolation = "constant"
            )


        }
    }
}

See Variant Set in Pixar USD glossary

Animation

Animation in USD is through time-varying attribute values, similar to how animation in Houdini is through time-varying node parameters.

  • Each attribute, besides have a single "default" value, can have an collection of "time samples", a list of values indexed by time. Metadata on the attribute specifies how to interpolate between the samples.

  • If you ask USD for the value at the "default" time, it will return the default value if one is set, otherwise it will return an empty value. All time sample values are ignored. If you ask for a value at any other time, and there is any default or time sampled value set, you will get back a value. This value may be the default (if you request the value for a time before the first time sample), the closest time sample value, or a linear interpolation of the two surrounding time samples.

You can animate Houdini parameters on LOP nodes, and when you write out USD to disk, it will write the animated values as time samples.

Schemas

  • Schemas are custom extensions that can define new prim types or define the meaning/behavior of new attributes.

    At the "tree" level, USD is quite generic and "free-form". You can set attributes to arbitrary typed values directly. Schemas provide a way to enforce constraints and invariants. By using the higher-level API, you ensure that attributes are authored "correctly" (such as keeping values within a certain range, or not allowing impossible configurations).

  • USD calls a schema that defines its own prim type a typed schema.

  • USD calls a schema that just defines an API you can apply on top of prims of different types an API schema. This type of schema generally have names that end in API.

    API schemas are further divided into singly-applied and multiply-applied.

    • An example of a singly-applied schema is UsdLuxShapingAPI, where a light prim either has shaping controls added or not. Prims have metadata which records a list of all singly-applied API schemas that have been applied.

    • An example of a multiply-applied schema is the CollectionAPI. You can define as many uniquely named collection: attributes on a given prim as you want.

Houdini generally doesn’t use custom attributes. Instead it often stores information in a "custom data" dictionary in the metadata of a prim. This space is intended for use by software packages and studios for their own use. Houdini stores bookkeeping information here that helps with saving USD to disk or navigating the scene, but this custom data is usually stripped from the USD written to disk.

Hydra and rendering

USD defines an API (called Hydra) for generating an image from a USD scene at a certain point in time. As long as a program (such as Houdini, or usdview, or a renderer) implements this API, it can be used to display/render a USD scene.

A piece of software that implements this API is called a render delegate.

In Houdini this has the great benefit that you can switch the scene view between available render delegates such as Houdini’s OpenGL viewer, Pixar’s Storm OpenGL viewer, and Karma IPR. Further, you can view the scene using any third-party renderer that has implemented the Hydra API. They don’t need to do extra work to integrate specifically with Houdini.

Organizing USD

  • Commonly, the "top-level" USD file will represent a shot. In a studio, the top-level file will usually reference in files broken down by department (characters, set/props, lights) which have their own references to individual assets. In a smaller or one-person environment, the top-level file might directly reference assets.

  • It’s not useful to look at USD reference depth as a measure of "slowness" (or complexity). USD’s composition engine is quite fast. Performance (such as load time) depends more on what is in the layers, and your infrastructure (such as loading files locally or over the network), than how many levels of references there are.

Tips

  • In USD documentation, they sometimes refer to the scene tree as the namespace.

  • Avoid using absolute file paths, and use forward slashes even on Windows.

    Relative path references (for example ./chair.usd) are relative to the layer file containing the reference (for example /Users/Aisha/Work/kitchen.usd).

    For large studios or complex file management cases, it’s probably better to use asset IDs and a custom resolver than to use complicated file paths.

    In Houdini, you will usually store files relative to the scene file ($HIP/...). When Houdini writes out USD, it calls output processors to translate file paths. The default output process translates file paths relative to $HIP into paths relative to the top-level USD file, in line with USD’s conventions.

Further reading

Solaris

USD

Tutorials

Karma