カテゴリー: CG

CG, KATANA

XMLファイルを使ってショットシーンを構築する

Importomatic ノードで XML ファイル経由でアセットを読み込むことができたので、もう少し複雑なシーンも構築してみます。

使用するネタは CGWorld.jp の記事で使用したシーンです。この時は USD 形式でき出力しましたが、やることはまあ同じなので何となくパラメータを読み替えつつ XML ファイルで出力するようにします。


xml_tmpl = r'''
<scenegraphXml name="Zombie_OnFire" version="0.1.0">
  <!-- channelData startFrame="1" endFrame="10" 
      ref="/tmp/myChannelData" / -->
  <instanceList>
    %s
  </instanceList>
</scenegraphXml>
'''

instance_tmpl = r'''
    <instance type="reference" name="%(name)s" refType="abc" refFile="%(path)s">
      <xform value="%(xform)s"/>
    </instance>
'''

import maya.cmds as cmds

nodes = []
for r in cmds.ls(type='reference'):
    if r == 'sharedReferenceNode':
        continue

    fn = cmds.referenceQuery(r, filename=True)
    ns = cmds.referenceQuery(r, namespace=True)[1:]
    path = cmds.referenceQuery(r, filename=True, withoutCopyNumber=True)
    root = cmds.ls('%s:*_root' % ns)
    xform = cmds.getAttr('%s.worldMatrix' % root[0])
    nodes.append(instance_tmpl % {'name' : ns,
                                  'path' : path,
                                  'xform' : str(xform)[1:-1]})

fp = open(r'/home/chiyama/Documents/katanta/scene_build/layout.xml', 'w')
fp.write(xml_tmpl % '\n'.join(nodes))
fp.close()

このようにして出力したファイルを KATANA で読むと、Maya 上でレイアウトしたシーンを再現することができました。

※そのままでは見た目がよくわからなかったので、全てのオブジェクトにグレーなマテリアルを割り当てています

CG, KATANA

Importomatic ノードでアセットを読み込む(2)

昨日はアセット用 .abc ファイルと、それに使用する .klf ファイルを一つづつ選んでシーンを構築してみました。

が。

そんなことを実際のシーンでやっていたらめんどくさくて禿げちゃいます。アセット100個、名前を指定しながら読み込むとか無理デスヨネー。
しかも、Maya など前段階の作業でアセットの読み込みはしているため、どのアセットが必要なのか既にわかっているはずです。その情報をそのまま使いたいというのが人情です。

そんな時には XML ファイルを使用して Importomatic ノードでアセットを読み込むことができます。

まず、シーン構築用の XML ファイルを用意します。


<scenegraphXml name="Zombie_OnFire" version="0.1.0">
  <instanceList>
    <instance type="reference" name="zombie" refType="abc"
              refFile="/home/chiyama/Documents/katanta/scene_build/Zombie_OnFire_2B.abc">
      <xform value="1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1"/>
      <lookFile ref="/home/chiyama/Documents/katanta/scene_build/zombie_on_fire.klf" />
    </instance>
  </instanceList>
</scenegraphXml>

そして、これを Importomatic ノードで読み込みます。

無事にアセットは読み込まれたのですが、マテリアルがついてきていません。これは Scene Graph の階層構造が変わってしまってるためでしょう。この辺りは何度か手戻りを繰り返しながら良い感じになるように Lookdev 用ファイルを作り直したり XML ファイルをみなおして調整する必要があります。

まだちょっと調整が必要ではあるものの、これでレイアウトやアニメーションの時に作成した情報を元にアセットを集めてシーンを構築するためのメドがついたことになります。

CG, KATANA

Importomatic ノードでアセットを読み込む

ショットシーン構築のためにアセットを読み込む方法を試行錯誤していたところ、KATANA 警察の方から Importomatic というのがあるよッ!!というのを教えていただきました。どうやらこのノードをパイプラインに合わせてゴリゴリいじれば良さそうです。

ということで複数の .abc ファイルを読んでみました。

なるほどこれは簡単。そして、Look File のアサインもここでおこなうことができるようです。

アセットの読み込みと Look File のアサインの方法がわかったのでやってみます。

。。。。と、その前に。Importomatic を使用してシーンを構築する場合、Look File 作成時にも Importomatic を使用してアセットを読み込んでおいた方が良いようです。もしかしたら、ちゃんとやれば Alembic_In で読んでいてもいいのかもしれないですが私のケースでは Importomatic で読んだアセットに対して Look File を作らないとマテリアルが再現されませんでした。Attribute も確認したのですが、違いを見つけることができなかったので何故こうしなければいけないのかは謎です。

このようにして作成した .klf ファイルを使ってマテリアルをアサインし、レンダリングしてみました。無事に再現されています。

CG

USDって結局何なのよ?

本記事の内容に対して、事実と異なることが含まれている可能性があることに気づきました。今後、検証・訂正記事を書くのでこちらの内容は一旦白紙とさせてください。
(追記)訂正記事を書きました。やはり私の勘違いで、Maya でもかなりの恩恵を享受できそうです

SIGGRAPH ASIA 2018 が始まりましたね。皆さん有楽町の超長いレジストの行列並びましたか~?

今回も PIXAR は “USD and Scene Interoperability: Demystifying the State of the Art”と題して USD の紹介をするためのセッションを設けていました。

この USD なんですが、CGWorld.jp の連載で USD について記事を書いた関係でいろんな人から、「それで、結局 USD って何なのよ?」と聞かれることがちょいちょいあります。そこで、私なりの解釈で USD というものを説明したいと思います。

と言っても USD の内容については元PIXARの手島さんが日本語で詳細な解説をされていたり、本家でも詳細なドキュメントが公開されています

これと同じ話をしてもしょうがないですし、私もそこまで詳しくないので、本記事では開発元である PIXAR とは別の、ユーザー側の視点から補足する形で USD って一体何なのよ?という疑問にお答えします。

USD って一体何なのよ?

USD にはいくつかの側面があります。

  • シーンを記述するためのファイルフォーマット
  • シーングラフと API
  • パイプライン構築のための基盤技術

このどれもが USD であり、それぞれが密接に関わりあっています。そのため、一緒に解説がおこなわれてしまって混乱を招いている部分が大きいと感じます。そこで、ファイルフォーマットと基盤技術という二つの面を別々に説明します。
※シーングラフと API に関しては今回あまり重要じゃないので割愛します。

シーンを記述するためのファイルフォーマット

いわゆる .usd ファイル(に書かれれている内容)です。これは、ざっくりと Alembic や FBX の凄い版と考えて良いでしょう。DCCTool は .usd ファイルフォーマットの読み書きに対応することで、他のアプリケーションとやり取りをすることができます。

ただし。

ここが大事なことなのですが、アプリケーションが USD の全ての仕様を満たし、潜在能力を発揮できるとは限りません。USD ファイルを読みこんだ後はアプリケーションネイティブなシーンデータになるだけなので、USD の仕様にあるけれどもアプリケーションが対応していない機能は当然実現できません。

たとえば、Maya に RenderMan のシェーダ情報を埋め込んだ .usd ファイルを読み込んで Arnold でレンダリングをしても、RenderMan のシェーダを使ってレンダリングできるわけではないです。当然ですよね。

アプリケーションは、パイプラインの中で自分が必要とする USD の機能をサポートし、共通ファイルフォーマットとして入出力をおこないます。

んじゃ FBX でいいじゃん?って思うかもしれませんが、ここでレンダラまで一気通貫で対応している USD の強みが効いてきます。USD ならパイプラインのごく初めの段階で作ったデータを変換することなく、そのままレンダリングまで持っていくことができます。

また、共通のファイルフォーマットなので複数のアプリケーションを使用してデータを作成する場合にも複雑なデータ変換の手続きを踏まなくてもそのままガッチャンコとできます。

ここまでが共通ファイルフォーマットとしての USD のお話です。これは USD を導入するとすぐに享受できるメリットです。これだけでも十分すごいですよね。

パイプライン構築のための基盤技術

これが実はかなり厄介というか、初見殺しの内容になっています。

USD という基盤技術を使うことでコンカレントパイプラインを構築することができますよということでプレゼンでもかなり力を入れて説明がされるところなのですが、残念なことに

通常、我々はそのまま享受することはできません。

もう一回言います。

我々には使えません。

え?え?どゆこと!? PIXAR 嘘ついてるの!?と思うかもしれませんが、そんなことはありません。事実、PIXAR では USD を使ってそういうパイプラインを作っていますから。

そう、Presto があればねッ!!!!

先ほどのファイルフォーマットの時の説明を思い出してください。

“USD の仕様にあるけれどもアプリケーションが対応していない機能は当然実現できません”

※(訂正)USD プラグインとして提供される機能で対応している内容もあり、Maya の場合は USD 標準の Maya プラグインでかなりのことができるようです。USD が実現しようとしていることを完全に実現できているかはちょっとわからないですが、かなり高いレベルで実装されているようです。

つまり、コンカレントパイプラインを構築するためにはそれに対応した環境を自分で作る必要があります。これは当然ですね。パイプラインは各社各様それぞれなのでそこを USD がどうこうすることはできません。

PIXAR は自社で Presto を開発して対応していますし、AnimalLogic は AL_USDMaya を開発して Maya 上で対応しています。コンカレントパイプラインという思想を実現するための強力な基盤技術として USD を使用するのです。

また、当然ながらこの部分を USD に依存する必要性も無いのです。KATANA なんかは入出力として USD に対応しますが、シーン管理は自前でおこなっています。

よくわからないけど凄そうな usdview

ここで混乱に拍車をかけているのが usdview の存在です。USD のデモでは必ず登場し、ある意味 USD の顔のような存在になっています。ただ、これは名前の通り USD シーンのビューワでしかなく、シーンの編集機能は一切無いです。

何も知らないと “PIXAR で使っている CG ソフトがオープンソースに!?私も同じツールで CG を作れるの!?”とワクワクしてしまうのですが、そういうわけではないのです。

usdview 自体はとても強力なツールで、.usd ファイル(だけではなく、.abc ファイルも)を扱う時には手放すことのできない相棒のような存在です。

まとめ

私の視点からみた USD の概要についてまとめてみました。特に、PIXAR の中の人にとっては当たり前すぎて省略してしまいがちなことを重点的に書き出してみたので、この内容を念頭に置いた状態で PIXAR の資料を読むと、今までモヤッとしていた部分が晴れてくるんじゃないかなとおもいます。

CG, KATANA

OpScript を使ってアセットを読み込む

昨日の記事ではゴリゴリと Node Graph を編集する形でアセットを読み込む方向で進めてみたんですが、私のゴーストがこっちじゃないと囁くんでもうちょっとスマートな方法を調べてみることにします。

多分、KATANA 的にはショットシーンの雛形もあんまりゴリゴリ自動生成しないで、メタデータなりなんなりを読み込んで最初のシーンを構築するんじゃないかと思います。多分。

これには、OpScript で複数の .abc ファイルを読み込みつつ Scene Graph を構築する方法が確立できればいいんじゃないかということでチョチョッと調べます。

とりあえず .abc ファイルを一つだけ読み込むには

local argsGb = GroupBuilder()
abcFile = "/home/chiyama/Documents/usdtest/assets/Zombie_OnFire_2B.abc"
argsGb:set('fileName', StringAttribute(abcFile))
Interface.ExecOp('AlembicIn', argsGb:build())

こんなことをすればいいようです。

ExecOp で AlembicIn ノードを作ってデータを読み込んでいるっぽいことはわかったので、引数で良い感じにファイルを読み込む Scene Graph のパスを指定すれば複数のアセットを読み込むことができるんじゃないかとおもいます。

ちょっと謎なのが、KATANA でのノードの Alembic_In とは必要な引数名が異なるので、どのように読み替えればいいのかがわからないところです。

この部分が解決すればやりたいことはできるんでしょうが、生憎今日はここまでで閉店ガラガラ~。

CG, KATANA

アセット読み込みテンプレートの作成

※エイヤッと記事にはしたものの、どうもこの方法でショットシーンを作ることは無さそうです。まあテクニックの一つとしてこんなこともできるんだ的な感じということで;;;

ショットシーンを構築する際、一つ一つ手でアセット読み込み用のノードグラフを作ってファイルを読んで。。。とやっていたら大変すぎます。そこで、アセット読み込み用のテンプレートを用意し、スクリプトを使ってアセットの読み込みをできるようにします。

KATANA の場合、Alembic ファイルを読み込んだり LookFile をアサインするといった定例処理をノードとして組むので、CG ソフトのように Export Selection と Import や Merge をやる方法では対応できなさそうです。このあたりは 3DCG ツールというよりも Nuke のようなコンポジットソフトに感覚が近いです。

さてどうするかな。。。。と調べていたら、ノードの情報を XML 形式で入出力できるようです。これがきちんと動くなら、使えそうです。

まずは元になるノードグラフを作成します。今回はターンテーブル作成に使用したものを使います。

この、Alembic_In, LookFileAssign, LookFileResolve を使用します。

Python タブで以下のコードを実行すると、/home/chiyama/Documents/katanta/scene_build/template.xml に上記三つのノード情報が格納されたファイルが作成されます。

from Katana import NodegraphAPI
nodes = [NodegraphAPI.GetNode('Alembic_In'),
         NodegraphAPI.GetNode('LookFileAssign'),
         NodegraphAPI.GetNode('LookFileResolve')]

xmlNode = NodegraphAPI.BuildNodesXmlIO(nodes)

fp = open('/home/chiyama/Documents/katanta/scene_build/template.xml', 'w')
fp.write(xmlNode.writeString())
fp.close()

試しにこのファイルをそのまま使ってシーンにノードを作ってみます。空のシーンで

fp = open('/home/chiyama/Documents/katanta/scene_build/template.xml', 'r')
xmlData = fp.read()
fp.close()

xmlNode, result = NodegraphAPI.LoadElementsFromString(xmlData)
newNode = KatanaFile.Paste(xmlNode, NodegraphAPI.GetRootNode())[0]

と実行すると、見事にノードグラフが再現されました。

ここまでくればもう好きにやりたい放題です。

今回は以下のようなテンプレートファイルにしました。


<katana release="3.0v6" version="3.0.1.000002">
  <node name="__SAVE_exportedNodes" type="Group">
    <node baseType="Alembic_In" name="%(NAMESPACE)s_Alembic_In" selected="true" type="Alembic_In" x="413.984309" y="-302.346567">
      <port name="out" type="out"/>
      <group_parameter name="%(NAMESPACE)s_Alembic_In">
        <string_parameter name="name" useNodeDefault="false" value="/root/world/geo/%(NAMESPACE)s"/>
        <string_parameter name="abcAsset" useNodeDefault="false" value="%(ABC_FILE)s"/>
        <number_parameter name="addForceExpand" value="1"/>
        <string_parameter name="addBounds" value="root"/>
        <number_parameter name="fps" value="24"/>
        <number_parameter name="addToCameraList" value="0"/>
        <group_parameter name="timing">
          <string_parameter name="mode" value="Current Frame"/>
          <number_parameter expression="globals.inTime" isexpression="true" name="holdTime"/>
          <number_parameter expression="globals.inTime" isexpression="true" name="inTime"/>
          <number_parameter expression="globals.outTime" isexpression="true" name="outTime"/>
        </group_parameter>
        <group_parameter name="advanced">
          <number_parameter name="useOnlyShutterOpenCloseTimes" value="0"/>
        </group_parameter>
      </group_parameter>
    </node>
    <node baseType="GenericAssign" name="%(NAMESPACE)s_LookFileAssign" selected="true" type="LookFileAssign" x="413.084537" y="-370.293177">
      <port name="input" source="%(NAMESPACE)s_Alembic_In.out" type="in"/>
      <port name="out" type="out"/>
      <group_parameter name="LookFileAssign">
        <string_parameter name="CEL" value="(/root/world/geo/%(NAMESPACE)s)"/>
        <string_parameter name="location" value=""/>
        <group_parameter name="args">
          <group_parameter name="lookfile">
            <group_parameter name="asset">
              <number_parameter name="enable" value="1"/>
              <string_parameter name="value" value="%(KLF_FILE)s"/>
              <string_parameter name="default" value=""/>
              <string_parameter name="type" value="StringAttr"/>
            </group_parameter>
          </group_parameter>
        </group_parameter>
      </group_parameter>
    </node>
    <node baseType="LookFileResolve" name="%(NAMESPACE)s_LookFileResolve" selected="true" type="LookFileResolve" x="413.084529" y="-420.346567">
      <port name="A" source="%(NAMESPACE)s_LookFileAssign.out" type="in"/>
      <port name="out" type="out"/>
      <group_parameter name="LookFileResolve">
        <string_parameter name="passName" value=""/>
      </group_parameter>
    </node>
  </node>
</katana>

そして、以下のコードを実行します。

fp = open('/home/chiyama/Documents/katanta/scene_build/template.xml', 'r')
xmlTemplate = fp.read()
fp.close()

xmlData = xmlTemplate % {'NAMESPACE' : 'foobar',
                         'ABC_FILE' : '/home/chiyama/Documents/usdtest/assets/Zombie_OnFire_2B.abc',
                         'KLF_FILE' : '/home/chiyama/Documents/katanta/lookdev/zombie_on_fire.klf'}

xmlNode, result = NodegraphAPI.LoadElementsFromString(xmlData)
newNode = KatanaFile.Paste(xmlNode, NodegraphAPI.GetRootNode())[0]

できたノードグラフはこちら。

これで、ネームスペースと .abc ファイル、.klf ファイルを指定してアセットを読み込むことができるようになりました。これを使えば Katana 上でショットシーンを自動生成することはできそうです(これが一般的な方法かはわからないのですが)。

CG, KATANA

MaterialAssign, MaterialResolve, OpScript

MaterialAssign から MaterialResolve をして OpScript でアトリビュートを上書きする流れが、KATANA の内部を理解する上でとても良い事例になりそうなので詳細を順を追って解説します。

まず最初に、Merge でオブジェクト用ツリーとマテリアル用ツリーを一つにまとめます。この時、オブジェクトにはマテリアル情報はまだ何も登録されていません。

materialAssign というアトリビュートはあるものの、空になっています。

ここで、MaterialAssign を用いて pCylinderShape1 にマテリアルを割り当てると materialAssign アトリビュートに値がセットされます。

次に MaterialResolve ノードを通すと、オブジェクトに material アトリビュートが追加され、かわりに materialAssign アトリビュートが空になります。

この時にはマテリアル内の値は元々のマテリアルにあったものそのままです。

最後に OpScript で Alembic に含まれていた値をマテリアルの値にセットします。

ちょっと注意が必要なのが、マテリアル内のシェーダーネットワークのアトリビュートはリアルタイムに更新されず、Refresh ボタンを押すことで最新の情報が更新されることです。KATANA は情報のリアルタイム更新周りはあまり力を入れていないようで、慣れないとたまにオヤッ!?と思うことがあります。ただ、リアルタイム更新周りを充実させるとどんどんアプリケーションが重くなっていき、起動が遅くなったり普段のオペレーションに支障が出てくるようになるので個人的には今の方が好きです。

CG, KATANA

Mayaからカスタムアトリビュートを渡す

Maya のリグなどから KATANA にカスタムアトリビュートを渡し、その値によってシェーダのパラメータを変えてみます。これができれば、Maya と KATANA を接続する際の最大の難関が突破できたことになります(多分)。

そして、この方法を調べるのがものすごい大変でした、、、まだまだ KATANA のことを理解してないなぁと痛感します。

まずは、Maya から Alembic データを出力するときにカスタムアトリビュートを追加します。これは簡単で、出力するメッシュにカスタムアトリビュートを追加して値を入れ(もちろんキーフレームアニメーションも可/ベイクはした方がよいでしょう)、Export ダイアログ中で Attributes にカスタムアトリビュート名を追加して出力するだけです。

出力したファイルを KATANA で読んでマテリアルをアサインするところまではこれまでと同じです。

カスタムアトリビュートの値をシェーダーに持っていく前に MaterialResolve でオブジェクトにマテリアル情報を焼き付けておきます。こうすることで、オブジェクトに付加されたアトリビュートとしてシェーダのパラメータにアクセスできるようになります。

その後、OpScript を使用して Alembic のカスタムアトリビュートをマテリアルのパラメータにセットします。この時、既にオブジェクトにマテリアル関連のアトリビュートが追加されているため、この値を操作しているだけということに注目します。

そのようにしてレンダリングしたのが下の画像です。左から base が 0.0, 0.5, 1.0 となっていて、それに合わせて色が変わっています。

CG, KATANA

アトリビュートの上書き

リグの情報をジオメトリキャッシュに埋め込んで、その情報を元にシェーダのパラメータを指定したいような場合がよくあります。たとえば皺用ディスプレイスメントとか色を変化させるような場合ですね。

この場合、AttributeSet ノードを使用してアトリビュートを上書きすることで実現できます。

今回はマテリアルの色を上書きしてみます。

AttributeSet ノードを作成した後、パラメータを上書きしたいノード(/root/materials/zombie_Mtl)を指定し、上書きしたいアトリビュートを attributeName で指定します。これは Attributes タブから簡単に探すことができます。

今回は base という float 値なので attributeType を float にし、numberValue は 1×1 で値を 0.0 にします。

すると、元の suaface_shader ノードでは 1.0 を指定して赤色になっていたジオメトリが黒色になりました。
値は他のパラメータと同様アニメーションカーブをアサインできたり、Expression で他のところから値を取得することができるので、好きなところから値を参照することができます。

CG, KATANA

KATANA の便利ノード達

これまで作ってきたような単純な例ならいいのですが、本番で使おうとするとこの何十倍も複雑なシーンを扱わなければいけなくなります。

そんなときでも Node Graph を整理するための便利ノードが幾つか用意されています。

Backdrop ノード

ノードを処理単位で枠で囲ってコメントを書くことができるノードです。これは通常のノードを作るのと同様、Tab→Backdrop でも作成できますし、囲いたいノードを選択した状態で Edit→Fit Backdrop Node to Selected Nodes でも作成できます。

作成後、ダブルクリックでノードの編集用ダイアログが開き、そこでメッセージや色の指定ができます。

Grouping ノード

Node Graph の一部をグループ化して、見た目上一つのノードとして扱うことができるノードです。

グループ化したいノードを選択してから ‘g’ キーを押すと、シュルシュルッと一つのノードにまとまります。

見た目は一つのノードですが、内部の構成はそのまま残っていてノードの + ボタンを押すと中を確認することができます。

Dot ノード

これは既に使っていますね。ノード間を接続しているエッジを曲げたり分岐したりするのに使います。Tab→Dot で作成できます。

これらのノードを使用して Node Graph の構成を綺麗に保つことはとても大事です。試行錯誤中はどうしてもノードが汚くなってしまいがちですが、そのままでは他の人が見た時にどこで何の処理をしているのか把握するのが難しくなってしまいますし、作った本人ですら時間が経つと細かい内容を忘れてしまいます。

そのようなことが無いよう、常日頃から綺麗なノード構成になるよう心掛けておくことが大事です。心の乱れはグラフの乱れ、グラフの乱れは作品の乱れ。南無~