All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UsdShade : USD Shading Schema

UsdShade provides schemas and behaviors for creating and binding materials, which encapsulate shading networks.

UsdShade Networks

UsdShade provides schemas and behaviors for creating shading networks (UsdShadeNodeGraph) and materials (UsdShadeMaterial). The networks are composed of UsdShadeShader objects, as well as other UsdShadeNodeGraph.

Objects in a network are connected together and to their encapsulating Material using the UsdShadeConnectableAPI schema, which allows one to create UsdShadeInput and UsdShadeOutput (which are UsdAttribute schemas), and connect them using UsdAttribute connections.

Here's a python example.

1 # create material
2 materialPath = Sdf.Path('/Model/Materials/MyMaterial')
3 material = UsdShade.Material.Define(stage, materialPath)
4 
5 # create shaders
6 downStreamShader = UsdShade.Shader.Define(
7  stage, materialPath.AppendChild('Upstream'))
8 upstreamShader = UsdShade.Shader.Define(
9  stage, materialPath.AppendChild('Downstream'))
10 
11 # Connect
12 inputPort = downStreamShader.CreateInput(
13  'DownstreamInput', Sdf.ValueTypeNames.Float)
14 inputPort.ConnectToSource(upstreamShader, 'UpstreamOutput')

This will yield a material with two connected nodes.

#usda 1.0
def "Model"
{
def "Materials"
{
def Material "MyMaterial"
{
def Shader "Upstream"
{
float inputs:DownstreamInput.connect =
</Model/Materials/MyMaterial/Downstream.outputs:UpstreamOutput>
}
def Shader "Downstream"
{
float outputs:UpstreamOutput
}
}
}
}

Encapsulation and Sharing

Note
In UsdShade, all shaders are UsdPrims or just "prims". However, in deference to the larger body of technical discourse on shading, we will refer to them as "nodes" in this discussion.

Shading nodes should be encapsulated in a containing object, and are not generally used in isolation.

Shading networks can be organized into coherent packaged units (UsdShadeNodeGraph), with their own public parameters exposed and connected to the internal nodes. In this scenario, the UsdShadeNodeGraph is a parent or ancestor prim to all of the UsdShadeShader prims in the network, and serves as the point of encapsulation - the UsdShadeNodeGraph prim can then be referenced* into other, larger networks as a building block, with its entire network intact. When referenced into larger networks, NodeGraphs can also be instanced so that they appear as a single prim in the network, and can be processed more efficiently when referenced from multiple locations.

If the network of shading nodes is directly consumable as a "shader" of a type known to some client renderer (e.g. a surface shader), then the encapsulating parent/ancestor should be declared as a UsdShadeMaterial, which is a container that can also be bound to geometries or collections. Materials can also be reused and instanced, retaining the same network but allowing top-level "Material Interface" parameter to be authored uniquely.

To expose a parameter to the container, we use the same mechanism that connects nodes.

1 # Expose a parameter to the public interface
2 internalPort = upstreamShader.CreateInput(
3  'internalPort', Sdf.ValueTypeNames.Float)
4 exposedPort = material.CreateInput(
5  'ExposedPort', Sdf.ValueTypeNames.Float)
6 exposedPort.Set(1.0)
7 internalPort.ConnectToSource(exposedPort)

Which will yield a public interface parameter called 'ExposedPort' on the UsdShadeMaterial called 'MyMaterial', and set its default value to 1.0

#usda 1.0
def "Model"
{
def "Materials"
{
def Material "MyMaterial"
{
float inputs:ExposedPort = 1
def Shader "Upstream"
{
float inputs:DownstreamInput.connect =
</Model/Materials/MyMaterial/Downstream.outputs:UpstreamOutput>
}
def Shader "Downstream"
{
float inputs:internalPort.connect =
</Model/Materials/MyMaterial.inputs:ExposedPort>
float outputs:UpstreamOutput
}
}
}
}

To expose an output of a node network as an output of a NodeGraph, or as a "terminal output" of a Material, we again use the same connection API, except that now we are connecting an Output to another Output (in effect, forwarding the Output from a node to its encapsulating container):

1 # The output represents the result of the shader's computation. For
2 # complex types like "surface illumination" we use the type Token as
3 # a standin for the type specific to the renderer
4 outPort = surfaceShader.CreateOutput(
5  'out', Sdf.ValueTypeNames.Token)
6 surfaceTerminal = material.CreateOutput(
7  'surface', Sdf.ValueTypeNames.Token)
8 # For outputs, it is the container's Output that connect's to the Node's
9 # output
10 surfaceTerminal.ConnectToSource(outPort)

Which will yield a public interface parameter called 'ExposedPort' on the UsdShadeMaterial called 'MyMaterial', and set its default value to 1.0

#usda 1.0
def "Model"
{
def "Materials"
{
def Material "MyMaterial"
{
token outputs:surface.connect =
</Model/Materials/MyMaterial/Surface.outputs:out>
def Shader "Surface"
{
token outputs:out
}
}
}
}

UsdShade Based Shader Definition

UsdShade has an NdrParserPlugin (UsdShadeShaderDefParserPlugin) that enables shader definitions to be encoded as USD scene description using the schemas available in UsdShade. A discovery plugin can open a USD stage containing shader definitions and populate the shader registry with nodes using the API UsdShadeShaderDefUtils::GetNodeDiscoveryResults().

A USD file containing UsdShade-based shader definitions must adhere to the following rules, in order to produce valid SdrShaderNode s in the shader registry:

  • Every concrete shader prim at the root of the composed UsdStage should represent a new and complete shader definition. Inherits, references and other composition arcs may be used to avoid redundant scene description.
  • The shader prim's name becomes the unique identifier of the corresponding shader node in the registry. A shader's identifier is a concatenation of the
    1. family name of the shader,
    2. any type variations pertaining to the shader and
    3. the shader version, which can contain one or two ints representing the major number and an optional minor number. The type variations and shader version are optional parts of a shader identifier (i.e. not all shader identifiers may include them). If present, the different parts of the identifier are delimited by an underscore. Using UsdShadeShaderDefUtils::SplitShaderIdentifier, a shader's identifier can be split into the family name, implementation-name of the shader node (which includes the family name and the type information) and the shader version. For example,
    • if the shader prim is named "MultiTexture", the family name of the SdrShaderNode will be "MultiTexture". The corresponding shader-node's implementation name will also be "MultiTexture" and its version will be empty.
    • if the shader prim is named "MultiTexture_float2", the family name of the shader will be "MultiTexture" and its implementation name will be "MultiTexture_float2". Its version will be empty.
    • if the shader prim is named "MultiTexture_3", the family name of the shader will be "MultiTexture". It's implementation name will also be "MultiTexture" and its version will be 3.
    • if the shader prim is named "MultiTexture_float2_3_1", the family name of the shader will be "MultiTexture". The implementation name will include the type information and be set to "Primvar_float2".
  • The info:id attribute value of the shader, if authored, must match the name of the shader prim (i.e. the identifier of the SdrShaderNode).
  • The info:implementationSource of the shader must be UsdShadeTokens-> sourceAsset. There must be one or more "info:SOURCE_TYPE:sourceAsset" attributes that point to resolvable shader implementations for different source types (eg, glslfx, OSL etc.).
  • Shader prims, their inputs and outputs can contain sdrMetadata values meant to be recorded in the shader registry. The keys in the sdrMetadata dictionary correspond to the keys in SdrNodeMetadata and SdrPropertyMetadata. The only exceptions are as follows:
    • defaultInput metadatum on shader inputs gets translated to a more obscure key value of __SDR__defaultInput (which is the value of SdrPropertyMetadata->DefaultInput) in the metadata dictionary recorded by SdrRegistry.
    • Setting sdrMetadata["primvarProperty"]="1" on a shader input implies that the input names a primvar to be consumed by the shader. This causes '$' + inputName to be included in the SdrShaderNode->Primvars metadata on the SdrShaderNode. Note that it's not translated to metadata on the property itself.
  • connectability metadata authored on UsdShadeInputs gets translated to SdrPropertyMetadata->Connectable. Connectability value of "interfaceOnly" is converted to connectable="0". Connectability value of "full" is converted to connectable="1".
  • SdfAssetPath (or asset) valued shader inputs are automatically tagged with sdr metadata SdrPropertyMetadata->IsAssetIdentifier="1".

Here's an example shader definition file with comments explaining the various bits.

#usda 1.0
# The prim name becomes the SdrShaderNode's identifier.
def Shader "Primvar_float_2" (
doc = "Version 2 of a Primvar node that outputs a float"
sdrMetadata = {
# This identifies the shader's role in the shading network as being
# a primvar reader.
token role = "primvar"
# The following sdr-metadatum could be authored on the node directly
# <b>in lieu of</b> authoring primvarProperty="1" on
# inputs:primvarName.
# string primvars = "$primvarName"
}
)
{
uniform token info:implementationSource = "sourceAsset"
# If primvarReader.oso can be resolved to an existent asset, then a
# SdrShaderNode is created with sourceType=OSL and sourceUri pointing
# to the resolved primvarReader.oso file.
uniform asset info:OSL:sourceAsset = @primvarReader.oso@
# If primvarReader.glslfx can be resolved to an existent asset, then
# another SdrShaderNode is created with sourceType=glslfx and sourceUri
# pointing to the resolved primvarReader.glslfx file.
uniform asset info:glslfx:sourceAsset = @primvarReader.glslfx@
token inputs:primvarName (
connectability = "interfaceOnly"
sdrMetadata = {
# This causes '$primvarName' to be appended to the
# SdrNodeMetadata->Primvars metadata on the SdrShaderNode.
string primvarProperty = "1"
}
doc = """Name of the primvar to be fetched from the geometry."""
)
# Asset valued inputs are automatically tagged with
# sdrMetadata[SdrPropertyMetadata->IsAssetIdentifier] = "1".
asset inputs:primvarFile = @@ (
connectability = "interfaceOnly"
doc = """File containing some primvar info."""
)
float inputs:fallback = 0.0 (
doc = """Fallback value to be returned when fetch failed."""
sdrMetadata = {
# This gets translated to SdrPropertyMetadata->DefaultInput="1"
# on the "fallback" SdrShaderProperty.
token defaultInput = "1"
}
)
float outputs:result
}