Use a Built-In Node#
Goal. Get a common operation without writing any GPU code.
Before you start. Publish, Overwrite and Read Back AOVs.
The Shape#
A built-in node is named rather than sourced. There is no GPU file and no launch script:
def Shader "AddNode"
{
uniform token info:implementationSource = "id"
uniform token info:id = "spg:rtx.spg.stdlib/Add"
opaque inputs:A.connect = <../LdrColor.omni:rtx:aov>
opaque inputs:B.connect = <../LdrColor.omni:rtx:aov>
opaque outputs:Result
}
How It Works#
The scene adds LdrColor to itself with the built-in Add node, doubling brightness with
saturating arithmetic, then halves the resolution with the built-in Scale node. Highlighted:
the two lines that name each one. Everything else is the ordinary wiring of any graph.
stdlib_scene.usda, from the runnable built-in nodes example#def Scope "Render"
{
def RenderProduct "StdlibDemo"
{
uniform int2 resolution = (1280, 720)
rel camera = </World/Camera>
rel orderedVars = [ <LdrColor>, <Downscaled> ]
def RenderVar "LdrColor"
{
uniform string sourceName = "LdrColor"
opaque omni:rtx:aov
}
def RenderVar "Downscaled"
{
uniform string sourceName = "Downscaled"
opaque omni:rtx:aov.connect = <../ScaleNode.outputs:Output>
}
def Shader "AddNode"
{
uniform token info:implementationSource = "id"
uniform token info:id = "spg:rtx.spg.stdlib/Add"
opaque inputs:A.connect = <../LdrColor.omni:rtx:aov>
opaque inputs:B.connect = <../LdrColor.omni:rtx:aov>
opaque outputs:Result
}
def Shader "ScaleNode"
{
uniform token info:implementationSource = "id"
uniform token info:id = "spg:rtx.spg.stdlib/Scale"
float inputs:scaleX = 0.5
float inputs:scaleY = 0.5
opaque inputs:Input.connect = <../AddNode.outputs:Result>
opaque outputs:Output
}
}
}
info:implementationSource = "id"tells SPG this shader is identified by an SPG node ID, not a source asset.info:idnames the built-in node ("spg:rtx.spg.stdlib/Add","spg:rtx.spg.stdlib/Scale"). Thespg:prefix scopes SPG node IDs away from MaterialX and UsdPreview shader IDs.No GPU source file, launch script, or external shader definition is needed. The entire graph is defined inline in the scene file.
Shader-to-shader chaining works the same way as for nodes you write yourself:
ScaleNode.inputs:Inputconnects directly toAddNode.outputs:Result.Value-inputs (
scaleX,scaleY) also behave the same way. Scene files can override defaults per shader instance.orderedVarslists only the input (LdrColor) and final output (Downscaled). The intermediate brightened result from AddNode is internal to the chain.
A built-in is the whole node, not a starting point. You get it by name and you get its behaviour as it is: no GPU source to edit, no launch script to adjust, and no way to change what it does beyond the value-inputs it declares. The moment you need something it does not do, you are writing a node, and the two cannot be mixed within one shader. What the built-ins are is Built-In Node Catalogue.
Verify It Worked#
Compare both the dimensions and the values against the input. Add and Scale together double
brightness with saturating arithmetic and halve each dimension, so the output is exactly half the
input’s width and height, and a pixel at value v comes back at min(2v, 255). A value that
did not double means Add never ran; a size that did not halve means Scale never ran. Both
operations are exact in integers, so the
built-in nodes example rebuilds them on the host
and compares with no tolerance, printing
largest difference from the host-computed result: 0.
When It Goes Wrong#
The node is not found: check the
info:idagainst Built-In Node Catalogue. A misspelling is the usual cause. An ID outside that table may belong to another renderer component; it is not a supported authoring surface.The node rejects its input: these operate on 2D textures only, and not on integer formats.