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.