今回のアドベントカレンダー、レベル高すぎるよ! 今までレベル高すぎたので、この辺で、全然役に立たない記事をぶっこんでも大丈夫ですよね! 皆さんこんにちは、八田です。レイトレ順調でしょうか? 自分はまだ現時点で1行もコードを書いていません!!! 正確には、3月くらいにMMDBridgeRT というNVIDIA Optix 製のレンダラーを MikuMikuDance用に作りましたが、これは CUDA 必須であり、レイトレ合宿では残念ながら使えません。 これについて言いたいことは1つだけで、TwitterでMMDBridgeRT で検索してみてください。 なぜか、イケメンの画像等が大量に出てきますよね?? 日本ではパストレでイケメンの男モデルをレンダーする需要があるッ…!(女性にも人気!) MaterialX 今回は MaterialX についてです。 前回の SIGGRAPH で、仕様書が pdf 1枚 公開されただけで、首を長くして待っていましたが、 昨年調べた情報(古いので注意)によると、マテリアル情報をXMLに書いて定義できる、外付けのファイルフォーマットで、いろいろなシーングラフや3Dオブジェクトに対してマテリアルを定義でき、シェーダーまでぶっ込めるもののようです。 考えてみると、世の中には共通のマテリアルフォーマットは皆無でした。 Substance Painter というツールがありますが、SDKは公開されていなくてSDK利用は企業からしても爆高いという噂ですし、 NVIDIA MDLとかプロプライエタリなものは最近出てきましたが、オープンソースではありません。 シーン交換のためのファイルフォーマットである、Alembicも、Materialは去年ようやく基本的な部分がついた程度です。 Pixar USDはAlembicに対応しており、さらに独自のメタデータやら何やらを自由に入れられるようですが、 シーンデータ交換のためには、ある程度の縛りが必要と思います。 このMaterialXは、前回調べたところによると、ある程度フォーマットが縛られていてAlembicと併用可能で、大手が作ってることもあり、期待ができます。 早速ビルドしていきます。 git cloneして、いつも通り CMakeします。VS2015でやっています。 特に何事もなく、configure が終わるんですが、MATERIALX_BUILD_PYTHONというチェックがあるようなので 当然チェックを入れて、generateします。 ソリューションができたので、早速 Release でビルドします。 どうやらPythonを使っているプロジェクト以外はビルド成功しているようです。 これはビルド楽勝っぽいですね! 残りのビルド(PyMaterialX)を成功させるために、Python2.7系の64bit版をインストールします。 インストール場所は、プロジェクトファイル見る限り、デフォルトの C:\Python27で良さそうです。 Pythonのバインディングは、pybind11を使ってるようです。 このライブラリはboost::pythonよりもお手軽に使えるヘッダーオンリーのライブラリでとても良いです。最近shared_ptrにも対応しました。MaterialXではpybind11のshared_ptrのバインドも使っているようですね。 Python27をインストールすることで、無事ビルドが成功しました。 出来上がったブツ(のうち使うと思われるもの)は以下の通り。
Testのexeができていますが、まぁこれはただの単体テスト用でしょう。ライブラリの使い方を見るときには参考になります。 ライブラリなので、Pythonからpydを使うか、C++からMaterialX~.libを使うことになると思います。 早速使っていきます。 ビルドが面倒なのでpython版を使ってみます。 import PyMaterialX as mx print dir(mx) とりあえずこのようなtest.pyファイルをPyMaterialX.pyd と同じディレクトリに作り、 C:\Python27\python.exe test.py として実行します。もしくはインタラクティブコンソールでお試ししても良いです。 すると、以下のような出力が出ます。 ではじゃんじゃん行きます。MaterialXTestのDocument.cppを見ながら # coding: utf-8 import PyMaterialX as mx # ドキュメント新規作成 doc = mx.createDocument() # ノードグラフの作成 nodeGraph = doc.addNodeGraph() # ノードグラフに constant ノードを追加 # C++では addNode() constant = nodeGraph._addNode("constant") # constant ノードにパラメータを設定 # C++では setParameterValue() constant._setParameterValuecolor3("value", mx.Color3(0.5, 0.5, 0.5)) # 出力ノードの追加 output = nodeGraph.addOutput() output.setConnectedNode(constant) # XMLファイル文字列を表示 print mx.writeToXmlString(doc) これでXMLが出力されます。このXMLがMaterialX( .mtlx )です。 ノードエディタでノード作ってるみたいな感じのコードで分かりやすいですね。 微妙に関数がC++とPythonで違うのが嫌ですが、"_"付きなので、そのうち変更されるかもしれません。 公式のPythonサンプルコードでは、"_"無しで書いていますが、もちろん現状それでは動きません。 とりあえず以下のコードを付け足してファイルにも保存しておきましょう。 # XMLファイル出力 mx.writeToXmlFile(doc, "test.mtlx") できました! 一応読み込みもやっておきます。 # coding: utf-8 import PyMaterialX as mx # ドキュメント新規作成 doc = mx.createDocument() # mtlxファイルから読み込み mx.readFromXmlFileBase(doc, "test.mtlx") # 有効なドキュメントか? print doc.validate() 問題なく True になります。 ついでに前回調べたとき(=ソース公開前)にBlenderのプラグインから出力して作った、オレオレmtlxファイルを読み込んでみたところ、validate()でTrueが返ってきました!普通にtraverseもできる。ちょっとうれしい。ただ、opgraphなどのエレメントは現行仕様では消滅しており、nodegraphなどという名前に変わっている。コードを漁ると、ちゃんと過去のバージョンもサポートしているので使えるようではあるが… さて、妙にノードっぽいコードでしたが、MaterialXTestのサンプルを見ていると、Node.cppにこんなコメントがあります TEST_CASE("Topological sort", "[nodegraph]") { // Create a document. mx::DocumentPtr doc = mx::createDocument(); // Create a node graph with the following structure: // // [constant1] [constant2] [image2] // \ / \ / // [image1] [add1] [add2] // \ / \______ | // [multiply] \__ [add3] [noise3d] // \____________ | ____________/ // [mix] // | // [output] // もうこれどっからどうみてもノードエディタで使ってくれ(ノードエディタ作ってくれ)と言っているようにしか見えません!Topological Sortってノードエディタ作るときには大抵やりますよね。MaterialX = ノードベースのマテリアルフォーマット、と考えて問題ないと思います。 他、ちょっと目についたところとしては、Observerの仕組みが組み込まれているようです。Observe可能なDocumentを作成することができ、それに対してObserverを複数登録できます。これにより、アプリケーション内でマテリアルを編集した時等の通知を受け取ることができます。 次に、 .obj ファイルのMTL の代わりのmtlxファイルを作ってみることにします。 手始めにめちゃくちゃシンプルなやつで。 objファイルとしてはこんな感じ (cabbage.obj) # Blender v2.78 (sub 0) OBJ File: '' # www.blender.org mtllib cabbage.mtl o cabbage v 0.518277 1.045460 -1.303864 v 0.745456 0.803751 -1.099580 v -0.926315 2.123996 0.604743 v 0.008301 1.787386 1.152170 v 0.669115 1.202087 -0.994056 ・・・ mtlファイルとしてはこんな感じ (cabbage.mtl)# Blender MTL File: 'None' # Material Count: 2 newmtl cabbage Ns 92.156863 Ka 1.000000 1.000000 1.000000 Kd 0.398431 0.398431 0.398431 Ks 0.000000 0.000000 0.000000 Ke 0.000000 0.000000 0.000000 Ni 1.000000 d 1.000000 illum 2 map_Kd cabbage_diff.jpg これのmtlxファイルを作ります。 MaterialXの構造は以下のようになっています。 ![]() やることは恐らく以下の通りです。
これを行うとこういうコードになります。 # coding: utf-8 import PyMaterialX as mx # 1. ドキュメント新規作成 doc = mx.createDocument() # 2. ジオメトリ定義の追加 geominfo1 = doc.addGeomInfo("geominfo1", "cabbage") geominfo1._setGeomAttrValuestring("description", u"これはキャベツ", "") # 3. ドキュメントにノードグラフを作成 nodeGraph = doc.addNodeGraph() # 4. ノードグラフに出力ノードの追加 output = nodeGraph.addOutput() # 4. ノードグラフにimageNodeを追加 imageNode = nodeGraph._addNode("image", "image_node") imageNode._setParameterValuestring("file", "cabbage_diff.jpg") # パスを設定 # 5. Mateiralの作成 material = doc.addMaterial() # 6. Shader の作成 shader = doc.addNodeDef("shader1", "surfaceshader", "simpleSrf") Ns = shader.addInput("Ns", "float") Ka = shader.addInput("Ka", "color3") Kd = shader.addInput("Kd", "color3") Ks = shader.addInput("Ks", "color3") Ke = shader.addInput("Ke", "color3") Ni = shader.addInput("Ni", "float") d = shader.addInput("d", "float") illum = shader.addInput("illum", "float") Ns._setValuefloat(92.156863) Ka._setValuecolor3(mx.Color3(1.000000, 1.000000, 1.000000)) Kd._setValuecolor3(mx.Color3(0.398431, 0.398431, 0.398431)) Ks._setValuecolor3(mx.Color3(0.000000, 0.000000, 0.000000)) Ke._setValuecolor3(mx.Color3(0.000000, 0.000000, 0.000000)) Ni._setValuefloat(1.000000) d._setValuefloat(1.000000) illum._setValuefloat(2.000000) # 6. MaterialにShaderを登録 shaderRef = material._addShaderRef("shaderRef1", "simpleSrf") # 7. imageNode → ノードグラフのoutput と接続 output.setConnectedNode(imageNode) # 8. output → Kd と接続 bindInput = shaderRef.addBindInput("Kd") bindInput.setConnectedOutput(output) # 9. Lookの作成 # Lookにmaterialassignを入れることにより material → Look ← Geometory という接続を作る. look = doc.addLook() materialAssign = look.addMaterialAssign("", material.getName()) materialAssign.setGeom("cabbage") # XMLファイル文字列を表示 print mx.writeToXmlString(doc) # XMLファイル出力 mx.writeToXmlFile(doc, "test.mtlx") 出来上がったファイルはこんな感じ <?xml version="1.0"?> <materialx version="1.35"> <geominfo name="geominfo1" geom="cabbage"> <geomattr name="description" type="string" value="これはキャベツ" /> </geominfo> <nodegraph name="nodegraph1"> <output name="output1" type="color3" nodename="image_node" /> <image name="image_node" type="color3"> <parameter name="file" type="string" value="cabbage_diff.jpg" /> </image> </nodegraph> <material name="material1"> <shaderref name="shaderRef1" node="simpleSrf"> <bindinput name="Kd" type="color3" nodegraph="nodegraph1" output="output1" /> </shaderref> </material> <nodedef name="shader1" type="surfaceshader" node="simpleSrf"> <input name="Ns" type="float" value="92.1569" /> <input name="Ka" type="color3" value="1, 1, 1" /> <input name="Kd" type="color3" value="0.398431, 0.398431, 0.398431" /> <input name="Ks" type="color3" value="0, 0, 0" /> <input name="Ke" type="color3" value="0, 0, 0" /> <input name="Ni" type="float" value="1" /> <input name="d" type="float" value="1" /> <input name="illum" type="float" value="2" /> </nodedef> <look name="look1"> <materialassign name="materialassign1" material="material1" geom="cabbage" /> </look> </materialx> validate()でもTrueがちゃんと返ってきます。 うーん、なんかめっちゃ色々あって、お手軽・・・では無いですね。 MTLではmap_Kdのテクスチャは、Kdとすり替えて使うのか、Kdとミックスして使うのか、Kdと乗算するのか、そのあたりは仕様を読まないと分からなかったですが、MTLXでは、ノードの接続によりimageをKdに接続しているのは明らかなので、Kdのvalueとしてimageを使うというのが明確であるような気がします。 これを表示するためのコードを書くのは結構しんどいので、このあたりで調査終了ということにしておきます。 今後は、Blenderからmtlxを書き出す自作プラグインを、MTLXの最新仕様に沿って書き換えたりしたいと思います。 以上がMaterialX セカンドインプレッションになります。 今年の SIGGRAPH では、Pixar から OpenTimelineIO というものが公開されるようでそちらも楽しみですね。 |