プログラムの概要: 次のプラグイン コードは、OpenMayaMPx.MPxNode クラスを継承することによって新しい深度シェーダを作成します。このシェーダは、Maya ソフトウェア シェーダによってのみ使用できます。compute() メソッドは、レンダーされるすべてのサーフェス ポイントに対して実行され、サーフェス ポイントの Z 軸のカメラからの距離に基づいてピクセルの色を決定します。
レンダーされる各サーフェス ポイントの深度にアクセスするため、nodeInitializer() には次の数値アトリビュートが作成されています。
depthShader.surfacePointAttribute = numericAttributeFn.createPoint('pointCamera', 'p')
ここでは、このアトリビュートの名前(pointCamera)および短い名前(p)が非常に重要です。この名前により、Maya はシェーダ ノードを samplerInfo ノードの pointCamera 出力アトリビュートに自動的に接続できます。samplerInfo ノードは、レンダーされているポイントに関する重要な情報を提供します。アトリビュートの詳細なリストについては、『Maya ユーザ ガイド』 > 「Technical Documentation」 > 「Nodes」 > 「samplerInfo」を参照してください。また、他のレンダリング アトリビュートの詳細なリストについては、API マニュアルの「付録 C: レンダリング アトリビュート」を参照してください。
# depthShader.py import sys import math import maya.api.OpenMaya as OpenMaya def maya_useNewAPI(): """ The presence of this function tells Maya that the plugin produces, and expects to be passed, objects created using the Maya Python API 2.0. """ pass # Plug-in information: kPluginNodeName = 'pyMyDepthShader' kPluginNodeClassify = 'utility/general' kPluginNodeId = OpenMaya.MTypeId(0x870FE) # Default attribute values defaultNearDistance = 20.0 defaultFarDistance = 70.0 defaultNearColor = (1.0, 1.0, 1.0) # (r,g,b) white defaultFarColor = (0.0, 0.0, 0.0) # (r,g,b) black defaultGamma = 1.0 minGamma = 0.1 maxGamma = 5.0 ########################################################## # Plug-in ########################################################## class depthShader(OpenMaya.MPxNode): ''' Creates a depth shader which renders in Maya's software renderer. ''' # Define the static variables to which we will assign the node's attributes # in nodeInitializer() defined below. surfacePointAttribute = OpenMaya.MObject() nearDistanceAttribute = OpenMaya.MObject() farDistanceAttribute = OpenMaya.MObject() nearColorAttribute = OpenMaya.MObject() farColorAttribute = OpenMaya.MObject() gammaAttribute = OpenMaya.MObject() outColorAttribute = OpenMaya.MObject() def __init__(self): ''' Constructor. ''' OpenMaya.MPxNode.__init__(self) def compute(self, pPlug, pDataBlock): ''' Node computation method. - pDataBlock contains the data on which we will base our computations. - pPlug is a connection point related to one of our node attributes (either an input or an output). ''' if ( pPlug == depthShader.outColorAttribute ): # Get the data handles corresponding to your attributes among the values in the data block. surfacePointDataHandle = pDataBlock.inputValue( depthShader.surfacePointAttribute ) nearDistanceDataHandle = pDataBlock.inputValue( depthShader.nearDistanceAttribute ) farDistanceDataHandle = pDataBlock.inputValue( depthShader.farDistanceAttribute ) nearColorDataHandle = pDataBlock.inputValue( depthShader.nearColorAttribute ) farColorDataHandle = pDataBlock.inputValue( depthShader.farColorAttribute ) gammaDataHandle = pDataBlock.inputValue( depthShader.gammaAttribute ) # Obtain the (x,y,z) location of the currently rendered point in camera coordinates. surfacePoint = surfacePointDataHandle.asFloatVector() # Since the camera is looking along its negative Z axis (the Y axis is # the up vector), we must take the absolute value of the Z coordinate # to obtain the point's depth. depth = abs(surfacePoint.z) # Get the actual near and far threshold values. nearValue = nearDistanceDataHandle.asFloat() farValue = farDistanceDataHandle.asFloat() # Find the proportion of depth between the near and far values. if( (farValue - nearValue) == 0): # Avoid a division by zero if the near and far values somehow have the same value. depthProportion = 0 else: depthProportion = (depth - nearValue) / (farValue - nearValue) # Clamp the depthProportion value in the interval [0.0, 1.0] depthProportion = max(0, min( depthProportion, 1.0 ) ) # Modify the depth proportion using the gamma roll-off bias. gammaValue = gammaDataHandle.asFloat() depthProportion = math.pow(depthProportion, gammaValue) # Linearly interpolate the output color based on the depth proportion. outColor = OpenMaya.MFloatVector(0, 0, 0) nearColor = nearColorDataHandle.asFloatVector() farColor = farColorDataHandle.asFloatVector() outColor.x = nearColor.x + ( ( farColor.x - nearColor.x ) * depthProportion ) outColor.y = nearColor.y + ( ( farColor.y - nearColor.y ) * depthProportion ) outColor.z = nearColor.z + ( ( farColor.z - nearColor.z ) * depthProportion ) # Write to the output data. outColorDataHandle = pDataBlock.outputValue( depthShader.outColorAttribute ) outColorDataHandle.setMFloatVector( outColor ) outColorDataHandle.setClean() else: return OpenMaya.kUnknownParameter ########################################################## # Plug-in initialization. ########################################################## def nodeCreator(): ''' Creates an instance of our node plug-in and delivers it to Maya as a pointer. ''' return depthShader() def nodeInitializer(): ''' Defines the set of attributes for our node. The attributes declared in this function are assigned as static members to our depthShader class. Instances of depthShader will use these attributes to create plugs for use in the compute() method. ''' # Create a numeric attribute function set, since our attributes will all be defined by numeric types. numericAttributeFn = OpenMaya.MFnNumericAttribute() #================================== # INPUT NODE ATTRIBUTE(S) #================================== # - The (x,y,z) point on the surface defined according to the camera's frame of reference. # > (!) Important: the 'pointCamera' string relates to the samplerInfo maya node. # > This value is supplied by the render sampler at computation time. depthShader.surfacePointAttribute = numericAttributeFn.createPoint('pointCamera', 'p') numericAttributeFn.storable = False numericAttributeFn.hidden = True depthShader.addAttribute( depthShader.surfacePointAttribute ) # - The 'near' distance, i.e. the minimum distance threshold from the camera after which the # pixel's color is modified by the depth of the point. # > This value can be defined by the user, and is storable. global defaultNearDistance, defaultFarDistance depthShader.nearDistanceAttribute = numericAttributeFn.create( 'nearDistance', 'nd', OpenMaya.MFnNumericData.kFloat, defaultNearDistance ) numericAttributeFn.storable = True numericAttributeFn.setMin(0.0) numericAttributeFn.setMax( defaultFarDistance ) depthShader.addAttribute( depthShader.nearDistanceAttribute ) # - The 'far' distance, i.e. the minimum distance threshold from the camera before which the # pixel's color is modified by the depth of the point. # > This value can be defined by the user, and is storable. depthShader.farDistanceAttribute = numericAttributeFn.create( 'farDistance', 'fd', OpenMaya.MFnNumericData.kFloat, defaultFarDistance ) numericAttributeFn.storable = True numericAttributeFn.setMin( defaultFarDistance + 0.1 ) # Add an epsilon value of 0.1 to avoid near distance overlap. numericAttributeFn.setMax( 3 * defaultFarDistance ) depthShader.addAttribute( depthShader.farDistanceAttribute ) # - The 'near' color. # > This value can be defined by the user using a color picker, and is storable. global defaultNearColor depthShader.nearColorAttribute = numericAttributeFn.createColor( 'nearColor', 'nc' ) numericAttributeFn.storable = True numericAttributeFn.default = ( defaultNearColor[0], defaultNearColor[1], defaultNearColor[2] ) depthShader.addAttribute( depthShader.nearColorAttribute ) # - The 'far' color. # > This value can be defined by the user using a color picker, and is storable. global defaultFarColor depthShader.farColorAttribute = numericAttributeFn.createColor( 'farColor', 'fc' ) numericAttributeFn.storable = True numericAttributeFn.default = ( defaultFarColor[0], defaultFarColor[1], defaultFarColor[2] ) depthShader.addAttribute( depthShader.farColorAttribute ) # - The gamma value, or roll-off bias, which will affect how the color is interpolated between # the near and far colors. # > This value can be defined by the user using a slider, and is storable. global defaultGamma, minGamma, maxGamma depthShader.gammaAttribute = numericAttributeFn.create( 'gamma', 'g', OpenMaya.MFnNumericData.kFloat, defaultGamma ) numericAttributeFn.storable = True numericAttributeFn.setMin( minGamma ) numericAttributeFn.setMax( maxGamma ) depthShader.addAttribute( depthShader.gammaAttribute ) #================================== # OUTPUT NODE ATTRIBUTE(S) #================================== # - The pixel color output. # > This value is computed in our depthShader.compute() method, and should not be stored. depthShader.outColorAttribute = numericAttributeFn.createColor( 'outColor', 'oc') numericAttributeFn.storable = False numericAttributeFn.writable = False numericAttributeFn.readable = True numericAttributeFn.hidden = False depthShader.addAttribute( depthShader.outColorAttribute ) #================================== # NODE ATTRIBUTE DEPENDENCIES #================================== # - All the input attributes affect the computation of the pixel color output (outColor). depthShader.attributeAffects( depthShader.surfacePointAttribute, depthShader.outColorAttribute ) depthShader.attributeAffects( depthShader.nearDistanceAttribute, depthShader.outColorAttribute ) depthShader.attributeAffects( depthShader.farDistanceAttribute, depthShader.outColorAttribute ) depthShader.attributeAffects( depthShader.nearColorAttribute, depthShader.outColorAttribute ) depthShader.attributeAffects( depthShader.farColorAttribute, depthShader.outColorAttribute ) depthShader.attributeAffects( depthShader.gammaAttribute, depthShader.outColorAttribute ) def initializePlugin( mobject ): ''' Initializes the plug-in. ''' mplugin = OpenMaya.MFnPlugin( mobject ) try: mplugin.registerNode( kPluginNodeName, kPluginNodeId, nodeCreator, nodeInitializer, OpenMaya.MPxNode.kDependNode, kPluginNodeClassify ) except: sys.stderr.write( "Failed to register node: " + kPluginNodeName ) raise def uninitializePlugin( mobject ): ''' Unitializes the plug-in. ''' mplugin = OpenMaya.MFnPlugin( mobject ) try: mplugin.deregisterNode( kPluginNodeId ) except: sys.stderr.write( "Failed to deregister node: " + kPluginNodeName ) raise ########################################################## # Sample usage. ########################################################## ''' # Copy the following lines and run them in Maya's Python Script Editor: import maya.cmds as cmds import random # Load our plug-in. cmds.loadPlugin( 'pyDepthShader.py' ) # Clear the scene. cmds.file( new=True, force=True ) # Create an instance of our depth shader. depthShaderName = cmds.shadingNode( 'pyMyDepthShader', asUtility=True ) cmds.setAttr( depthShaderName + '.gamma', 1.4 ) cmds.setAttr( depthShaderName + '.farColor', 0, 0.43, 1, type='double3' ) # Create a surface shader to which we will attach our depth shader. surfaceShaderName = cmds.shadingNode( 'surfaceShader', asShader=True ) cmds.connectAttr( depthShaderName + '.outColor', surfaceShaderName + '.outColor', force=True ) # Create a shading group to tie everything together. shadingGroupName = cmds.sets( name='surfaceShader1SG', empty=True, noSurfaceShader=True, renderable=True ) cmds.connectAttr( surfaceShaderName + '.outColor', shadingGroupName + '.surfaceShader', force=True ) # Create a bunch cubes pseudo-randomly placed according to a seeded random number generator. numCubes = 50 positionRange = 14 randomSeed = 5 random.seed( randomSeed ) for i in range( 0, numCubes ): # Create a new cube. newCubeName = cmds.polyCube( w=2, h=2, d=2 ) # Move the cube somewhere near the grid. cmds.move( random.randint( -positionRange, positionRange ), # x random.randint( -positionRange, positionRange ), # y random.randint( -positionRange, positionRange ), # z newCubeName ) # Rotate the cube along all three axes. cmds.rotate( random.randint( 0, 360 ), # x axis random.randint( 0, 360 ), # y axis random.randint( 0, 360 ), # z axis newCubeName ) # Assign the cube we have created to our surface (depth) shader. cmds.sets( newCubeName, e=True, forceElement=shadingGroupName ) # Render the current view. cmds.render() '''
# depthShader.py import sys import math import maya.OpenMaya as OpenMaya import maya.OpenMayaMPx as OpenMayaMPx # Plug-in information: kPluginNodeName = 'myDepthShader' kPluginNodeClassify = 'utility/general' kPluginNodeId = OpenMaya.MTypeId(0x870FE) # Default attribute values defaultNearDistance = 20.0 defaultFarDistance = 70.0 defaultNearColor = (1.0, 1.0, 1.0) # (r,g,b) white defaultFarColor = (0.0, 0.0, 0.0) # (r,g,b) black defaultGamma = 1.0 minGamma = 0.1 maxGamma = 5.0 ########################################################## # Plug-in ########################################################## class depthShader(OpenMayaMPx.MPxNode): ''' Creates a depth shader which renders in Maya's software renderer. ''' # Define the static variables to which we will assign the node's attributes # in nodeInitializer() defined below. surfacePointAttribute = OpenMaya.MObject() nearDistanceAttribute = OpenMaya.MObject() farDistanceAttribute = OpenMaya.MObject() nearColorAttribute = OpenMaya.MObject() farColorAttribute = OpenMaya.MObject() gammaAttribute = OpenMaya.MObject() outColorAttribute = OpenMaya.MObject() def __init__(self): ''' Constructor. ''' OpenMayaMPx.MPxNode.__init__(self) def compute(self, pPlug, pDataBlock): ''' Node computation method. - pDataBlock contains the data on which we will base our computations. - pPlug is a connection point related to one of our node attributes (either an input or an output). ''' if ( pPlug == depthShader.outColorAttribute ): # Get the data handles corresponding to your attributes among the values in the data block. surfacePointDataHandle = pDataBlock.inputValue( depthShader.surfacePointAttribute ) nearDistanceDataHandle = pDataBlock.inputValue( depthShader.nearDistanceAttribute ) farDistanceDataHandle = pDataBlock.inputValue( depthShader.farDistanceAttribute ) nearColorDataHandle = pDataBlock.inputValue( depthShader.nearColorAttribute ) farColorDataHandle = pDataBlock.inputValue( depthShader.farColorAttribute ) gammaDataHandle = pDataBlock.inputValue( depthShader.gammaAttribute ) # Obtain the (x,y,z) location of the currently rendered point in camera coordinates. surfacePoint = surfacePointDataHandle.asFloatVector() # Since the camera is looking along its negative Z axis (the Y axis is # the up vector), we must take the absolute value of the Z coordinate # to obtain the point's depth. depth = abs(surfacePoint.z) # Get the actual near and far threshold values. nearValue = nearDistanceDataHandle.asFloat() farValue = farDistanceDataHandle.asFloat() # Find the proportion of depth between the near and far values. if( (farValue - nearValue) == 0): # Avoid a division by zero if the near and far values somehow have the same value. depthProportion = 0 else: depthProportion = (depth - nearValue) / (farValue - nearValue) # Clamp the depthProportion value in the interval [0.0, 1.0] depthProportion = max(0, min( depthProportion, 1.0 ) ) # Modify the depth proportion using the gamma roll-off bias. gammaValue = gammaDataHandle.asFloat() depthProportion = math.pow(depthProportion, gammaValue) # Linearly interpolate the output color based on the depth proportion. outColor = OpenMaya.MFloatVector(0, 0, 0) nearColor = nearColorDataHandle.asFloatVector() farColor = farColorDataHandle.asFloatVector() outColor.x = nearColor.x + ( ( farColor.x - nearColor.x ) * depthProportion ) outColor.y = nearColor.y + ( ( farColor.y - nearColor.y ) * depthProportion ) outColor.z = nearColor.z + ( ( farColor.z - nearColor.z ) * depthProportion ) # Write to the output data. outColorDataHandle = pDataBlock.outputValue( depthShader.outColorAttribute ) outColorDataHandle.setMFloatVector( outColor ) outColorDataHandle.setClean() else: return OpenMaya.kUnknownParameter ########################################################## # Plug-in initialization. ########################################################## def nodeCreator(): ''' Creates an instance of our node plug-in and delivers it to Maya as a pointer. ''' return OpenMayaMPx.asMPxPtr( depthShader() ) def nodeInitializer(): ''' Defines the set of attributes for our node. The attributes declared in this function are assigned as static members to our depthShader class. Instances of depthShader will use these attributes to create plugs for use in the compute() method. ''' # Create a numeric attribute function set, since our attributes will all be defined by numeric types. numericAttributeFn = OpenMaya.MFnNumericAttribute() #================================== # INPUT NODE ATTRIBUTE(S) #================================== # - The (x,y,z) point on the surface defined according to the camera's frame of reference. # > (!) Important: the 'pointCamera' string relates to the samplerInfo maya node. # > This value is supplied by the render sampler at computation time. depthShader.surfacePointAttribute = numericAttributeFn.createPoint('pointCamera', 'p') numericAttributeFn.setStorable(False) numericAttributeFn.setHidden(True) depthShader.addAttribute( depthShader.surfacePointAttribute ) # - The 'near' distance, i.e. the minimum distance threshold from the camera after which the # pixel's color is modified by the depth of the point. # > This value can be defined by the user, and is storable. global defaultNearDistance, defaultFarDistance depthShader.nearDistanceAttribute = numericAttributeFn.create( 'nearDistance', 'nd', OpenMaya.MFnNumericData.kFloat, defaultNearDistance ) numericAttributeFn.setStorable( True ) numericAttributeFn.setMin( 0.0 ) numericAttributeFn.setMax( defaultFarDistance ) depthShader.addAttribute( depthShader.nearDistanceAttribute ) # - The 'far' distance, i.e. the minimum distance threshold from the camera before which the # pixel's color is modified by the depth of the point. # > This value can be defined by the user, and is storable. depthShader.farDistanceAttribute = numericAttributeFn.create( 'farDistance', 'fd', OpenMaya.MFnNumericData.kFloat, defaultFarDistance ) numericAttributeFn.setStorable( True ) numericAttributeFn.setMin( defaultFarDistance + 0.1 ) # Add an epsilon value of 0.1 to avoid near distance overlap. numericAttributeFn.setMax( 3 * defaultFarDistance ) depthShader.addAttribute( depthShader.farDistanceAttribute ) # - The 'near' color. # > This value can be defined by the user using a color picker, and is storable. global defaultNearColor depthShader.nearColorAttribute = numericAttributeFn.createColor( 'nearColor', 'nc' ) numericAttributeFn.setStorable( True ) numericAttributeFn.setDefault( defaultNearColor[0], defaultNearColor[1], defaultNearColor[2] ) depthShader.addAttribute( depthShader.nearColorAttribute ) # - The 'far' color. # > This value can be defined by the user using a color picker, and is storable. global defaultFarColor depthShader.farColorAttribute = numericAttributeFn.createColor( 'farColor', 'fc' ) numericAttributeFn.setStorable( True ) numericAttributeFn.setDefault( defaultFarColor[0], defaultFarColor[1], defaultFarColor[2] ) depthShader.addAttribute( depthShader.farColorAttribute ) # - The gamma value, or roll-off bias, which will affect how the color is interpolated between # the near and far colors. # > This value can be defined by the user using a slider, and is storable. global defaultGamma, minGamma, maxGamma depthShader.gammaAttribute = numericAttributeFn.create( 'gamma', 'g', OpenMaya.MFnNumericData.kFloat, defaultGamma ) numericAttributeFn.setStorable( True ) numericAttributeFn.setMin( minGamma ) numericAttributeFn.setMax( maxGamma ) depthShader.addAttribute( depthShader.gammaAttribute ) #================================== # OUTPUT NODE ATTRIBUTE(S) #================================== # - The pixel color output. # > This value is computed in our depthShader.compute() method, and should not be stored. depthShader.outColorAttribute = numericAttributeFn.createColor( 'outColor', 'oc') numericAttributeFn.setStorable( False ) numericAttributeFn.setWritable( False ) numericAttributeFn.setReadable( True ) numericAttributeFn.setHidden( False ) depthShader.addAttribute( depthShader.outColorAttribute ) #================================== # NODE ATTRIBUTE DEPENDENCIES #================================== # - All the input attributes affect the computation of the pixel color output (outColor). depthShader.attributeAffects( depthShader.surfacePointAttribute, depthShader.outColorAttribute ) depthShader.attributeAffects( depthShader.nearDistanceAttribute, depthShader.outColorAttribute ) depthShader.attributeAffects( depthShader.farDistanceAttribute, depthShader.outColorAttribute ) depthShader.attributeAffects( depthShader.nearColorAttribute, depthShader.outColorAttribute ) depthShader.attributeAffects( depthShader.farColorAttribute, depthShader.outColorAttribute ) depthShader.attributeAffects( depthShader.gammaAttribute, depthShader.outColorAttribute ) def initializePlugin( mobject ): ''' Initializes the plug-in. ''' mplugin = OpenMayaMPx.MFnPlugin( mobject ) try: mplugin.registerNode( kPluginNodeName, kPluginNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kDependNode, kPluginNodeClassify ) except: sys.stderr.write( "Failed to register node: " + kPluginNodeName ) raise def uninitializePlugin( mobject ): ''' Unitializes the plug-in. ''' mplugin = OpenMayaMPx.MFnPlugin( mobject ) try: mplugin.deregisterNode( kPluginNodeId ) except: sys.stderr.write( "Failed to deregister node: " + kPluginNodeName ) raise ########################################################## # Sample usage. ########################################################## ''' # Copy the following lines and run them in Maya's Python Script Editor: import maya.cmds as cmds import random # Load our plug-in. cmds.loadPlugin( 'depthShader.py' ) # Clear the scene. cmds.file( new=True, force=True ) # Create an instance of our depth shader. depthShaderName = cmds.shadingNode( 'myDepthShader', asUtility=True ) cmds.setAttr( depthShaderName + '.gamma', 1.4 ) cmds.setAttr( depthShaderName + '.farColor', 0, 0.43, 1, type='double3' ) # Create a surface shader to which we will attach our depth shader. surfaceShaderName = cmds.shadingNode( 'surfaceShader', asShader=True ) cmds.connectAttr( depthShaderName + '.outColor', surfaceShaderName + '.outColor', force=True ) # Create a shading group to tie everything together. shadingGroupName = cmds.sets( name='surfaceShader1SG', empty=True, noSurfaceShader=True, renderable=True ) cmds.connectAttr( surfaceShaderName + '.outColor', shadingGroupName + '.surfaceShader', force=True ) # Create a bunch cubes pseudo-randomly placed according to a seeded random number generator. numCubes = 50 positionRange = 14 randomSeed = 5 random.seed( randomSeed ) for i in range( 0, numCubes ): # Create a new cube. newCubeName = cmds.polyCube( w=2, h=2, d=2 ) # Move the cube somewhere near the grid. cmds.move( random.randint( -positionRange, positionRange ), # x random.randint( -positionRange, positionRange ), # y random.randint( -positionRange, positionRange ), # z newCubeName ) # Rotate the cube along all three axes. cmds.rotate( random.randint( 0, 360 ), # x axis random.randint( 0, 360 ), # y axis random.randint( 0, 360 ), # z axis newCubeName ) # Assign the cube we have created to our surface (depth) shader. cmds.sets( newCubeName, e=True, forceElement=shadingGroupName ) # Render the current view. cmds.render() '''