UsdShade Material Assignment
© Pixar Animation Studios, 2017
Status as of 5/2018: Deployed in UsdShade, UsdImaging (Hydra), and Maya. Katana import on the way.
- Basic Proposal for Collection-Based Assignment
- Analysis of Collection-Based Binding
- Remaining Questions
In UsdShade of summer 2017, material assignment is expressed as a single-target relationship that identifies a Material that should be bound to the anchoring prim and all of its descendants, unless some descendant itself possesses a direct material assignment, in which case its binding will be considered stronger than the inherited one. "Material resolution" itself is somewhat more nuanced, because what actually matters to a particular renderer is not truly the bound Material, but the Material output (referred to sometimes as terminals or ports) that the renderer understands: each output identifies a shading network, and different renderers may consume different shading networks. In the example below, the Material </PreviewMaterial> bound to a root prim </Bob> may provide only a glslfx:surface output, while a descendant Mesh prim </Bob/Geom/Body> may bind a Material </Skin> that only defines a renderman-specific, ri:bxdf output. Even though the ancestral binding is weaker (for the mesh) than the descendant-binding, when a GL renderer asks </Bob/Geom/Body> what shading network it should use, the answer should be the glslfx:surface defined on the ancestor prim. This nuance points out that addressing the problem of binding materials must be done in consideration of the larger problem of identifying useful shading networks; we will address this problem in the second half of this proposal.
In the several years we have been using this binding mechanism, we have bumped up against two key llimitations:
- Direct bindings cannot be established for gprims inside aggregate instances without breaking the instances. This can be problematic in systems like Maya that natively allow unique shader bindings to instances, as recently reported in Issue 332. This has been problematic for our Katana-based set-shading workflows, as well, in which a set of unshaded, instanced components are referenced into a set, and we wish to bind set-level Materials to the primitives inside the components.
- For high-level distinctions like "preview" vs "full" rendering, wherein we frequently wish to bind materials at different granularity, it can be onerous to manage and interrogate multiple materials bound at different points in the hierarchy... this is especially true in Katana, where the cost of examining a material in order to discover whether it is the one you want is high.
Other systems, such as Katana, Houdini Material Style Sheets, MaterialX, and upcoming versions of Maya allow materials to be assigned to a collection that identifies the gprims (or ancestors of gprims) that should bind to the given Material. In USD, collections are represented by include and exclude relationships in the UsdCollectionAPI schema, and relationships authored "outside" instances are completely free to target prims inside instances, in which case USD core will readily resolve the targets to "instance proxy prims" . This means that we can create collections above/outside instances that refer to constituents of individual instances, and bind the collections to Materials defined either inside or outside of instances.
In this document we present a model for binding materials via collections, with an eye towards efficient evaluation and paying only for the mental/pipeline complexity you need, togther with a new concept we call material purpose whose goal is to make it easier for users and renderers to manage and discover the materials relevant to a particular rendering task.
Basic Proposal for Collection-Based Assignment
Currently, the material:binding relationship is restricted to targeting a single Material. We propose adding an additional set of binding properties that can appear on any prim, material:binding:collection:XXX that identifies a (Material, Collection) pair to be bound:
- material:binding:collection defines a namespace of binding relationships to be applied in namespace order, with the earliest ordered binding relationship the strongest. Generally, we expect binding collections to be non-intersecting, and therefore order to be unimportant, but when ordering is necessary, it relies on the hosting prim's native property ordering.
- For a material:binding:collection:NAME relationship to be a valid binding, it must target a single Material and a single Collection, and represents the binding of the targeted Material to all of the prims identified by the Collection that live beneath the binding relationshp's prim in namespace. The name chosen as the last element of the binding property's name is irrelevant, save that:
- it establishes an identity for the binding that is unique on the prim. Attempting to establish two collection bindings of the same name on the same prim will result in the first binding simply being overridden. In the future, we may allow a single colllection binding to target multiple collections, if we can establish a reasonable round-tripping pattern for applications like Maya.
- NAME must be a single token, not a namespaced token sequence. This restriction facilitates advanced features (purpose) described later in this document.
- If a prim has both authored material:binding and authored material:binding:collection:XXX properties, the "direct" material:binding is weaker than the collection-based assignments, reflecting our belief that the combination would appear primarily to define a "fallback" material to be used by any child prims that are not targeted by a more specific assignment.
- The "specificity" with which a prim is included in a collection is irrelevant to the binding strength of the collection. For example, if a prim contains the ordered collection bindings material:binding:collection:metalBits and material:binding:collection:plasticBits , each of which targets a collection of the same name, then if metalBits includes </Chair/Back>, while plasticBits includes </Chair/Back/Brace/Rivet>, the binding for </Chair/Back/Brace/Rivet> will be metalBits, because the metalBits collection is bound more strongly than the plasticBits, and includes an ancestor of </Chair/Back/Brace/Rivet>.
This formulation addresses both of the concerns raised in the last section:
- Because we still require a material:binding:collection:NAME relationship on a prim or one of its ancestors, it is irrelevant whether any Collection associated with the binding identifies objects in arbitrary corners of the scenegraph, because we know we will only need to care about objects at or below the bound prim.
- The rule presented in item (c) above provides resolution behavior for a single prim. For collections bound on different prims, the simple rule already in use for direct bindings applies: bindings lower in namespace (closer to leaf gprims) are stronger than bindings on ancestors.
Example Collection-Based Assignment
Let us suppose we have a set constructed of unshaded, instanced assets. The set contains a group model called "Desk_Assembly" at some point in its namespace, and the Desk_Assembly contains a number of groups and component models, among them, a number of referenced, instanced, "Pencil" models. The shading artist locates (in a shader library) or creates Materials for "PinkPearl" and "YellowPaint" and adds them to the set - the Materials themselves can live anywhere in the scene. The shading artist then creates collections that identify all of the erasers in all of the pencils, and all of the wooden shafts of the pencils, authoring them on "Desk_Assembly" or some other set-level prim (need not be a direct ancestor of the Pencil instances); although the declarative collections may wind up being large if there are many pencils, the procedural representation (such as authored in Katana) from which we baked the collections would generally be quite compact. To each collection, the artist binds the appropriate Material. Finally, the artist would add two material assignments (again on an ancestor of the instanced pencils) that identifies the two collections. The result might look something like:
The material:binding of the "Default" Material serves the role of a "fallback material", which will provide a material definition for any prim in the Desk_Assembly that possesses no other material bindings. It will also be present in the list of bound materials to be resolved for all prims in the assembly, but at a weaker strength
Refinement 1: Specifying Binding Strength
The Office_set above is a simple set-shading example, because none of the component models were shaded as component assets. For simple assets and uses of UsdShade, what we have presented thus far should be sufficient. But let us consider a more realistic example in a film pipeline, in which some of the assets referenced into a set are unshaded, but some have already been shaded at the asset-level, so that as the assets are referenced into the set, they bring their materials and material assignments along with them. Because bindings established on components will be stronger than bindings established on grouping models introduced at the containing set-level, it may now be difficult or impossible to assign materials "at the set level" using established and favored tools. For referenced, uninstanced models, it will be possible to override the component-level bindings via many direct overrides on the bound prims. But for instanced components, we may not even have that option if the bindings are established internally to the component models, without uninstancing the components, which we wish to avoid.
Thinking ahead also to how we will represent layered materials and facilitate such authoring actions as "here at the set-level, I want to add a layer of dirt onto all (or some) of the prim materials in some sub-tree", we may desire the ability to specify a modifier to the binding that can determine:
- whether the binding should be weaker (the default) or stronger than bindings that appear lower in namespace
- Alternatively, whether the bound material should be consumed as a named material layer by layered materials beneath the bound prim in namespace... this is speculative since we do not yet know exactly how we will specify layered materials, but it suggests that a binding modifier might better be cast as a string/token rather than bool.
Possessing such an ability can also help with a key problem for component assets that, beneath their root, reference in other complete assets, namely, that it leads to Materials showing up in unusual/unexpected places, which is a particular problem in Katana, which expects Materials to be in particular places. With the ability to add bindings on the referencing (enclosing) model that targets and overrides bindings on its referenced sub-models, we can provide a model structure in which all used-Materials for a component model can be discovered without needing to explore the entire model.
We can add a "token bindMaterialAs" metadatum to the binding schema that allows the bindings on a prim to individually specify their target strength/layer. We stipulate the metadata appear on each binding relationship rather than on the bound prim, because it keeps related data together, for diagnostic ease, and because copying a binding property from one prim to another will work without needing to remember to copy some of the source prim's metadata along with the relationship. Let us revisit the Desk_set example, and presume that some of the referenced assets may already possess material bindings that we wish to stomp, at the set-level. This would require two applications of the metadatum: one for each of the collection-based bindings, but not for the "direct" binding, because it is just intended as a fallback:
The bindMaterialAs metadatum could be extended, in the future, to accept tokens such as "layer:dirt" to facilitate layer injection, but initially would just possess the two values "weakerThanDescendants" and "strongerThanDescendants".
Refinement 2: Material Purpose
The basic material binding scheme presented here, without the bindMaterialAs refinement presented in the last section, should be sufficient for many uses of USD, but even TurboSquid's StemCell technology packages one set of textures feeding specular/gloss signals "For use in DCCs and offline renderers" and another set of textures feeding Metallic PBR "for real-time game engines". This distinction between full render materials and preview render materials is common in the VFX industry. From Pixar's own experience, we find it desirable to potentially have both full and preview materials - and the binding of geometry to these materials - available at the same time in a scene, with the ability for an application to switch between them without needing to recompose the scene.
In Pixar's pipeline, the "full" shading is authored for consumption by Renderman, and the "preview" shading authored to be consumed by Hydra's OpenGL "stream" backend - but the association of full or preview with particular renderers need not be made; for example, we should be able to produce a Renderman render that consumes preview materials. The concept we are discriminating is not which particular renderer is important, but what the goal or purpose of a given render is, and to choose the "one material used to shade a particular gprim" based on that criterion. We therefore propose to introduce the optional concept of material purpose to our binding schema and material resolution algorithm, with possible values:
- full- when the purpose of the render is entirely about visualizing the truest representation of a scene, considering all lighting and material information, at highest fidelity.
- preview- when the render is in service of some other goal (such as scene manipulation, modeling, or realtime playback). Latency and speed are generally of greater concern for preview renders, therefore preview materials might generally be designed to be "lighterweight" than full materials. However, there might be more fundamental representational differences between full and preview, should the domain benefit from it... for example, preview materials might be employed to emphasize or categorize objects along some dimension(s) not important for a final render, but useful for selection and manipulation, such as rendering all fruits one way and all vegetables another so as not to pick an unintended edible.
We see no reason to limit the possible purposes for material use in a pipeline or DCC, but to promote reliable interchange, we propose to make full and preview purposes canonical.
Having settled on the concept, we need to decide how material purpose is encoded. While it would be possible to use naming schemes in Material output terminals to encode purpose within materials, we find it more useful to encode purpose as part of the material binding itself. Recording purpose as a material binding property rather than as an internal property of Materials has two key advantages:
- It is possible for a prim to bind multiple materials for different purposes, each material having distinct public interfaces. This is a need that comes up regularly in Pixar's character pipeline, and is a problem-spot for our current "single binding" schema.
- Material resolution, using purpose as the primary selector, need only examine the bindings, not the materials themselves, in order to locate "the one material" for a gprim. For some DCC's this provides a substantial performance benefit.
To understand how purpose is encoded and could be used, let us consider an extension of the first example in this document that demonstrated hierarchical material resolution:
In this example we see three bindings, to three different materials. Of note:
- Purpose is encoded by relationship name, rather than as metadata on the binding relationship. This makes the purpose more salient in most DCC's, and also makes the encoding of multiple bindings on a single prim easier to maintain and understand (since, as metadata, we would need to arbitrarily adjust binding property names for uniqueness if we wanted to bind different materials to the same collection for different purposes).
We preserve the ability to add bindings with no specific purpose. The binding on </Bob/Geom/Belt> is the current, simple material:binding relationship. The semantic associated with a no-purpose binding is to serve as a "fallback" or "all purpose" binding, when a specific purpose is requested, but no more-specific binding is available. This addresses several concerns:
- Backwards compatibility - existing material bindings continue to work in the new scheme, in perpetuity
- Simple needs result in simple scene description - assets whose shading does not require special handling can simply bind Materials without any specific purpose, which means they will be available for all purpose requests.
- Aids deployment of "library materials" - if we have taken the time to craft reusable materials that contain both preview-usable and final-render-usable shaders, it would be unfortunate to require that each gprim we bind actually create two binding relationships targetting the same prim. We can instead just use the all-purpose binding.
Similarly for collection-based bindings... the binding property for a preview material looks like
Thus, simply from the number of tokens in a valid binding relationship's name, we immediately know its role:
- Two tokens: the fallback, "all purpose", direct binding, material:binding
- Three tokens: a purpose-restricted, direct, fallback binding, e.g. material:binding:preview
- Four tokens: an all-purpose, collection-based binding, e.g. material:binding:collection:metalBits
- Five tokens: a purpose-restricted, collection-based binding, e.g. material:binding:collection:full:metalBits
So, using the example, if we asked for the preview purpose Materials for either of the gprims, we would get </PreviewMaterial>, because the material:binding:preview binding on </Bob> provides a preview-purpose material for all descendant prims, and there are no other preview-specific bindings beneath it in namespace. If we ask for the full binding, we will get </Skin> for </Bob/Geom/Body> thanks to its material:binding:full binding, and get </Leather> for </Bob/Geom/Belt>, because its material:binding relationship will substitute for lack of a specific full purpose binding. Finally, were we to remove or block the material:binding:preview binding on </Bob> and then resolve the preview bindings, we would get no binding for </Bob/Geom/Body>, but </Leather> for </Bob/Geom/Belt>, again because the all-purpose binding fills in for preview just as it does for full.
Material Resolve: Determining the Bound Material for any Geometry Prim
We define Material Resolve as the process by which a renderer selects, for each renderable primitive, the single UsdShadeMaterial from which it should extract the data needed to build the shading/displacement shaders/operators it will use in illumination and displacement calculations. In either the existing, direct-binding-only, or in the proposed, dual direct/collection binding systems, there will often be an ordered list of possible Materials from which to choose - i.e. Materials bound to the given prim or its ancestor prims.
The material resolve algorithm in place prior to this new OM required clients to potentially search through all of the materials in the list, starting with the strongest, stopping only when they find the particular output terminal they were seeking - or if they ran out of Material candidates. In addition to the onus of needing to examine all of the materials, this meant that there might not be "one bound material" since we were, in theory, free to author surface and displacement terminals/networks on different Materials, bound at different points in the hierarchy.
With the introduction of Material Purpose, we seek to simplify the resolve step by parameterizing it with material purpose, and requiring that each Material contain all the shading required for the purpose, so that there can be a single result Material from the resolve process. At the client API level, we accept just a single purpose (preview or full), to which is automatically added the all-purpose binding as a second, weaker binding source. If needed, we can optionally provide API that returns the actual purpose (specified or all-purpose fallback) that we resolved, as well as the prim on which the winning binding was found, etc.
Following is very un-optimized pseudocode for GetBoundMaterial(), for a given prim prim:
Currently, material binding is exposed as a handful of static methods on UsdShadeMaterial. Given the substantial added functionality and API we are adding to binding, we propose to follow a similar path to that taken with UsdSkel, and add a UsdShadeMaterialBindingAPI, to be applied to geometry prims, which contains all binding-related mutators and queries.
Analysis of Collection-Based Binding
- This encoding maps well to how material assignment is often authored in Katana, especially in set-shading workflows. It also closely matches MaterialX's encoding of material assignments. The lack of ability to assign materials to sub-instanced geometry was also one of the early (awaiting resolution) stumbling blocks for the Autodesk engineering team investigating USD-encoded shading.
- This encoding captures grouping information that we have found useful in the past, but have needed to compute more expensively, just-in-time. For example, optimizations like shader coalescing in the REYES architecture derived benefits from finding all the gprims bound to each shader, so that the shader could be emitted and bound just once, followed by all affected gprims. While that particular optimization may no longer be relevant in the RIS architecture, the fact that we can now easily reason about the collection of objects bound to each shader may be beneficial in other computations.
- This encoding allows a more formal handoff from modeling to shading, especially for complex models. Rather than relying on naming conventions to identify all the window glass in a building, or all the same-construction bolts in a car, the modeler can craft and ship the collections that identify these generally uniformly-shadable groups of prims (and facesets).
- Although both the direct-binding encoding and collection-based encoding require a traversal of ancestors to discover any single gprim's assignment, the amount of work required to answer the question for a single gprim is tremendously more expensive for the collection-based encoding, because we may need to resolve all collections bound to all of the gprim's ancestors to perform membership tests in all of them. It is a practical requirement that we provide an efficient, memoized computation for material assignment for use by any client that wants to look up assignments one gprim at a time, which currently is all known clients.
- If material-assignment collections and bindings are authored on component model roots, as one might reasonably decide to do, and the models are instanced (as is common in Pixar's pipeline), then the "Masters" for these instances on the UsdStage will have no binding information, because we do not compose any properties on the root prims of masters, whose properties would all be overridable by the instances themselves. The collections and bindings will be expressed on the instance roots themselves, which, by the nature of instancing, can freely vary, per-instance. Given that we do not expect to need or want to vary the bindings on every instance, we would prefer to be able to reap the benefits of instancing that we get from knowing that data is encapsulated inside a master, and therefore need be computed only once and shared by all instances. Therefore we propose to promote authoring collections and bindings inside component models. In Pixar's pipeline, this would mean authoring collections and material bindings on </MODEL/Geom>. This need not be a schema requirement, but computations will be much more efficient if the pattern is followed.
We have considerable flexibility in how we translate collection-based bindings in pxrUsdIn. With our eye on round-tripping, we might want to preserve the UsdShade encoding as closely as possible; the biggest impact (apart from a new data model/presentation for downstream-pipeline users) is the need to create a new material resolver op that gets injected into the proper place in the recipe. Steve L believes that it should be able to leverage Katana's existing infrastructure for pattern-based evaluation, and should be at no substantial performance disadvantage over direct bindings. However, although we will likely find it convenient to encode the majority of our collection-based bindings such that the collections are authored on ancestors of the prims targeted by the collection, there is no performance reason in USD for requiring that collections target only objects beneath their owning prim, and we do not want to place such a restriction on clients ability to organize collections. The Katana implementation must be prepared for such a situation, though we will advertise "best practice" for material collection authoring to get the best Katana consumption performance.
Collection-based material binding maps quite well to Maya's native encoding. Maya does not support, however:
- Hierarchical resolution of materials (as described in the opening example of this document). Therefore, this is a problem we already face (and currently ignore) in importing USD into Maya.
- Any notion of "binding point" in the geometry hierarchy. In Maya, all collections (sets) and materials are global in scope (except for the unique, per-instance binding arrays, which are logically associated with the instance in the dag), and material-binding sets simply apply to all targets of the set. We would need to figure out a way to preserve binding information on Maya import if round-tripping is a concern.
We may hope for a closer match to Maya with its MaterialX-inspired pattern/collection bindings.
We have not yet explored Houdini I/O implications deeply, but hope to soon.
There is no question that the "material resolve" algorithm for any given, single prim, can become substantially more expensive with collection-based material assignment. Previously, we needed only to resolve a single relationship for each ancestor of any prim. Now, in addition to resolving that same relationship, we may need to evaluate the prim's path against any number of collections targeted by the material:bind:collections relationships. The only reasonable way to resolve materials involves either a top-down (pre or as part of main) traversal of the scene to discover, compute, and cache results efficiently, or with memoization. The former approach is more threading-friendly, and the option we will likely pursue for UsdImaging, however the "computer" we would provide would also submit to requests that would memoize only the sub-computations needed to answer queries for single prims, allowing subsequent queries to reuse the results, when applicable.
A potential concern about memory consumption has been raised over such a caching computer; we will need to monitor memory consumption in both batch and interactive rendering applications.
Implication on Renderer Instancing
A primary motivator for this change to material binding is to support (re)shading of instanced assets without needing to break the instancing. This does not deteriorate the two primary advantages we derive from instancing - reduced prim count on a UsdStage, and sharing of diced geometry in renderers - but it does mean renderers that directly support aggregate instancing (Renderman and HdStream for sure, not Arnold, unsure about other renderers) will need to gracefully fall back to gprim-level instancing when two aggregate instances bind different sets of materials. Is this something that Hd itself addresses? Or does it want to pass along (aggregate instance, resolved materials closure) pairs to backends to apply/transform as they deem necessary?
It is interesting to note that Houdini already allows a similar, but very much more powerful and procedural mechanism that allows material and material-parameter overrides that can penetrate into "packed prims", Houdini'd equivalent of aggregate instances. This mechanism is called Material Stylesheets; it is similar in spirit to Pixar's refinement of Katana's "Material Override" operator (MOP) when the MOP is used in the mode that ties its application to specific parts of the geometry hierarchy - however MOPs cannot vary instance-specific geometry inside of Katana native instances. On the other hand, Houdini's Material Stylesheets can only be consumed (we believe) by the Mantra renderer.
Although there is little possibility we can answer this question prior to having a material layering scheme, the question remains of what interaction collection-based assignment would have with material layering.