SOLARISのVEXで遊ぼう
Houdini Apprentice Advent Calendar 2020
5日目は
「SOLARISのVEXで遊んでみよう」
です。
ゴール¶
まず今回の目的ですが、私自身Pythonでコードを書いたりすることはできますが
HoudiniのVEXは全く書けないため、その書き方の基本的な部分を理解することが
今回のゴールになります。
VEXで書いたら何ができる?
基本的な文法は?
どういうときに向いてる?
といった基本的なところを調べながら書いていこうと思います。
基本的な文法は
https://www.sidefx.com/docs/houdini/vex/lang
公式ドキュメントを参考にします。
まずは使ってみる¶
まずは基本的なVEXを書くところまでやってみます。
まず、SOLARISでVEXで色々処理するために attribwrangle ノードを作り
こんな感じのノードを作ります。
attribwrangleノードのVExpression に、
1 |
|
と入力してみます。
無事Cubeが出現しました。
こんな感じで、VEXにはUSD用の関数が複数追加されていて
これを利用してUSDのシーングラフを操作をすることができます。
処理の対象について¶
VEXで処理をする対象は Primitives で指定した入力Primが対象です。
1 |
|
デフォルトがこちら。
Input 0 に入力されているPrimのうち
最後に編集されたPrimの数だけVEXの処理が実行されます。
なので、こんなかんじで複数PrimをマージしたあとにWrangleにつなぐと
CubeとSphereの2回分実行されます。
複数処理する場合は、その処理ごとに @primpath などの Built-in variable(後で説明)が
Primの処理ごとに置換されます。
なので、入力が複数であっても for で繰り返しなどを書く必要がありません。
Build-in variables¶
Build-in variables とは、SOPでいうところの @Cd や @P といったアトリビュートと同じで
InputのPrimを受け取るための変数(?)です。
受け取るPrimitivesは、このPrimitivesで指定されたものになります。
つまり、これを利用するとInputで与えられるなにかしらのPrimitiveに対して
Forを使用して何かしらの処理をまとめて実行することができます。
たとえば、入力のPrimに対してAttributeを追加したい場合。
Pythonの場合は
1 2 3 4 5 6 7 8 |
|
こうなります。
では、同様の処理をVEXで書いてみるとどうなるかというと
1 2 |
|
こうなります。
Build-in variables の @primpath は、入力のPrimitiveのPathに置換されます。
入力が多数ある場合、このVEXpressionに書かれた記述が
入力のPrimitive全てに対して実行され、その実行都度、 Build-in variables は
処理するPrimitivePathに置換されます。
つまり、↑だけで繰り返し処理を書いたのと同様の処理になるわけですね。
なるほど。
このBuild-in variables は、
@primpath @elemnum @numelem @primtype @primkind @primname @primpurpose
@primdrawmode @primactive @primvisible
の計10個あります。
これを利用すれば、
指定のスキーマのときだけ
Xformにだけ移動値を入れたい、Cubeのときだけ動かしたいなどといった
特定の処理を書いたりできます。
制御構文¶
if¶
まずは if 文。
1 2 3 4 |
|
指定のPrimTypeのときのみなにか処理を実行。
usd_is###系の関数を使えば、色々判定ができるので
その判定を利用して if 文で処理をわけたりできます。
for¶
次に、 for で複数のPrimを作ってみます。
1 2 3 4 |
|
Inputのノード以下に複数のPrimを作ります。
for文などは他の言語とほぼ同じ感じで使用できました。
1 2 3 4 |
|
固定の数値というのもあれなので、Houdiniのアトリビュートを追加してみた例。
今までの関数も使用できます。
USD固有の関数を使う¶
次は関数の使い方。
https://www.sidefx.com/docs/houdini/solaris/vex.html
USDのVEXの一覧はこちら。
ぱっと見た感じ、USDの操作は一通りのことができそうです。
使いそうなものからいくつかピックアップして試してみます。
Prim名を取得¶
1 |
|
現在処理をしているPrim名を取得します。
関数を使う場合の1つ目の引数はStageの読み先となる入力番号を指定します。
なので、Wrangleノードの1つ目の入力にノードが接続されている場合は 0 になります。
usd_nameの場合 primPathを指定するとPrim名が帰ってくるので
現在処理中の PrimPathに置換される @primpath を指定すればよさそうです。
移動¶
1 |
|
移動などの処理はすべて関数が用意されていて、このようにすると
指定のPrimを移動することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
その場合は、移動情報を追加することができます。
ただ、この場合は移動するPrimがXformである必要があるようで
(UsdGeom.XformCommonAPIを使用するときも同様なので、このCommonAPIの仕様っぽい)
注意が必要です。
そもそもSphereに対してxformできてしまうのが問題かもしれないです。
transform以外にも rotate scaleなどの関数があるので
Wrangleで移動などの操作がしたい場合は同じ方法でできます。
メタデータの編集¶
1 2 |
|
USDはレイヤー、Prim、アトリビュートそれぞれに対してメタデータを仕込むことができます。
スキーマで指定されたメタデータであれば documentation のように指定できるし
それ以外の任意の情報であれば、 customData:~~の形式でセットできます。
1 |
|
メタデータの取得もできるので、
USD側になにか情報を入れておいて処理したい場合などは
この方法で対応できます。
Primを作成¶
1 |
|
指定のPathに対してPrimを生成します。
3つ目の引数がPrimのタイプで、この場合はステージに1つのCubeを作ります。
まとめ¶
Build-in variables と、ステージハンドルの意味を抑えておけば
あとはほぼ関数名通りの昨日をVEXで使用できました。
関数の一覧を見る限り、PrimやAttributeの操作を実行するものは
ほぼVEXでなんとかなりそうです。
ただし、Namespaceを変更したりLayerに関わる操作はこちらではできなそうなので
あくまでも入力のステージのうち該当するPrim or Attributeに対して
何らかの処理をしたい場合に使うのが良さそうです。
アトリビュートへのアクセス¶
次にアトリビュート周り。
関数を使用するとアトリビュートへのアクセスができましたが
VEX的な書き方でもっと簡略化ができるようです。
SOPでは、GeometrySpreadSheetなどで
各オブジェクトの頂点だったりフェースだったりノーマルに対して
1 |
|
このようにジオメトリのアトリビュートや情報にアクセスしていました。
SOPでは、まさにジオメトリごとのアトリビュート操作がキモになっていて
このアトリビュートに対して色々セットしたり取得したりして
処理を書くことができます。
ではLOPの場合はどうかというと、USDのアトリビュートに対して
編集したり取得したりすることが同様の記述方式できます。
セットする¶
まずはかんたんなサンプルから。
こんなかんじでSphere1を作り、attribwrangleノードにつなげます。
そしてアトリビュートを追加して、
1 |
|
VEXにこう書きます。
結果。
fは、アトリビュートの型で、@以降はUSDのAttribute名を指定することで
USDのアトリビュートに対して値を設定することができます。
この型は、いわゆるUSDのスキーマで定義されているものです。
スキーマが持つアトリビュートは、 Edit Parameter Interface のFromUSDに一覧があるので
スキーマのアトリビュートを編集できるようにするのならば、
このスキーマのアトリビュートをノードに追加して
VEXでこのアトリビュートの値をセットする ... のようにすれば
編集できるようになりそうです。
アトリビュートを作成する¶
これはすでに定義済のものでしたが、
1 |
|
例えばこうすると、
1 2 3 4 5 6 7 8 9 10 11 12 |
|
customのアトリビュートが追加されます。
つまりは、USDに対して自分の好きな値を埋め込むことができるわけです。
SOLARIS上でUSDのアトリビュートを操作する方法は、私が調べた限りだと
Python側から操作する以外見当たらず、アトリビュートの編集の方法がわかりませんでした。
しかし、このVEXからだと
Pythonから操作するよりも遥かに簡単に かつHoudini的にアクセスできるので
こちらを使うのが正解のようです。
xformOp¶
上のような単純なアトリビュート以外にも、移動などのパラメーターも設定できます。
1 2 |
|
VEXでPrimを移動する例。
1 2 3 4 5 6 7 8 9 10 11 12 |
|
実行すると、指定のPrimに対して translate のアトリビュートが追加されました。
このxform:### というのは、USDの XformPrimなどでの移動値などを保持するアトリビュートです。
1 2 3 4 5 6 7 8 |
|
Pythonの場合は、こんな感じで、指定のXformPrimに対して SetTranslateすると
xformOp:translate と xformOpOrder に対してVEXで書いた内容を追記します。
VEXだけだとものすごいわかりにくいですが、
USDの記述を手書きで書いているのと同じように、移動などの値も編集できるわけですね。
(超マニュアル操作!!)
このxformOpOrderというのは、アトリビュートとしてセットされた
xformOp:#### の操作順をどの順で実行するか という意味になります。
Translate1つならば問題ありませんが
1 2 3 |
|
例えば 移動して回転する例の場合、
X方向に回転してから移動するのと、移動してから回転するのでは結果がかわります。
その操作順を指定するのがこの xformOpOrder で、
これはUSD側の記述ルールです。
なので、USD側の挙動がわかっていればVEXの type@attrname = ~~~~ で
USD側のアトリビュートを編集したり加工したりできるわけですね。
https://graphics.pixar.com/usd/docs/Referencing-Layers.html
一応公式Docsのチュートリアルを見ると、アトリビュート名を確認できます。
SOPなどでのアトリビュートコントロールをLOPに当てはめると
LOPの中ではUSDのコントロールすることになります。
とはいえ、ほぼusdaのアトリビュートを手書きするようなものなので
VEXでなにかする場合はUSDのAPIやスキーマの把握が必須になりそうです。
そのかわり、USDの構造をほぼダイレクトに操作できるので
構造を理解できればかなりシンプルにUSDの構造を操作する事ができそうです。
パフォーマンス¶
最後にパフォーマンスについて。
ほぼ同様の処理はPythonでも書けますが、Pythonと比較してどのぐらいの速度差があるか
確認してみます。
とりあえずCube100個を作ってみます。
VEXの場合
1 2 3 4 |
|
Pythonの場合
1 2 3 4 5 6 7 8 |
|
わかってはいたものの、驚愕の速度差です。
基本繰り返し処理に関してはPythonは避けてVEXかLOPのノードで実行するほうが
良さそうです。
まとめ¶
とりあえず、基本的な書き方のみを調べるだけになってしまいましたが
おかげでいつかわかったことがあります。
まず使い方について。
VEXの場合は、
Primitivesで指定されたPrimに対して何らかの処理を書くことができる。
基本的なものは関数でカバーされているのでそれを使用すれば
USDの操作はほぼカバーできるので、Primに対しての処理はVEXを使うのが良さそうです。
アトリビュートを操作する
SOLARIS上ではUSDのアトリビュートの操作を可能にするノードがあまりありません。
(ConfigurePrimを使えばMetaDataの編集は可能)
なので、基本Houdini上にUSDのアトリビュート編集をできる
Houdiniのアトリビュートを作った場合は、このVEX上で
値をセットさせるのが良さそうです。
ただし、配置したりに関しては
現状だとSOPでPointを作りつつ組み立てるほうがやりやすそうなきがしました。
少し前にこんなつぶやきをしたのですが、
SOLARIS自体にしても、SOLARISのVEXにしても USDの構造をほぼマニュアル操作で
いじくり回す必要がある(とくに構造を作ったりする場合)ので
このあたりをいい感じにカバーしつつ
USDの構造などを完全に把握しなくても
USDのワークフローが作れるようにしていくのが今後の課題かなと思いました。