株式会社デジタル・フロンティア-Digital Frontier

Header

Main

  • TOP
  • DF TALK
  • Arnoldのちょいといい話!? part.2 -User dataについて-

Arnoldのちょいといい話!? part.2 -User dataについて-

2015/6/17

Tag: ,,,,,

ど~もお久しぶりです。
開発部ゴトー[@jackybian]です。
今年の5月は異常な暑さが続き夏みたいでしたね~。でもようやく東京も梅雨入りしましたね。
と書き始めて前々回の「Arnoldのちょっといい話!?」を見返したところ
ちょうどあの時も梅雨時期だったんですね~1年前か。。。早っ年々時間が経つのが早く感じますわ(゚д゚)!

この1年間もArnoldは着実にversionアップが繰り返され、細かな点も含めて色々進化してくれています。
HtoAも正式にリリースになりましたしね。という訳で今回もpart.2と題して、Arnold(MtoA)関連のお話をしようと思います。

実際にプロジェクトをやっていると通常shaderから取得できるデータ(normal,positionなど)以外にも色々なデータを取得してレンダリングしたい場合が出てきます。
例えば、deformerなどで計算されたsurfaceのtension情報などもその1つです。これをAOVとしてレンダリングするとか。
これ以外にも何か特殊なindexだったり、textureのパスだったりと何かしらの情報を埋め込んでレンダラーに渡したい場合があったりします。そこで今回は”MtoAにおけるUser-data”についてお話します。

Webで調べてみると、SolidAngle社など幾つかヒットするページはObject単位のdata付与の例が殆どでそれ以外はあまり載っていないようなのでまとめてお話できたらと思います。

MtoAでUser-dataを付与するにはMeshのShapeノードにUserAttributeを追加する必要があります。
こうすることでMtoAがこのAttributeを解釈してUser-dataがAss(Arnold Scene Source:scene description file)ファイルに埋め込まれるしくみです。
この埋め込まれたUser-dataは専用のshader(User-data Shader)で情報を取得することが可能になります。
では実際にどのような型や種類がサポートされ、どのようにAttributeを追加するのか?についてお話します。

Dataの型

こちらは標準でサポートされているUser-data Shaderをみると現在は以下の型が取得できます。

●string
●bool
●Int
●float
●vector
●point2(2次元ベクトル)
●color
※APIではこの他にもByte,Ptr,Matrixなどもサポートしているようです

Dataの種類

ここでいう種類というのは何に対するデータを付与できるのか?ということです。
APIリファレンスを見ると、
●constant — constant parameters are data that are defined on a per-object basis and do not vary across the surface of that object.
●uniform — uniform parameters are data that are defined on a “per-face” basis. During subdivision (if appropriate) values are not interpolated. Instead, the newly subdivided faces will contain the value of their “parent” face.
●varying — varying parameters are data that are defined on a per-vertex basis. During subdivision (if appropriate), the values at the new vertices are interpolated from the values at the old vertices. The user should only create parameters of “interpolatable” variable types (such as floats, colors, etc.)
●indexed — indexed parameters are data that are defined on a per-face-vertex basis. During subdivision (if appropriate), the values at the new vertices are interpolated from the values at the old vertices, preserving edges where values were not shared. The user should only create parameters of “interpolatable” variable types (such as floats, colors, etc.)
ということでObject単位、Face単位、Vertex単位、FaceVertex単位でデータが格納できるようです。
※現在のところMtoAではindexedはサポートされていないようです

User-dataの付与

User-dataの付与は、
Shapeノードに対して”mtoa_”+Dataの種類+”_”+”parameter名”という規則に従いAttributeを追加します。
例えばObjectに対してvector型でparameter名:myVectorとしてAttribute追加する場合は下図のようになります。
追加されたAttributeに値(User-data)をセットすることでdataが付与されます。

ShaderによるData取得

①MeshにSurfaceShaderをアサイン。aiUserDataVectorノード作成。Vector Attr Nameに追加Attributeの”parameter名”を指定
②aiUserDataVector.outValure -> surfaceShader.outColorへコネクト
レンダーしてみるとAttributeに設定された値(User-data)でオブジェクトがレンダリングがされているかと思います。

ここまでが ”User-dataの付与~ShaderでのData取得~レンダリング” までの基本的な流れになります。

では次にObject単位ではなく、Face単位、Vertex単位のUser-dataの付与の場合はどうなるか?を紹介したいと思います。
Dataの種類としては前述の通り”uniform”, “varying”を使ってAttribute追加を行うことになりますが、この場合Data対象がFaceやVertexになるので格納するDataも配列型になります。
この場合の”Attributeの追加~User-dataのセット”を見ていこうと思います。
※mayaの配列型としては、stringArray, Int32Array, doubleArray, vectorArray, pointArrayあたりを使用します。
例えばFace単位(uniform)のvectorデータを付与する場合は、以下のpythonスクリプトを実行してvectorArrayタイプでuniformアトリビュートを作成し、値をセットします。

import pymel.core as pm
pm.addAttr('pPlaneShape1',longName='mtoa_uniform_faceVector', dt='vectorArray')
pm.setAttr('pPlaneShape1.mtoa_uniform_faceVector', [[0.3,0.2,0.1],[0.5,0.4,0.3],[0.7,0.6,0.5],[0.9,0.8,0.7],[1,1,1]])

先程同様aiUserDataVectorノードのVector Attr Nameに”faceVector”を指定してレンダリングしてみると

Face毎のUser-dataが取得されてレンダリング出来ていると思います。実際にこのMeshをASSファイルとして出力してみると

polymesh
{
 name pPlaneShape1
・・・・・・・・・・・・
・・途中省略・・・・・・
・・・・・・・・・・・・
 declare mtoa_shading_groups constant ARRAY NODE
 mtoa_shading_groups "aiUtility2SG"
 declare faceVector uniform VECTOR
 faceVector 5 1 VECTOR
0.300000012 0.200000003 0.100000001 0.5 0.400000006 0.300000012 0.699999988 0.600000024 0.5 0.899999976 0.800000012 0.699999988 1 1 1
}

Mesh情報の中にVECTOR型のUser-dataが正しく付与されていることが確認出来ると思います。
前述の通りVector以外にも色々な型のUser-dataが付与できるので、Data型, Dataの種類などを指定してAttributeを追加する関数をつくりテストしてみました。※numpy使ってますのでご注意を!

import pymel.core as pm
import numpy.random as nr, random, string

def addMtoaAttr(target, attrName, attrType, dataType):
    shape = pm.listRelatives(target, children=True, shapes=True)[0]
    dAtype = {'obj':'constant', 'face':'uniform','vertex':'varying'}
    dDtype = {'str':'string', 'strArray':'stringArray',
            'int':'long','intArray':'Int32Array',
            'float':'double','floatArray':'doubleArray',
            'vector':'double3','vectorArray':'pointArray',
            'color':'float3',
            'float2':'double2'}
    dAsize = {'obj':1}
    dAsize.update(pm.polyEvaluate(target, v=True, f=True))
    comp_size = dAsize[attrType]
    attrType = dAtype[attrType]
    dataType = dDtype[dataType]
    attrName = 'mtoa_' + attrType + '_' + attrName

    if pm.attributeQuery(attrName, node=shape, exists=True) == False:
        if dataType in ['long','double']:
            pm.addAttr(shape,longName=attrName, at=dataType)
        elif dataType == 'double3':
            pm.addAttr(shape,longName=attrName, at=dataType)
            for elm in ['x','y','z']:
                pm.addAttr(shape,longName=elm,attributeType='double',parent=attrName)
        elif dataType == 'float3':
            pm.addAttr(shape,longName=attrName, uac=True, at=dataType)
            for elm in ['r','g','b']:
                pm.addAttr(shape,longName=elm,attributeType='float',parent=attrName)
        elif dataType == 'double2':
            pm.addAttr(shape,longName=attrName, at=dataType)
            for elm in ['X','Y']:
                pm.addAttr(shape,longName=elm,attributeType='double',parent=attrName)
        else:
            pm.addAttr(shape,longName=attrName, dt=dataType)

    return [shape, attrName, dataType, comp_size]

def createData(dataType, dataSize):
    if 'Array' in dataType: vData = []
    for i in xrange(dataSize):
        if dataType == 'string':
            vData = ''.join([random.choice(string.ascii_letters + string.digits) for i in range(8)])
        elif dataType == 'stringArray':            
            vData.append( ''.join([random.choice(string.ascii_letters + string.digits) for i in range(8)]) )
        elif dataType == 'long':
            vData = nr.randint(0,100)
        elif dataType == 'Int32Array':
            vData.append( nr.randint(0,100) )
        elif dataType == 'double':
            vData = nr.rand(1)[0]
        elif dataType == 'doubleArray':
            vData.append( nr.rand(1)[0] )
        elif dataType in ['double3','float3']:
            vData = nr.rand(3)
        elif dataType == 'double2':
            vData = nr.rand(2)
        elif dataType == 'pointArray':
            vData.append( nr.rand(3) )

    return vData 

## obj:string
tName,aName,dType,dSize = addMtoaAttr('pPlane1','objStr','obj','str')
dVal = createData(dType, dSize)
pm.setAttr(tName + '.' + aName, dVal)
print(pm.getAttr('pPlaneShape1.mtoa_constant_objStr'))

## face:stringArray
tName,aName,dType,dSize = addMtoaAttr('pPlane1','faceStr','face','strArray')
dVal = createData(dType, dSize)
pm.setAttr(tName + '.' + aName, dVal)
print(map(str,pm.getAttr('pPlaneShape1.mtoa_uniform_faceStr')))

## vertex:stringArray
tName,aName,dType,dSize = addMtoaAttr('pPlane1','vertexStr','vertex','strArray')
dVal = createData(dType, dSize)
pm.setAttr(tName + '.' + aName, dVal)
print(map(str,pm.getAttr('pPlaneShape1.mtoa_varying_vertexStr')))

## obj:int
tName,aName,dType,dSize = addMtoaAttr('pPlane1','objInt','obj','int')
dVal = createData(dType, dSize)
pm.setAttr(tName + '.' + aName, dVal)
print(pm.getAttr('pPlaneShape1.mtoa_constant_objInt'))

## face:intArray
tName,aName,dType,dSize = addMtoaAttr('pPlane1','faceInt','face','intArray')
dVal = createData(dType, dSize)
pm.setAttr(tName + '.' + aName, dVal)
print(map(str,pm.getAttr('pPlaneShape1.mtoa_uniform_faceInt')))

## vertex:intArray
tName,aName,dType,dSize = addMtoaAttr('pPlane1','vertexInt','vertex','intArray')
dVal = createData(dType, dSize)
pm.setAttr(tName + '.' + aName, dVal)
print(map(str,pm.getAttr('pPlaneShape1.mtoa_varying_vertexInt')))

## obj:float
tName,aName,dType,dSize = addMtoaAttr('pPlane1','objFloat','obj','float')
dVal = createData(dType, dSize)
pm.setAttr(tName + '.' + aName, dVal)
print(pm.getAttr('pPlaneShape1.mtoa_constant_objFloat'))

## face:floatArray
tName,aName,dType,dSize = addMtoaAttr('pPlane1','faceFloat','face','floatArray')
dVal = createData(dType, dSize)
pm.setAttr(tName + '.' + aName, dVal)
print(map(str,pm.getAttr('pPlaneShape1.mtoa_uniform_faceFloat')))

## vertex:floatArray
tName,aName,dType,dSize = addMtoaAttr('pPlane1','vertexFloat','vertex','floatArray')
dVal = createData(dType, dSize)
pm.setAttr(tName + '.' + aName, dVal)
print(map(str,pm.getAttr('pPlaneShape1.mtoa_varying_vertexFloat')))

## obj:vector
tName,aName,dType,dSize = addMtoaAttr('pPlane1','objVector','obj','vector')
dVal = createData(dType, dSize)
pm.setAttr(tName + '.' + aName, dVal)
print(pm.getAttr('pPlaneShape1.mtoa_constant_objVector'))

## face:vectorArray
tName,aName,dType,dSize = addMtoaAttr('pPlane1','faceVector','face','vectorArray')
dVal = createData(dType, dSize)
pm.setAttr(tName + '.' + aName, dVal)
print(map(str,pm.getAttr('pPlaneShape1.mtoa_uniform_faceVector')))

## vertex:vectorArray
tName,aName,dType,dSize = addMtoaAttr('pPlane1','vertexVector','vertex','vectorArray')
dVal = createData(dType, dSize)
pm.setAttr(tName + '.' + aName, dVal)
print(map(str,pm.getAttr('pPlaneShape1.mtoa_varying_vertexVector')))

## obj:color
tName,aName,dType,dSize = addMtoaAttr('pPlane1','objColor','obj','color')
dVal = createData(dType, dSize)
pm.setAttr(tName + '.' + aName, dVal)
print(pm.getAttr('pPlaneShape1.mtoa_constant_objColor'))

## obj:pnt2
tName,aName,dType,dSize = addMtoaAttr('pPlane1','objUv','obj','float2')
dVal = createData(dType, dSize)
print dVal
pm.setAttr(tName + '.' + aName, dVal)
print(pm.getAttr('pPlaneShape1.mtoa_constant_objUv'))

以下はVertex単位のVectorデータを付与してレンダリングした例

この結果、pointArray型はASSファイルではVECTOR型として付与されるようです。

こんな感じでいろんな型のUser-dataを埋め込み、Shaderでピックアップすることが出来るので考え方次第ではとても役立つ場面があるのではと思います。興味のある方はぜひ利用してみてください。
次回はこの派生型利用法が紹介できたらと思っています。
という訳でお時間がきたようなので今回はこのあたりで。では(´∀`*)ノ マタ~

※免責事項※
本記事内で公開している全ての手法・コードの有用性、安全性について、当方は一切の保証を与えるものではありません。
これらのコードを使用したことによって引き起こる直接的、間接的な損害に対し、当方は一切責任を負うものではありません。
自己責任でご使用ください

Pocket

コメント

コメントはありません

コメントフォーム

コメントは承認制ですので、即時に反映されません。ご了承ください。

*