本ブログではHoudiniのWrangleやVEXについていくつかの考察記事を書いてきました。
Houdiniを使い続けていく中でより考えがまとまってきたので知識を整理するために記事を書いておきます。
本記事では学び初めの頃しっくり来なかったWrangleのRun Overの仕組みとVEXの並列処理について、個人的に馴染み深いスクリプト言語であるJavaScript*1と比較しながら振り返ってみます。
本記事は現時点での僕の理解をまとめたものですので、間違いが含まれている可能性があります。ご容赦ください。
サンプルファイル
VEXpressionテキストエリアとRun Overの仕組み
TABキーで呼び出せるPoint Wrangle、Primitive Wrangle、Vertex Wrangleの中身はAttribute Wrangleで、Run Overのみが異なっています。よく使うものをテンプレートとして用意してくれているというわけですね。*2
下記画像をご覧ください。
ここでは
- Run Overが指定するものは何か
- VEXpressionはJSで言うところのどこを表しているのか
に焦点を当てて考察していきます。
VEXpressionテキストエリア
Point Wrange シュミレーション
JSとVEXで似たような状況をシュミレーションしてみました。イメージとしては下記画像の状態をJSで再現したと思ってください。※VEXコードを実行した出力結果については後ほど見ていきます
続いてJSのコードを見てみましょう。*3
上記実行結果は下記のとおりです。実行はコマンドラインで
$node point_wrangle_sample.js
とかでいいでしょう。
Vimmerであれば外部コマンドの実行、:!node %
でももちろんOKですね
出力結果
point.ptnum: 0 point.numpt: 4 point.P: 0,0,1 point.P[0]: 0 point.Alpha: 1 point.pscale: 0 ----- point.ptnum: 1 point.numpt: 4 point.P: 0,0,-1 point.P[0]: 0 point.Alpha: 1 point.pscale: 0 ----- point.ptnum: 2 point.numpt: 4 point.P: 1,0,0 point.P[0]: 1 point.Alpha: 1 point.pscale: 0 ----- point.ptnum: 3 point.numpt: 4 point.P: -1,0,0 point.P[0]: -1 point.Alpha: 1 point.pscale: 0 -----
何も難しいことはありませんね。
- ポイントを模した
p0
からp3
をオブジェクトとして定義 - それらのポイントを配列
points
に格納 for文
で配列を走査します。配列のメンバをそれぞれローカル変数point
に代入、メンバのパラメータにアクセスし出力します
ここで重要なのはforループ
内のpoint.hoge
がVEXでいうところの@hoge
に相当すること。
またAttribute WrangleのVEXpressionに記述されるコードはfor文
内のコードブロックに相当することです。
VEXの@
は他の言語でいうところのフェッチに相当しますが、「ああ、オブジェクトのパラメータにアクセスしてんのね」という感覚的な理解が大事なのかなと思います。
続いてPrimitive Wrangleを見ていきましょう。
Primitive Wrange シュミレーション
イメージ的には下記の通り。VEXコードもそうですが、Geometry Spreadsheetも参考ください。これをJSで書いてみます。
これも実行してみましょう。
出力結果
prim.primnum: 0 prim.numprim: 2 prim.shop_materialpath: "/mat/iron" prim.Cd: 0.37,0.96,0.41 ----- prim.primnum: 1 prim.numprim: 2 prim.shop_materialpath: "/mat/plastic" prim.Cd: 0.7,0.04,0.93 ---
こちらも難しくないでしょう。
- プリミティブを模した
prim0
、prim1
をオブジェクトとして定義 - それらのプリミティブを配列
prims
に格納 for文
で配列を走査します。配列のメンバをそれぞれローカル変数prim
に代入、メンバのパラメータにアクセスし出力します
Pointのときと全く同じです。
forループ
内のprim.hoge
がVEXでいうところの@hoge
に相当する- Attribute WrangleのVEXpressionに記述されるコードは
for文
内のコードブロックに相当する
ということですね。
Run Overの仕組み
ここまで見れば一目瞭然ですね。Run OverはJSのfor文
で言うところのpoints
やprims
を示します。そんなの分かっとるがな。という方も多いかと思いますが、ややこしいのが実際のジオメトリではpointやprimitiveには相関性があり、Run Overのターゲットとは異なるコンポーネント、そのアトリビュートにアクセスできてしまうという点です。
これはvertexがpointの参照として定義されていたり、またそれらの集合としてprimitiveが形成されているためなかば当然に起こることなのですが、ここが混乱を生みます。(ぼくは混乱しました)
現在の僕の理解、イメージ、作法としては、Run Overで指定したコンポーネント以外のアトリビュートにはアクセスしないという考え方に落ち着いていて、他のコンポーネントのアトリビュートを使いたい場合は、Attribute Promoteなどを使って事前にRun Overのターゲットに移植しておくという手法が最もスマートだと思っています。
WrangleのRun Overは君しか見えない!君以外考えられない!というイメージで使うと良いのかなと思う次第です。
VEXの並列処理とは
VEXという言語があるのにわざわざJSで解説したのは、もちろん僕がJSに慣れているからというだけではありません。他の言語と比較することで違いが分かりやすいかと思ったからです。
サンプルファイルのノードでどのような出力になるか見てみましょう。
pointwrangle1
出力結果
@ptnum: 0 @ptnum: 1 @ptnum: 2 @ptnum: 3 @numpt: 4 @P: {0.0,0.0,1.0} @P: {0.0,0.0,-1.0} @P: {1.0,0.0,0.0} @P: {-1.0,0.0,0.0} @P.x: 0.0 @P.x: 0.0 @P.x: 1.0 @P.x: -1.0 @Alpha: 1.0 @pscale: 0.0 -----
primitivewrangle1
出力結果
@primnum: 0 @primnum: 1 @numprim: 2 @shop_materialpath: "/mat/iron" @shop_materialpath: "/mat/plastic" @Cd {0.37,0.96,0.41} @Cd {0.70,0.04,0.93} -----
verticeswrangle1
出力結果
@ptnum: 0 @ptnum: 1 @ptnum: 2 @ptnum: 0 @ptnum: 1 @ptnum: 3 @numpt: 4 @creaseweight: 3.0 @creaseweight: 3.0 @creaseweight: 3.0 @creaseweight: 0.0 @creaseweight: 3.0 @creaseweight: 0.0 -----
出力を確認しよう
見ての通り、JSとは出力のされ方が異なっていますね。
JSのようにコードブロックの上から下まで評価してから次のメンバに行くのではなく、1行のコードを各メンバごとに評価し、すべてのメンバが処理し終えたら次の行を評価しに行くという順番になっています。
そして@numpt
や@numprim
、本例での@Alpha
や@pscale
などすべてのターゲットで同じ値のものは1行だけ出力されています。*4
これがVEXの並列処理と呼ばれるものです。この処理順序を意識するシーンはそれほど多くないでしょうが、プリントデバッグを行うケースでは仕組みを理解していたほうが良いでしょう。
また、Attribute Wrangleは基本的に、Run Overで指定したコンポーネントのひとつひとつの要素を順に処理していくため、要素ひとつひとつの変更がドミノ倒し的(帰納的)に反映されるような実装はできません。そのような処理を行いたい場合はRun OverをDetail(Once Only)
に指定する必要があります。並列処理が理解できるとここもスッキリするのではないでしょうか。
まとめ
いかがでしたでしょうか。今回まとめた内容はどれもぼくが去年つまづいた部分になります。
じっくりと考え直したきっかけは下記表現を作っていた時です。
Houdiniでカメラの方向とジオメトリの法線の内積を取って色情報に変換、スキャッターのデンシティに利用したあとグローで加算するテスト pic.twitter.com/XqlPUeUPPv
— 30億のデバイスで走るめんたいこ (@kickbase) 2018年2月17日
これについてはアドバイスを頂き理解が深まったり、またCookの実行速度などを意識するいい機会になったので、次回の記事でまとめてみようと思います。
本記事がどなたかのお役に立てば幸いです。