Nodes and Node Graphs

Introduction

In MaterialX, shading computations are represented via a graph of nodes.

The basic components for all graphs are:

  1. Nodes
  2. Ports
  3. Connections

Notation: XML notation will be used to denote types of components, such that a component is bracketed by start and end angle braces: < and >. For example a node is denoted as <node>.

Nodes

A <node> can be thought of as representing an atomic unit of computation. They are instances of node definitions . Definitions determine the public interface for a computation. Details about definitions can be found here.

The API class is called Node.

There are various semantic types for nodes within MaterialX. For the purposes of shader computation, we will consider nodes which either:

  1. Perform a computation on or route input values to produce output values. This includes nodes which may extract or combine channels of an input or output.
  2. Perform conditional branching logic (such as an 'if' or 'switch' condition)
  3. Route application data such as constants, geometry, or images.

Each node must have a unique string identifier (name). This identifier can only contain alphanumeric characters, excluding the path separator character: /. To aid with identifier naming, Materialx provides identifier creation utilities (e.g. createValidChildName() )

For the purposes of illustration, rectangular boxes are used to denote nodes. For example, the following diagram shows 3 nodes named "node1", "node2", and "node3"

graph TD; node1 node2 node3
Interfaces and Ports

Each node's definition is specified by an interface which contains 0 or more port elements or ports for short. All ports are "strongly typed" ( all ports must have a defined type ).

The API class for ports is PortElement.

  • Ports which receive data are called inputs (<input>)
  • Ports which send data are called outputs (<output>).
  • A port's type is defined by the API class TypedElement, Types are defined as part of the standard library definitions using a `` element type. The library glossary lists these allowed types. An example type is color3 to represent 3 channel colors.

While a definition specifies the entire interface, it is not required that any of these ports be explicitly specified on the node instance:

  • If and only if a connection is required and / or a non-default value is required does an input need to be specified.
  • Outputs do not need to be specified explicitly for a node.

Notation: A dot (".") will be used to indicate that an input or output is part of a node. Thus in the diagram shown, the notation used to indicate "Input1" would be "Node.Input1".

Containers

A graph element is a container for a set of nodes.

The API class is called GraphElement.

Graph elements themselves cannot be created. Instead either of the following can be instantiated:

  • Document: A MaterialX document (<document>) is a top level container which can be thought as corresponding to a single "file". The API class: Document.

  • Node graph: A container which resides within a document or another node graph <nodegraph>. The API class is: NodeGraph.

Graph elements can contain:

  • 0 or more nodes or node graphs and
  • 0 or more direct child <input>s or <output>s.
Scope and Path Notation

Direct children of a graph element are considered to be in scope.

The follow scoping rules apply:

  • Every direct child must have a unique string identifier (1)
  • <document>s and <nodegraph>s cannot contain child <document>s. There is no concept of a document referencing another document.
  • <input>s cannot be instantiated as direct children of a <document>.

(1) Note that <document>s have no identifier by default.

Document Scope

The diagram below shows a <document> with valid children. "my_node" is a node instance, and "my_nodegraph" a node graph.

graph TD subgraph document my_node subgraph my_nodegraph end end
Node Graph Scope

This example shows valid children for a node graph called "my_nodegraph"

  • an <input> called "input1",
  • an <output> called "output1"
  • a node called "node1"
  • a <nodegraph> called "nodegraph2" which contains a child node called "node2"
graph TD subgraph nodegraph2 node2 end subgraph my_nodegraph input1(input1) style input1 fill:#1b1,color:#fff output1(output1) style output1 fill:#0bb,color:#fff node1 nodegraph2 end
Paths

Parent / child relationships can be described using a string path with forward-slashes ("/") being used as path separators:

<parent identifier>/<child identifier>

In the above example, the path to "node2" would be my_nodegraph/nodegraph2/node2 while the path to input1 is my_nodegraph.input1.

Connections

Connections can be formed between node instance or nodegraph ports to create graphs. While node graphs can have no outputs, they generally of no use as they cannot be connected to anything.

The following is an example of a node and a node graph showing various typed inputs and outputs. These <input>s and <output>s on <node>s or <nodegraph>s define what is connectable.


The above figure shows ports which are color coded to indicate whether they are inputs or outputs. The arrows which are connect to ports show the direction that data flows.

Key attributes to consider for connectivity for an <input> includes:

  • type: Every port has a type with the list of valid types defined by the standard library definitions. float and integer tuples and arrays as well as string and filename are common types.
  • uniform: A input can be marked as only accepting uniform values (non-varying across input geometry).
  • channel: An input can indicate that a specific channel be extracted from incoming data . For example the x channel of a vector can be extracted. A "dot" notation is used to specified channels on data

  • <type>.<channel identifier>

The rules for connection validity are as follows. It is assumed that all connections are within the same scope.

  • A <node> or <nodegraph> <output> may be connected one or more <input>s on another node or nodegraph <input>.
  • Ports cannot connect to themselves or other ports on the same node or node graph. (i.e. cycles / loops are not allowed)
  • An <output> can connect to one or more <inputs>. (fan-out allowed)
  • An <input> can be connected to at most one <output>. (fan-in disallowed)
  • Ports of different types cannot be connected. This takes in to consideration the type produced after applying any channel extraction.

    For example, a single channel which is extracted from a float vector can be connected to a float input.

  • Ports with different uniform attributes values cannot be connected.
  • string and filename typed constants can be connected to filename types.

For a given connection the source node / port is considered to be upstream of the second node / port, which is downstream.

The following is an example diagram showing how inputs, outputs and connections are drawn:

  1. Downstream input and upstream outputs are color-coded rounded rectangles.
  2. Connections are denoted with lines. Arrows denote the direction of data flow (pointing downstream)
graph LR; input1(input1) output1(output1) style output1 fill:#1b1,color:#fff style input1 fill:#0bb,color:#fff output1 --> input1
Inter Node / Graph Connections

The possible pair-wise configurations are shown below:

  1. Node-to-node: This example shows node1's input input1 is connected to node2's output called output1.
    graph LR; input1(node1.input1) output1(node2.output1) style output1 fill:#1b1,color:#fff style input1 fill:#0bb,color:#fff output1 --> input1
  2. Node-to-node graph connection:
  3. graph LR; output1(node.output1) subgraph nodegraph input1(.input1) end style output1 fill:#1b1,color:#fff style input1 fill:#0bb,color:#fff output1 --> input1
  4. Node graph-to-node connection:
  5. graph LR; subgraph nodegraph output1(.output1) end input1(node.input1) style output1 fill:#1b1,color:#fff style input1 fill:#0bb,color:#fff output1 --> input1
  6. Node graph-to-node graph connection:
  7. graph LR; subgraph nodegraph3 output1(.output1) end subgraph nodegraph input1(.input1) end style output1 fill:#1b1,color:#fff style input1 fill:#0bb,color:#fff output1 --> input1
  8. Combining some of the variants together a shader graph could look like the following. Fan-out is shown for one of the outputs on node graph nodegraph3.
  9.           
                
    graph TB node3(node3.out) --> node2(node2.in1) node2 --> node0.input1(.input1) node2 --> nodegraph2.input2(.input2) subgraph nodegraph3; nodegraph3.output1(.output1) end nodegraph3.output1 --> node0.input2(.input2) nodegraph3.output1 --> nodegraph2.input1(.input1) subgraph node0; node0.output1(.output1) node0.input2 node0.input1 end subgraph nodegraph2; nodegraph2.input1(.input1) nodegraph2.input2(.input2) nodegraph2.output1(.output1) end subgraph node1 node0.output1 --> node1.input1(.input1) nodegraph2.output1 --> node1.input2(.input2) end style node3 fill:#1b1,color:#fff style node0.output1 fill:#1b1,color:#fff style nodegraph3.output1 fill:#1b1,color:#fff style nodegraph2.output1 fill:#1b1,color:#fff style node0.input1 fill:#0bb,color:#fff style node0.input2 fill:#0bb,color:#fff style nodegraph2.input1 fill:#0bb,color:#fff style nodegraph2.input2 fill:#0bb,color:#fff style node2 fill:#0bb,color:#fff style node1.input1 fill:#0bb,color:#fff style node1.input2 fill:#0bb,color:#fff
Intra Graph / Interface Connections

The direct <input> and <output> children of a <nodegraph> are considered to be the exposed interface of the graph.

It is only through these interfaces that connections can be made to ports which are outside the scope of the nodegraph. These interfaces can in turn be connected to node ports within the scope of the node graph such that:

  1. A <nodegraph> <input> may be connected to one or more node's <input> within the same graph.
  2.           
              
    graph TB subgraph nodegraph2 .input1(.input1) --> node1.input1 .input1 --> node2.input2 .input2(.input2) --> node2.input1 style .input1 fill:#1b1,color:#fff style .input2 fill:#1b1,color:#fff end
  3. The <output>s of a node within the same nodegraph may be connected to one or more nodegraph outputs.
  4. graph TB subgraph nodegraph2 node1.output1 --> .output1(.output1) node1.output1 --> .output2(.output2) node1.output2 --> .output3(.output3) style .output1 fill:#0bb,color:#fff style .output2 fill:#0bb,color:#fff style .output3 fill:#0bb,color:#fff end