Referencing MDL Definitions in OpenUSD#

USD is agnostic about the underlying shading and rendering system. It uses Material prims, Shader prims, and UsdShade networks to define materials with parameters and shader graphs. NVIDIA defines now how materials and functions of the NVIDIA Material Definition Language (MDL) can be referenced in USD. Both, MDL material definitions as well as function definitions are represented as USD Shader prims. Unlike more conventional surface shaders, MDL materials define not only the surface light scattering behavior, but also emissive behavior, volumetric properties of an enclosed volume and geometric properties such as normal or displacement mapping. A USD shader prim representing an MDL material can therefore be connected multiple times to the USD Material prim, once for each of its defined outputs: surface, displacement, and volume. The outputs of a USD material prim use a sourceType to select the connected shaders by their source type. We use “mdl” as sourceType. A .usda file example excerpt looks as follow:

#usda 1.0
def Material "sample"

{
    token outputs:mdl:surface.connect = </sample/flex_material.outputs:out>
    token outputs:mdl:volume.connect = </sample/flex_material.outputs:out>
    token outputs:mdl:displacement.connect = </sample/flex_material.outputs:out>
    ...

    def Shader "flex_material"
    {
        token outputs:out
        ...
    }
}

MDL definitions are programs stored in MDL files, which fits nicely with the USD info:sourceAsset attribute to reference the implementation. We store the MDL module file path (Section 2.2 and 15.1, MDL Language Specification) in the source-asset attribute. MDL files can contain multiple definitions, which we need to distinguish when referencing them in the USD Shader prim. In close collaboration with Pixar, the solution is the new info:subIdentifier attribute available since USD Release Version 20.02. We store the fully-qualified MDL definition name in the sub-identifier attribute. A .usda file example excerpt that references the ::nvidia::core_definitions::flex_material as the implementation of a Shader prim:

def Shader "flex_material"
{
    token outputs:out
    uniform token info:implementationSource = "sourceAsset"
    uniform asset info:mdl:sourceAsset = @nvidia/core_definitions.mdl@
    uniform token info:mdl:sourceAsset:subIdentifier = "flex_material"
...
}

The following example puts all together and adds a couple of material parameters:

#usda 1.0
def Material "sample"

{
    color3f inputs:base_color          = (0.5, 0.5, 0.5)
    float   inputs:anisotropy          = 0
    float   inputs:anisotropy_rotation = 0

    token outputs:mdl:surface.connect = </sample/flex_material.outputs:out>
    token outputs:mdl:volume.connect = </sample/flex_material.outputs:out>
    token outputs:mdl:displacement.connect = </sample/flex_material.outputs:out>

    def Shader "flex_material"
    {
        uniform token info:implementationSource = "sourceAsset"
        uniform asset info:mdl:sourceAsset = @nvidia/core_definitions.mdl@
        uniform token info:mdl:sourceAsset:subIdentifier = "flex_material"

        color3f inputs:base_color.connect = </sample.inputs:base_color>
        float   inputs:anisotropy.connect = </sample.inputs:anisotropy>
        float   inputs:anisotropy_rotation.connect = </sample.inputs:anisotropy_rotation>
    }
}