【NUKE教程】Nuke Python 控制通道和层

21 九月, 2016
75
0

nukepylayer
本节讲如何获取和创建通道和层(也叫通道,集合)。

读取通道

获取当前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 Python 控制通道和层

警告: 不要给默认层添加通道,因为Nuke工程不保存。

重命名现有层:

	
nuke.Layer( 'customLayer' ).setName( 'myLayer' )

警告: 不要重命名默认层,当重新加载工程时会报错。

例子

autoComp

这个例子检查exr文件中的通道,常识讲第二pass合到beauty渲染中。
Nuke Python 控制通道和层
可以参考脚本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

Nuke Python 控制通道和层

如果取消了,代码就此停止运行。 否则,使用value()将选中的panel存入变量中,名字是panel上的。

Nuke Python 控制通道和层

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中:
Nuke Python 控制通道和层

给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' )

Nuke Python 控制通道和层

查看用户是否要归一化深度,如果是,运行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 )

节点图如下:
Nuke Python 控制通道和层

所有代码如下:

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 )