本节讲如何获取和创建通道和层(也叫通道,集合)。
读取通道
获取当前Nuke工程中所有的通道:
nuke.channels()
# Result:
['rgba.red', 'rgba.green', 'rgba.blue', 'rgba.alpha', 'depth.Z', 'forward.u', 'forward.v', 'backward.u', 'backward.v', 'disparityL.x', 'disparityL.y', 'disparityR.x', 'disparityR.y', 'mask.a', 'rotopaint_mask.a']
获取层名字:
nuke.layers()
# Result:
['rgb', 'rgba', 'alpha', 'depth', 'motion', 'forward', 'backward', 'disparity', 'disparityL', 'disparityR', 'mask', 'rotopaint_mask']
获取选取节点的通道:
node = nuke.selectedNode()
print node.channels()
# Result:
['rgba.red', 'rgba.green', 'rgba.blue', 'rgba.alpha']
添加新通道
添加一个新通道:
nuke.Layer('customLayer', ['red', 'green', 'blue'])
注意: 层不存在时,此函数会创建一个出来
警告: 不要给默认层添加通道,因为Nuke工程不保存。
重命名现有层:
nuke.Layer( 'customLayer' ).setName( 'myLayer' )
警告: 不要重命名默认层,当重新加载工程时会报错。
例子
autoComp
这个例子检查exr文件中的通道,常识讲第二pass合到beauty渲染中。
可以参考脚本the sample exr
首先,写个函数autoComp,接收一个节点作为参数。此节点包含第二通道或者“AOVs"(任意输出值)。第一步先获取节点的通道:
def autoComp( node ):
channels = node.channels()
node.channels()返回此节点的所有通道,返回值是通道名列表[‘rgba.red’, ‘rgba.green’, ‘rgba.blue’],为了获取层名,
使用点号来切分通道名,并获取结果的第一部分:
layers = [c.split('.')[0] for c in channels]
但是,这留给我们很多层复制名,想去掉这些复制层,先把这些结果放入set,然后在倒回list:
layers = list( set([c.split('.')[0] for c in channels]) )
方便起见先排序,代码如下:
def autoComp( node ):
channels = node.channels()
layers = list( set([c.split('.')[0] for c in channels]) )
layers.sort()
准备好了所有层和通道,我们就可以建一个简单UI,在合成中将现有的buffer 对应到正确的pass。创建标题为“Map AVOS”的panel,
并添加三个下拉菜单:
- texture -- 附加的层包含纹理颜色
- diffuse -- 附加上的层包含 diffuse 灯光信息
- specular -- 附加的层包含 specular信息
将我们获取的层名填入下拉菜单。 此例中,值为TCL样式的列表,空格区分(和‘ ’.join()很像):
p = nuke.Panel( 'Map AOVs' )
p.addEnumerationPulldown( 'texture', ' '.join( layers ) )
p.addEnumerationPulldown( 'diffuse', ' '.join( layers ) )
p.addEnumerationPulldown( 'specular', ' '.join( layers ) )
需要用户指定哪个通道包含深度信息,因此添加一个depth菜单,并给菜单添加通道名:
p.addEnumerationPulldown( 'depth', ' '.join( channels ) )
最后,添加两个复选框来设置是否归一化深度缓冲区(默认勾选)或者反转(默认关闭):
p.addBooleanCheckBox( 'normalise depth', True)
p.addBooleanCheckBox( 'invert depth', False )
简单的小界面就做完了。
注意: 想加更多控制和复杂功能,请看python panels
使用show()来显示界面。 这样打开界面是模式对话框(非模式对话框在此章节木有)。使用OK关闭窗口返回True,使用Cancel关闭返回False:
if not p.show():
return
如果取消了,代码就此停止运行。 否则,使用value()将选中的panel存入变量中,名字是panel上的。
texture = p.value( 'texture' )
diffuse = p.value( 'diffuse' )
spec = p.value( 'specular' )
depth = p.value( 'depth' )
normZ = p.value( 'normalise depth' )
invertZ = p.value( 'invert depth' )
现在利用panel中的信息建立节点树吧。
首先,为了以后处理方便,将texture,diffuse,specular缓冲区 导入rgb中。
注意: 从合成角度来说没必要这么干,因为可以将所有层、缓冲区内联合并起来,但保险起见我们用了shuffle节点
使用nuke.nodes 来创建Shuffle节点,节点标签为texture,将其连接到当前节点上:
shuffleNode = nuke.nodes.Shuffle( label='texture', inputs=[node] )
现在,设置inknob读取panel中指定的层来包含纹理信息:
shuffleNode['in'].setValue(layer)
接下来,打开Shuffle节点的邮戳,附加一个Dot节点保持紧凑的布局:
shuffleNode['postage_stamp'].setValue( True )
nuke.nodes.Dot( inputs=[ shuffleNode ] )
同样的事diffuse,specular层也要来一遍,所以把上面的代码封装成函数,方便重用:
def shuffleLayer( node, layer ):
shuffleNode = nuke.nodes.Shuffle( label=layer, inputs=[node] )
shuffleNode['in'].setValue( layer )
shuffleNode['postage_stamp'].setValue( True )
return nuke.nodes.Dot( inputs=[ shuffleNode ] )
返回autoComp函数(在panel保存变量之后)调用新函数给每个层创建Shuffle节点:
texNode = shuffleLayer( node, texture )
diffNode = shuffleLayer( node, diffuse )
specNode = shuffleLayer( node, spec )
至此所有代码如下:
def shuffleLayer( node, layer ):
'''
Shuffle a given layer into rgba
args:
node - node to attach a Shuffle node to
layer - layer to shuffle into rgba
'''
shuffleNode = nuke.nodes.Shuffle( label=layer, inputs=[node] )
shuffleNode['in'].setValue( layer )
shuffleNode['postage_stamp'].setValue( True )
return nuke.nodes.Dot( inputs=[ shuffleNode ] )
def autoComp( node ):
channels = node.channels()
layers = list( set([c.split('.')[0] for c in channels]) )
layers.sort()
# CREATE SIMPLE PANEL TO MAP THE BUFFERS
p = nuke.Panel( 'Map AOVs' )
p.addEnumerationPulldown( 'texture', ' '.join( layers ) )
p.addEnumerationPulldown( 'diffuse', ' '.join( layers ) )
p.addEnumerationPulldown( 'specular', ' '.join( layers ) )
p.addEnumerationPulldown( 'depth', ' '.join( channels ) )
p.addBooleanCheckBox( 'normalise depth', True)
p.addBooleanCheckBox( 'invert depth', False )
if not p.show():
return
# STORE PANEL RESULt IN VARIABLES FOR EASE OF USE
texture = p.value( 'texture' )
diffuse = p.value( 'diffuse' )
spec = p.value( 'specular' )
depth = p.value( 'depth' )
normZ = p.value( 'normalise depth' )
invertZ = p.value( 'invert depth' )
# CREATE SHUFFLE NODES
texNode = shuffleLayer( node, texture )
diffNode = shuffleLayer( node, diffuse )
specNode = shuffleLayer( node, spec )
现在运行脚本会产生三个Shuffle节点,将panle中指定的层倒入到rgba中:
给diffuse缓冲区添加一个Multiply节点,用户可以修改缓冲区,并和shuffle节点的texture合并:
mergeDiff = nuke.nodes.Merge2( operation='multiply', inputs=[ texNode, nuke.nodes.Multiply( inputs=[diffNode] ) ], output='rgb' )
上面那行代码做了如下事情:
- 创建Merge2节点
- 将操作knob设置成multiply
- 输入连接到texture节点,另一个输入(a)连接到新创建的Multiply节点
- Multiply节点连接到diffuse节点
- Merge节点的输出设置成**rgb,因此原生a通道完整保存下来
创建另一对Multiply和Merge节点,这次是给specular节点,将这个Merge节点连接到上面创建的Merge节点:
result = nuke.nodes.Merge2( operation='plus', inputs=[ mergeDiff, nuke.nodes.Multiply( inputs=[specNode] ) ], output='rgb' )
查看用户是否要归一化深度,如果是,运行getMinMax函数,在Grade节点中使用其结果:
if normZ:
black, white = examples.getMinMax( node, depth )
result = nuke.nodes.Grade( channels=depth, blackpoint=black, whitepoint=white, white_clamp=True, label='normalise depth', inputs=[result] )
检查是否反转深度通道,如果是,使用Invert节点,通道设置成depth:
if invertZ:
result = nuke.nodes.Invert( channels=depth, inputs=[result] )
最终,添加Grade节点稍微提升下深度通道中的黑色:
g = nuke.nodes.Grade( inputs=[result] )
g['black'].setValue( 0.05 )
g['mask'].setValue( depth )
节点图如下:
所有代码如下:
import examples
import nuke
def shuffleLayer( node, layer ):
'''
Shuffle a given layer into rgba
args:
node - node to attach a Shuffle node to
layer - layer to shuffle into rgba
'''
shuffleNode = nuke.nodes.Shuffle( label=layer, inputs=[node] )
shuffleNode['in'].setValue( layer )
shuffleNode['postage_stamp'].setValue( True )
return nuke.nodes.Dot( inputs=[ shuffleNode ] )
def autoComp( node ):
channels = node.channels()
layers = list( set([c.split('.')[0] for c in channels]) )
layers.sort()
# CREATE SIMPLE PANEL TO MAP THE BUFFERS
p = nuke.Panel( 'Map AOVs' )
p.addEnumerationPulldown( 'texture', ' '.join( layers ) )
p.addEnumerationPulldown( 'diffuse', ' '.join( layers ) )
p.addEnumerationPulldown( 'specular', ' '.join( layers ) )
p.addEnumerationPulldown( 'depth', ' '.join( channels ) )
p.addBooleanCheckBox( 'normalise depth', True)
p.addBooleanCheckBox( 'invert depth', False )
if not p.show():
return
# STORE PANEL RESULt IN VARIABLES FOR EASE OF USE
texture = p.value( 'texture' )
diffuse = p.value( 'diffuse' )
spec = p.value( 'specular' )
depth = p.value( 'depth' )
normZ = p.value( 'normalise depth' )
invertZ = p.value( 'invert depth' )
# CREATE SHUFFLE NODES
texNode = shuffleLayer( node, texture )
diffNode = shuffleLayer( node, diffuse )
specNode = shuffleLayer( node, spec )
mergeDiff = nuke.nodes.Merge2( operation='multiply', inputs=[ texNode, nuke.nodes.Multiply( inputs=[diffNode] ) ], output='rgb' )
result = nuke.nodes.Merge2( operation='plus', inputs=[ mergeDiff, nuke.nodes.Multiply( inputs=[specNode] ) ], output='rgb' )
if normZ:
black, white = examples.getMinMax( node, depth )
result = nuke.nodes.Grade( channels=depth, blackpoint=black, whitepoint=white, white_clamp=True, label='normalise depth', inputs=[result] )
if invertZ:
result = nuke.nodes.Invert( channels=depth, inputs=[result] )
g = nuke.nodes.Grade( inputs=[result] )
g['black'].setValue( 0.05 )
g['mask'].setValue( depth )