Dynamic Parameters

A sub-set of nodes in Katana feature “dynamic” parameters; dynamic in that their existence on a node is not fixed at node creation time, but rather is on attributes from the incoming scene or some other asynchronous process.

For example, upon adding a shader to a Material node, Katana must query the renderer for the set of parameters the shader exposes, as well as their default values. Until these queries complete and the Parameters tab is updated, the parameters representing the various shader configuration options do not yet exist.

Should you then wish to edit this material in another Material node downstream, this downstream node must query the incoming scene in order to display the existing material’s values in the Parameters tab. This effectively means the parameters on the downstream node must update dynamically based on our upstream node.

RenderSettings is another node that makes use of dynamic parameters. This node depends on the attributes under the renderSettings group attribute at /root and is used for the configuration of render settings such as output resolution.

Nodes with dynamic parameters include the following:

  • Material

  • NetworkMaterialParameterEdit

  • RendererProceduralArgs

  • RenderOutputDefine

  • TransformEdit

  • Shading nodes such as ArnoldShadingNode and PrmanShadingNode

When creating such nodes in Script mode using NodegraphAPI.CreateNode(), or getting a new reference to an existing node using calls, such as NodegraphAPI.GetNode(), Katana does not immediately create the dynamic parameters on the node, but only those set locally. While this behavior might go unnoticed when running Katana in UI mode (the Parameters tab is updated with any dynamic parameters in the normal course of UI event processing) it has very real implications if you are running Katana in script mode. In this context, your Python script executes synchronously and there is no UI event loop to take care of updating a node with the result of, say, querying the incoming scene graph or querying the renderer as to which parameters a shader exposes.

Instead, before you attempt to access these dependent parameters, you must first call checkDynamicParameters() on the node. This call blocks until the node’s dynamic parameters have been populated. Failing to call this method results in a getParameter() call returning None (because the parameter does not yet exist) or returning a parameter whose value is out-of-date (because changes from other parameters would cause its value to be different).

As an example, suppose you have an existing scene myKatanaProject.katana that contains a Material node with a PRMan surface shader, with locally-set values for Ks, but with a default value for Kd:

# query_parameters.py

from Katana import KatanaFile, NodegraphAPI

KatanaFile.Load('myKatanaProject.katana')

materialNode = NodegraphAPI.GetNode('Material')

# This call is necessary to populate the node with its "dynamic" parameters, if
# any.
materialNode.checkDynamicParameters()

# Access the Ks and Kd parameter on the material node.
specularParam = materialNode.getParameter('shaders.prmanSurfaceParams.Ks.value')
diffuseParam = materialNode.getParameter('shaders.prmanSurfaceParams.Kd.value')

# Sanity checks.
assert specularParam, "No value for the Ks parameter!"
assert diffuseParam, "No value for the Kd parameter!"

print("specular=%r, diffuse=%r" % (specularParam.getValue(0.0),
                                   diffuseParam.getValue(0.0)))

Running this script using katana --script query_parameters.py prints both the locally-set value for Ks and the default value for Kd. Failing to make the call to checkDynamicParameters() causes the second assertion to be tripped.

Below is a more complex example that demonstrates the creation of an Arnold Network Material:

from Katana import NodegraphAPI

###############################################################################
imageNode1 = NodegraphAPI.CreateNode('ArnoldShadingNode', NodegraphAPI.GetRootNode())
NodegraphAPI.SetNodePosition(imageNode1, (-75, 200))

imageNode1.getParameter('name').setValue('diff_image', 0.0)
imageNode1.getParameter('nodeType').setValue('image', 0.0)

# Call checkDynamicParameters() to ensure parameters dependent on "nodeType"
# are made available.
imageNode1.checkDynamicParameters()
imageNode1.getParameter('parameters.filename').createChildString(
    'hints', repr({
        'widget': 'filename',
        'dstPage': 'basics',
        'dstName': 'diff_texture'}))


imageNode2 = NodegraphAPI.CreateNode('ArnoldShadingNode', NodegraphAPI.GetRootNode())
NodegraphAPI.SetNodePosition(imageNode2, (75, 200))

imageNode2.getParameter('name').setValue('spec_image', 0.0)
imageNode2.getParameter('nodeType').setValue('image', 0.0)

# Call checkDynamicParameters() to ensure parameters dependent on "nodeType"
# are made available.
imageNode2.checkDynamicParameters()
imageNode2.getParameter('parameters.filename').createChildString(
    'hints', repr({
        'widget': 'filename',
        'dstPage': 'basics',
        'dstName': 'spec_texture'}))

###############################################################################
standardNode = NodegraphAPI.CreateNode('ArnoldShadingNode', NodegraphAPI.GetRootNode())
NodegraphAPI.SetNodePosition(standardNode, (0, 0))

standardNode.getParameter('name').setValue('standard_node', 0.0)
standardNode.getParameter('nodeType').setValue('standard', 0.0)

# Call checkDynamicParameters() to ensure parameters dependent on "nodeType"
# are made available.
standardNode.checkDynamicParameters()
standardNode.getParameter('parameters.Ks.enable').setValue(1, 0.0)
standardNode.getParameter('parameters.Ks.value').setValue(0.7, 0.0)
standardNode.getParameter('parameters.Ks_color.enable').setValue(1, 0.0)
standardNode.getParameter('parameters.Ks_color.value.i0').setValue(0.2, 0.0)
standardNode.getParameter('parameters.Ks_color.value.i1').setValue(0.7, 0.0)
standardNode.getParameter('parameters.Ks_color.value.i2').setValue(1.0, 0.0)

standardNode.getInputPort('Kd_color').connect(imageNode1.getOutputPort('out'))
standardNode.getInputPort('Ks_color').connect(imageNode2.getOutputPort('out'))

# Create "hints" parameters that describes how these parameters should be
# exposed in the material's public interface.
standardNode.getParameter('parameters.Kd').createChildString(
    'hints', repr({'dstPage': 'basics', 'dstName': 'Kd'}))
standardNode.getParameter('parameters.Ks').createChildString(
    'hints', repr({'dstPage': 'basics', 'dstName': 'Ks'}))

###############################################################################
networkMaterialNode = NodegraphAPI.CreateNode('NetworkMaterial', NodegraphAPI.GetRootNode())
NodegraphAPI.SetNodePosition(networkMaterialNode, (100, -100))

networkMaterialNode.addInputPort('arnoldSurface')
networkMaterialNode.getInputPort('arnoldSurface').connect(
    standardNode.getOutputPort('out'))

Note

The code sample above uses the ArnoldShadingNode type and only behaves correctly if you are using the Arnold renderer.

Node3D.checkDynamicParameters()

Checks and updates any dynamic parameters, i.e. parameters that rely on values from the incoming scene.