Houdini

提供: Houdini CGWIKI Jp
移動先:案内検索

Houdiniを手に入れよう : https://www.sidefx.com/ja/products/houdini-apprentice/

私のSOuPコレクションは一般レベルのMayaアーティストにとって役に立つ紹介だったと思います。 現在は私は少しばかりHoudiniを使用しています。SOuPはHoudiniの影響を強く受けており、Houdiniのチュートリアルを参考に移植してみるのも良い考えだと思います。

私は今まで軽蔑していたHoudini伝道師となりましたが謝るつもりはないです。 Houdiniは本当に素晴らしいソフトウェアで、ブラックボックスになっていなくて、今まで以上に習得しやすくなっています。ぜひ触るべきです!

あなたがここに辿り着いてまったくのHouini初心者であるならば、まず最初にHoudiniGettingStartedに進んでください。このサイトの使い方の概要、できるだけ早くHoudiniを快適に使用する方法を説明しています。

目次

Attribute Transfer SOPを使った位置の転送

Attribute transfer hou.gif

Houdiniシーン: File:attribute_transfer_position.hip

球の位置を滑らかに減衰させてグリッドのポイントに転送します。

  1. Grid SOPを作成します。
  2. Sphere SOPを作成します。
  3. Attribute Transfer SOPを作成します。
  4. そのAttribute Transfer SOPの1番目の入力にGrid SOPを、2番目の入力にSphere SOPを接続します。
  5. 'Match P Attribute'パラメータを有効にします。
  6. ConditionsタブのDistance Thresholdを低い値(例えば、0.1)に設定し、Blend Widthパラメータを高い値(例えば、2.0)に設定すると、SOuPのBlendy Warpのようなことができます。


ここで注目すべき事(または、いくつかのサンプルを試した後に見返すべき事) :

  • Pとは位置用の標準アトリビュートで、通常はポイントポジションのことです。Mayaでいうと、これは頂点ポジションに相当します。Mayaではめったにこれらの位置を直接変更することはありませんが(通常では、1レベル上のオブジェクトトランスフォームを制御します)、Houdiniが楽しい理由はここにあり、色々な制御でポイントを操ることができます。
  • このセットアップでは、球の位置を読み込み、その位置をグリッドの各ポイントに減衰させて転送します。減衰なしの設定をした場合、グリッド全体が球の中心の単一ポイントに移動して消えてしまいます。(実際に、Distance Thresholdパラメータを無効にするとそのようになります)。
  • 'ちょっと待った。基本的に頂点を制御しているはずなのに、このセットアップがどうして手品のように球のトランスフォーム情報を読み込んだのでしょうか?グリッドの頂点が球の表面上のすべての頂点に転送されないとおかしくないですか?' 良い質問ですね。その答えは、デフォルトのHoudiniの球(プリミティブ球と呼びます)はMayaのNURBS球またはポリゴン球とは違うからです。これは、Mayaで1点のパーティクルを球モードでレンダリングしたようなものです。つまり、その球には位置とスケールの情報を持っていますが、表面を構成する頂点を持っていないということです。そのことを知れば、このセットアップが少し理解できるのではないでしょうか。Mayaで例えるならば、1点のパーティクル(視覚的には球)を読み込んで、その位置を読み込み、グリッド内の各頂点との距離を調べて、その距離の近さに応じてパーティクルの位置にそれらの頂点を移動させたということです。
  • もちろんHoudiniにはSphere SOPの'Primitive Type'ドロップダウンメニューをご覧のとおり、通常のポリゴン球もNURBS球もあります。
  • 'Match P Attribute'は、通常ではカラーや他のアトリビュートを転送する時にポイントは実際には動かしたくないので無効にするのですが、ここではポイントを'動かしたい'ので必須です。

Attribute Transfer SOPを使ったカラーの転送

Attribute transfer col hou.gif

Houdiniシーン: File:attribute_transfer_color_and_position.hip

上記と同じですが、Attribute Transfer SOPのPointsパラメータのリストに'Cd'を加えました。 複数のアトリビュートを転送したい場合には、スペース区切りでアトリビュートを指定します。 例えば、カラー、法線、pscaleを転送したいのであれば、"Cd N pscale"とタイプします。

Grid SOPにColor SOPを追加してグリッドを赤に、Sphere SOPにもColor SOPを追加して球を緑にすると、何かが変化したのがわかります。

'Cd'って何? HoudiniはRenderMan由来の多くの命名規則を使用しています。Cdは'Colour Diffuse'のことです。'P'はPosition、'N'はNormalといった感じです。

Houdiniが認識するアトリビュートのリスト(この数に驚きました。私はおそらくその10%くらいしか使用していません): https://www.sidefx.com/ja/docs/houdini16.5/model/attributes#attributes

比較用にRenderManのシェーダ言語の変数のリスト: http://renderman.pixar.com/view/shader-global-variables-tables

Point SOP

Point hou 01.gif

シーンをダウンロード: File:point_sop.hipnc

廃止

これはHoudini15.5とそれ以前のバージョンのPoint SOP用でした。それ以降はVEXを利用したSOPに置換されており、このPoint SOPは使い難いので、VOP版とVEX版を使用する方が良いです。もう誰もPoint SOPを使用するべきではないと悟りました。この'改良した' Point SOPは扱いにくいので、SideFXの提案に同意です。 :)

エクスプレッションを使ってグリッドをサイン波に変形します。

  1. Grid SOPを作成します。
  2. Point SOPを作成します。
  3. tyパラメータにsin($PT*$FF*0.1)のエクスプレッションを設定します。


HoudiniにはRenderMan形式のパラメータを持っており、ドル記号で始まる大文字の英数字で定義された変数をよく使用します。 この変数はローカル変数と言ってHScriptエクスプレッションで使用します。

一部のノードでは、現行フレームを意味する$Fなどのグローバル変数にしか対応していないノードがあります。 Point SOPなどの他のノードでは、たくさんのローカル変数が定義されています。 これらのローカル変数は、そのノードのヘルプドキュメントの'ローカル変数'セクションで調べることができます。一部の古いまたは難解なノードでは、ローカル変数がまったく定義されていません。

上記のエクスプレッションの$PTはポイントID、$FFは現行の浮動小数点フレームを意味しています。

変数の呼び名

通常、Houdiniユーザーは、これらのエクスプレッションを見た時に'あぁ、HScriptだね'と言います。 厳密に言えば、(MayaのMELとMayaエクスプレッション言語が異なるのと同様に)Houdiniエクスプレッション言語とHScriptは異なりますが、誰も'Houdiniエクスプレッション言語'と叫んでも、それが間違えていても気にしないです。

同様に、'$VARIABLE'を見た時に'あぁ、ローカル変数だね'と言います。Houdiniはグローバル変数、標準変数、ローカル変数を別の変数として定義していますが、それらを言いくるめてローカル変数と言っています。

Point VOP

Pointvop hou 01.gif

上記のシーンを見てみると、Point SOPの隣に同じ処理をするPoint VOPが置かれています(H13とそれ以前のバージョンでは、処理内容に応じてVOPSOPまたはAttrib VOPと呼んでいました)。VOPに関してはHoudini Vopsで説明していますが、基本的にVOPを使うことで非常に高速なオペレータを記述することができます。

Mayaエクスプレッション、Python、HScriptはどれもインタープリター型のスクリプト言語で、シーンが複雑になるほどパフォーマンスの問題に遭遇します。 VOPはコンパイルされたマルチスレッドコードで、スケーラビリティが非常に良いです。 Mayaでこれに近い事はハイパーシェードで独自のデフォーマを記述することだと思いますが、それよりも多機能です。

Point SOPとHScriptは、Houdiniユーザーが昔から使ってきた手法ですが、これをVOPやVEXに移行させることで、パフォーマンスが得られます。

この場合では、VOPネットワークを一種のフローチャートだと考えて、左から右に向かって解読します。まず最初のセットアップ:

  1. Point VOPを作成します。
  2. Enterキーを押すかダブルクリックでサブネットワークの中に入ります。


そこで、新しいタイプのノードグラフが見つかります。これは、Houdiniシェーダ(SHOP)でも使用されるノードグラフです。 これらのノードは、上から下ではなく左から右に流れます。VOPネットワークでは、Houdiniは'すべてがポイントである'とみなすという事が非常に重要です。 このノードグラフ内の処理は、ジオメトリのすべてのポイントに対して同時に実行される、もしくは、あなたのコンピュータで並列処理が可能になることを意味しています。

一番左側のノードは単一ポイントを表わしており、すべての標準ビルドインアトリビュート(位置、カラー、法線、IDなど)が含まれています。 一番右側のノードはポイントの最終出力を表わしています。 ノードグラフ内のそれ以外のノードにはアトリビュートが暗黙的に渡されるので、入力Pが出力Pへ、入力Nが出力Nへといったように渡ります。 なにかのノードを接続してみると、ポイントの状態が変わります。このノードグラフは、1個のポイントではなくすべてのポイントに対して同時に実行されるので、ジオメトリも変更されます。

上記のPoint SOPの挙動を再現するために、以下の手順を考えました:

  1. ポイントID、現行フレーム、0.002の定数を受け取り、それらをまとめて乗算します。
  2. その結果をサイン関数に接続します。
  3. ポイントポジションを取得し、そのY成分をサイン関数の結果で置換します。
  4. 最後に、その結果をポイントの新しい位置として設定します。


このワークフローをVOPを使ってセットアップします。 おそらく画像のとおりに従う方が簡単です。Tabキーでノード名を入力してそのノードを配置して接続します。簡単。

  1. Mulltiply VOPを作成します。
  2. Constant VOPを作成します。Constant TypeパラメータをFloat、1Float Defaultパラメータを0.002に設定します。
  3. Geometry VOP Global Parameters VOPのFrame出力をそのMultiply VOPの1番目の入力に接続します。
  4. Geometry VOP Global Parameters VOPのptnum出力をそのMultiply VOPの2番目の入力に接続します。
  5. そのConstant VOPのValue出力をそのMultiply VOPの3番目の入力に接続します。
  6. Sine VOPを作成し、そのMultiply VOPのproduct出力をそのSine VOPのrad入力に接続します。
  7. Set Vector Component VOPを作成します。
  8. Geometry VOP Global Parameters VOPのP出力をそのSet Vector Component VOPのvec入力に接続します。
  9. そのSine VOPのsine出力をそのSet Vector Component VOPのfval入力に接続します。
  10. そのSet Vector Component VOPのVector Componentパラメータを'Component 2'に設定します。つまり、ポイントのXYZの2番目の成分のYを設定します。
  11. そのSet Vector Component VOPのnewvec出力をGeometry VOP OutputのP入力に接続します。

Point WrangleとVEX

Point wrangle.jpg

VOPノードは内部でVEXコードを生成しています。 このVEXコードがHoudiniのネイティブのマルチスレッド言語です。 必要に応じてVEXに直接コードを書くことができ、VOPを全く使わなく済ませることができます。 この構文は非常に単純です:

@P.y = sin( @ptnum * @Frame * 0.002 );


これは、ポイントID(@ptnum)毎に現行フレーム(@Frame)を乗算し、さらに0.002を乗算し、その結果を各ポイントのY位置( @P.y )に割り当てます。

HScript, VOPs, VEX (とPython), どちらを使用すべき?

MEL/Python/PyMELとは違って、これはどちらともなく、それぞれ長所と短所があります。

HScriptがMELに近いです。 これはHoudiniのオリジナルのスクリプト言語で、昔から使われているものなのでいろいろな箇所で見受けられます。 古いチュートリアルだとHScriptが使われていることが多く、私はその構文に準拠するのが苦手です。 これはMayaエクスプレッションを記述するような箇所(例えば、Animatable Channel)でよく使われているので、その編集をするのが少し抵抗感があり、MELとtcshスクリプトを変に組み合わたもののように感じます。また、HScriptを使ってでしかプロシージャルに駆動させることのできない古いHoudiniノードはシングルスレッドなのでスケーラビリティもよくありません。

それでもHScriptはトランスフォームレベルでよく使用されています。 例えば、カメラ、ライト、オブジェクト、ROPなどのプロパティを設定する時はこれが役に立ちますが、(例えばPoint SOPでHScriptを使用して)ポイントを編集する時は処理が重すぎてVOPまたはVEXを使った方が良いです。

VOPとVEX(Wrangles)はどちらも同じ処理をします。VEXはコア言語で、VOPはVEXコードを知らなくてもそのコードを生成する事ができるノードUIです。 VEXはマルチスレッドで動作するように設計されていて非常に高速なので、ジオメトリを制御したり、シェーダを書いたり、画像フィルターを作成するといった同じコードを同時に膨大なターゲットに対して走らせるような状況に適しています。 VEXはMELやPythonと違ってストレスを感じません。MayaでVEXに近い唯一の方法は、C++で独自シェーダやデフォーマを書くことくらいです。

VOPは、始めるのには素晴らしいもので、たとえHoudiniを熟知していても、ノードのドラッグアンドドロップで色々試すことができるので便利です。 VOPネットワークが肥大化すると、その内容を追うのがしんどくなり、条件分岐(if/for/while)は(H15から簡単になったものの)あまり使いやすいとは言い難いです。

VEXはすぐにタイプしてすぐに実行することができるのですが、もちろん、VEXの書き方を知っている必要があります。 VOPネットワークを右クリックしてVEX/VOP Optionsの'View Vex Code'を選択するとVEXコードが表示されるので学習するのに良いです。 ドキュメントを読んで、素晴らしい1~2個のWrangleノードが使われているhipファイルの中身を覗いてみるのも良いでしょう。

VEXとVOPを組み合わせることもできます。VOPネットワークを作成して、その中に'Inline Code' VOPを作成すれば、そこにVEXコードを記述することができます。 どちらの長所も使えるようになります!

HoudiniのPythonは面白いです。 一般的には、PythonはパイプラインまたはUIの制御に使用します。 ノードを作成/削除したい時、セントラルアセットデータベースからノードを読み込みたい時、面白い方法でテキストファイルまたは何かのデータを解析したい時には、通常はPythonを使用します。基本的にマッピング処理はPythonが非常に良くて、Pythonから色々な事を駆動させることができますが、場合によっては(PyMelと同様に)、HScriptにいくつかの処理を任せなければならないこともあります。また、HoudiniのPythonは、他のPythonと同じ問題を抱えていることを忘れないでください。Pythonは可読性と汎用性を目的に設計されており、パフォーマンスはよくありません。

MayaではPythonを使用していましたが、最近はPythonをあまり使っていません。 その主な理由は、MayaではUIの問題の回避や同じ作業の繰り返し、プロセスの自動化などはPythonを使用した方が良かったからです。 Houdiniでは、もともとプロシージャルで考えることが前提にあり、再利用の考えも普通にあることからスクリプトを書く機会があまりないです。

そのため、まず最初にVOP、次がHScript、そしてVEX、Pythonの順番で学習するのがいいと思います、

Point SOPでif文を使用する

Point if statement.gif

Houdiniファイル: File:point_sop_if.hipnc

廃止

もうこれは古いです。ここには見るものはありません。

HoudiniエクスプレッションはSOuPよりも簡潔で分かりやすいです:

if ($PT % $F == 0, 1 , 0)


この構文は'if( test_condition, value_if_true, value_if_false)'で、NukeのTCLのif文と同様です。

Point VOPでif文を使用する

Point VOP版は、若干分かりにくいですが、ジオメトリが多いほどパフォーマンスを発揮します。これは上記のシーンファイルに入れています。

Pointvop hou if.gif

左から右に読みます:

  1. ポイントIDをfloatに変換します。
  2. それをフレーム番号で割って余りを求めます。
  3. その余りが0かどうかチェックして、true/false値を取得します。
  4. これをTwo Way Switch VOPに渡します。trueなら1番目の値(1の定数)、falseなら2番目の値(0の定数)を返します。
  5. その値をポイントポジションのY値に設定します。


"ちょっと待って、'if' VOPはどこにあるの?"だって? 分岐制御系のVOP(If Block/For Loop/While Loop)はたしかに存在し、それらのVOPはブロックになっています。 入力の条件がTrueの間に、そのブロック内のノードが実行されます。これはこれで良いのですが、値を設定するだけの単純な処理の場合にはVOPネットワークは平坦な方がすっきりします。 Compare VOPとTwp Way Switch VOPも'if'ステートメントとして機能するのでブロックは不要で、少し速いです。

Point Wrangleでif文を使用する

Point Wrangle SOPを配置し、以下のコードを記述します:

if (@ptnum % @Frame == 0) {
 @P.y=1;
}


VEXコードの詳細はHoudiniVexページで説明しています。

ポイントの投射とパラメータのプロモート

Rays and promote param.gif

Houdiniシーン: File:point_sop_rays.hipnc

ここでもPoint SOP版とPoint VOP版のサンプルを用意しました。

このPoint VOPはプロモートさせたパラメータを使用しています。 VOPsはコンパイルされるので、VOPネットワークを修正する度にそのプラグインを再コンパイルする必要があります(Houdiniはあなたの代わりに自動的にこの処理を行ないます)。それが定数値でその数値をスライドさせた場合でも、HoudiniはVOPネットワークを再コンパイルしなければなりません。 比較的小規模なネットワークでジオメトリも軽量なら大して問題にならないのですが、場合によってはすぐに重くなってしまうことがあります。 上図のアニメーションgifでは、定数値を変更すると遅延が見受けられます。以下に、そのgifで起きていることを解説しています:

  1. 定数値をスライドさせただけで少し遅延があります。なんてこった。
  2. その定数の接続を解除します。
  3. アトリビュート入力上を中クリックして、'Promote Parameter'を選択します。すると、小さなノブが表示されます。
  4. そのノブを右クリックして(通常は少しズームインする必要があります。ほんと小さいヒット領域ですね!)、'Expose Input'を選択すると、そのノブのノードが表示されます。
  5. このノードに'Num rays'のような名前を付けます。
  6. 'u'を押して上位階層に移動してVOPネットワークを抜けます。
  7. VOPSOPに'Num rays'という新しいパラメータが追加されているのが確認できます。これをスライドさせてみると、パフォーマンスが非常に良くなっていることがわかります。


何が起きたのでしょうか? VOPネットワークが再コンパイルされましたが、今回の場合は、そのコードが'Num rays'を引数として受け取るようになっています。その引数はVOPSOPの外部参照となっているので、その引数を変更しても再コンパイルする必要がありません。 これによってパフォーマンスが非常に良くなったのです。必要なだけ引数を露出させることができ(むしろ、そうすべき!)、引数はfloat以外の型も露出させることができます。必要に応じてランプUIやドロップダウンメニュー、トグルを手軽に用意することができます。

ポイントを使ってエッジを動かす方法

シーン: ファイル:Move edges.hip

Move edges.gif

人によっては伝わらないかもしれませんが、SOuPと同じです。 Houdini(とSOuP)の困った事はエッジです。エッジを主体に考えられていないので、常にポイントを主体に考えなければなりません。 ないと困ることほどでもないのですが、今現在エッジに関する問題を抱えていたら、'クソだな... エッジにアクセスできたらいいのに...'って思うでしょう。 おそらくグループで代用して作業を続けることはできるのですが、それはあくまで回避策です。

ここの面白い事は、エクスプレッションを使えばグリッドのポリゴンを一列おきに削除できることです。 2で割った余りを利用します! グリッドの一列のポリゴン数が19個なら、フェースID(プリミティブIDとも呼び、ローカル変数は$PRです)を19で割った余りをさらに2で割ることで一列おきにポリゴンを取得することができます。 削除のエクスプレッションは以下のとおりです:

($PR%19)%2

グリッドの列の数に関係なく常に一列おきにポリゴンを削除させたいのであれば、グリッドのRowsパラメータをチャンネル参照として使用することです(削除するポリゴンが一列になるようにグリッドのColumnsパラメータをRowsパラメータと同じにしています)。

($PR%(ch("../grid1/rows")-1))%2


このサンプルと同じ事をVOPベースでも行ないました。 VOPベースの方がポイントが多いほど処理速度に大きな差が出ます。

ポイントに対するAttribute Transfer SOP

シーン: File:point_and_attrib_transfer.hipnc

Point attribtransfer.gif

Point SOPとPoint VOPのサンプルが入っています。 SOuPで作成したサンプルの方は、まったく正しくなかったのでそのサンプルを修正しました。

Houdiniは、Attribute Transfer SOPのソースとして、それが単一ポイントなのかポイント郡なのかそれともオブジェクトなのかどうかを区別しません。 このサンプルでは、ソースに一列のポイント群を設定したのですが、各ポイントにおける効果が重なって、その結果SOuPサンプルのようにカプセル状の減衰領域を表現することができました。

それ以外は何も特別なことをしていません。 これまでのサンプルと大きく違うのは、グリッドとラインに対して'weight'という名前のアトリビュートを作成したことです。 グリッドの'weight'アトリビュート値を0、ラインの'weight'アトリビュート値を1に設定しました。 Attribute Transfer SOPの1番目の入力(ターゲット)にグリッド、2番目の入力(ソース)にラインを接続し、AttributesタブのPointsパラメータに 'weight'を設定します。ConditionsタブのDistance Thresholdパラメータを0に設定し、Blend Widthパラメータを良い感じの減衰になるまで広げます。

なぜソースとターゲットの両方に'weight'を設定したのでしょうか? 両方にそれを設定しなかった場合、ブレンドは行われません。グリッドのAttribute Create SOPをバイパスにしてみると、そのブレンド領域が消失し、減衰距離の範囲内を一定量でまるごと転送されてしまいます。

通例では、VOPノードの方がPoint SOPなんかよりも非常に高速に処理されます。

ポイントに対してSolver SOPによるタイムラグを表現したAttribute Transfer SOP

Solver sop.gif

Houdini版: File:attrib_transfer_lag.hip

Solver SOPは、累積的な効果を表現することができます。

ほとんどのHoudiniノードは、Excelのスプレッドシートでいう式と同様に'今の状態に対して'動作します。 なにかしらの値または式(またはSOP)を変更すれば、下流のネットワークはできるだけ速くその変更を更新しようとします。 通常のSOPとエクセルスプレッドシートはどちらも、更新前よりもっと古い値または前フレームでの値について何も知りません。

Solver SOPは、サブネットのようにその中に入ることができます。 そこには興味深い2種類のノードがあります。Input_系のノードはSolver SOPの入力コネクタに相当し、紫色の'Prev_Frame'ノードは前フレームの結果を取得することができます。

Solver SOPを使用することで、これまで難しかった処理を行なうことができます。 前フレームの結果を受け取り数値を加算したり、前フレームと現行フレームを比較したりなど色々なことが可能になります。

このサンプルでは、Solver SOPの中でAttribute Transfer SOPを使ってカラーアトリビュートを転送しています。 これは前フレームのカラーに別のカラーを加算させることでカラーの軌跡を表現しています。 その軌跡をフェードアウトさせたいので、そのAttribute Transfer SOPにPoint VOPを接続して、その軌跡のカラーとオリジナルのグリッドカラーをブレンドさせています。ブレンドのウェイトを変更することで、その軌跡の長さが変わります。

Solver SOP外のそれより下流に2個のノードを接続して、軌跡を少し簡単に視覚化できるようにしています。 そのPoint VOP SOPは、軌跡のカラーからポイントのY座標を決定しています。 Color SOPは、境界ボックスに基づいてポイントカラーを設定しています。

初めてだとSolver SOPが少しとっつきにくいかもしれませんが、いくつか注意事項があります。 実はSolver SOPは単純なダイナミクスシミュレーション(dopnet。これはwikiの他のページで説明しています)で実装されているので、1フレームあたり何ステップで処理するのか、開始フレームは何フレーム目からなのか指定する必要があり、シミュレーションしてキャッシュ化しないと、タイムライン上でフレームを前後にスクラブするといったような即時的な操作ができません。また、ビューポートがSolver SOP外でないと正しくその軌跡を確認するができないので、Solver SOPの中に入る前にビューポートをピン留めすると良いでしょう。

もはやPoint SOPを学習したので、Point SOPの使用をやめます

Houdiniバージョン12以前のチュートリアルのほとんどがPoint SOPに大きく依存しており、Point SOPを使ってポイントポジション、法線、カラー、Velocityなどを設定しています。 しかしPoint SOPにはいくつか問題があります:

  • 膨大な数のポイントの処理が弱いです。
  • 他のノードに使用できないローカル変数がたくさんあるので覚え辛いです。
  • xyz/rgbの各成分に対してエクスプレッションを入力しなければならないのが辛いです。


昔はだいたいどのような場面でもみんなPoint SOPを使っていたのですが、今はPoint VOP、Point Wrangle、Attribute Create SOP、Attribute Randomize SOPを使用した方が良いです。これらのSOPすべてがVEXを利用しているのでマルチスレッドであり、Point SOPとローカル変数+グローバル変数+標準変数のHScriptを使用するよりもPointアトリビュートを一貫して使用した方が非常にスマートで、Pointアトリビュートはシェーダ、パーティクル、ダイナミクスのアトリビュートに直接マッピングできるというメリットがあります。

SideFXは、すべてをVEXに置き換えようとしていました(H15では、HScriptとグループフィールドに@var構文を使用できるようになったのでVEXは非常に素晴らしい橋渡しツールとなりました)。しかし、古くからの多くのセットアップがまだPoint SOPに依存しているのでPoint SOPを廃止することができませんでした。 H16以降では、その古いPoint SOPは、VEXを利用した非常に強力なWrangle SOPと新しいPoint SOPに置き換わりました。 新しいPoint SOPはごちゃごちゃして非常に使いにくいので避けましょう。

肝に銘じよ: "親友(とSideFX)は、Point SOPを使うなと言っている。"

オブジェクトをソースに使ったAttribute Transfer SOP

Houdiniシーン: File:point_and_objects.hip

Point and object scale.gif

再度言いますが、Houdiniは、Attribute Transfer SOPのソースがポイントなのかラインなのかオブジェクトなのか気にしません。 オブジェクトをソースに指定すると、そのオブジェクトを構成しているポイントだけを見てアトリビュートを転送します。 非常に簡単ですね。覚えておくべき唯一の事は、期待した通りの結果を得るにはソースジオメトリ内に十分な数のポイントがなければならないことです。 それらのポイントが疎らだと、塊や乱れが発生してしまいます。

Group SOPとDelete SOP

Houdiniシーン: File:group_delete_hou.hipnc

Group delete hou.gif

Houdiniで言うグループとは、コンポーネント選択に名前を付けたものです。 Mayaで一番近いのは、頂点やフェースの選択セットです。 ほとんどのHoudiniノードには、グループ選択が用意されています。つまり、それらのコンポーネントだけに影響を与えることができます。

Mayaの選択セットとは異なり、色々な方法でグループを定義することができます。 Mayaと同様のユーザー選択(もううんざり)、パターン(N番目のポイント毎)、エクスプレッション(例えば、Y値 > 4のポイントを全部選択)など色々な方法があります。 このサンプルでは、ティーポットとの交差によってグループを定義しています。 ティーポットにキーフレームを打つことで、そのグループの内容をその場ですぐに更新することができます。 それをDelete SOPのGroupパラメータに設定することで、アニメーションさせたティーポットの形状の内側のポイントを削除しています。

このサンプルでは、2個のTransform SOPも使用しています。 Transform SOPは基本的にはクラスタ(ジオメトリレベルのトランスフォームハンドル)です。 Transform SOPにもGroupパラメータが用意されているので、ジオメトリの一部だけを移動させたいのであれば、まず最初にGroupパラメータを設定すれば、そのグループをトランスフォームさせることができます。

累積的にグループ化して削除する方法

Houdiniシーン: File:group_delete_hou_accum.hipnc

Group delete accum2.gif

時間の経過と共にその軌跡から削除を継続させるには、Solver SOPの中にGroup SOPを配置します。以下がその手順です:

  1. Solver SOPを作成します。
  2. グリッドを1番目の入力に接続します。
  3. ティーポットを2番目の入力に接続します。
  4. Group SOPをCtrl-Xで切り取り、Solver SOPの中に入ってCtrl-Vでペーストします。
  5. 'Prev_Frame'をそのGroup SOPの1番目の入力に接続します。
  6. 'Input_2'をそのGroup SOPの2番目の入力に接続します。
  7. そのGroup SOPのMerge Operationパラメータを'Union with Existing'に設定します。
  8. 'u'キーで上位階層に移動します。
  9. そのSolver SOPの下にDelete SOPを接続します。
  10. そのDelete SOPにディスプレイフラグを設定して再生します。


では、何が起きたでしょうか? 以前のサンプルと比べて、Solver SOPが前フレームにアクセスできるようになりました。 Solver SOPの中にGroup SOPを配置したことで、前フレームに現行フレームのグループ情報が追加されるようになったので、グループの軌跡が構築されました。 注意してほしいのは、Group SOPのデフォルトの挙動は以前のグループの内容を常に置換する設定になっています。 Group SOPのMerge Operationパラメータを'Union with Existing'に設定することで、グループの内容が置換ではなく加算になります。

Solver SOPは最初は少しとっつきにくいので、そのための長いチュートリアルを別ページに書きました: The_solver_sop

@構文を使ったエクスプレッションによるグループ

ポイントを選択することでグループを作成することができますが、アトリビュートを使ったエクスプレッションでグループを作成することもできます。 例えば、Y座標が0未満のすべてのポイントをグループ化したいとします。その場合は以下のエクスプレッションを使うことでグループ化することができます。

@P.y<0


この強みは、Group SOPをまったく作成する必要がないということです。 Groupフィールドを持つどのSOPにでも、通常ならグループ名をタイプしなければならないところを、このようなエクスプレッションで指定すると、グループとして動作します。

ただし、エクスプレッションの中にスペースを入れないでください! Groupフィールドは'group1 group2 group3'のように複数のグループを指定できるようになっているので、万が一以下のようにエクスプレッションを入力してしまうと、

@P.y < 0


Houdiniは、それを'@P.y'のグループ、'<'のグループ、'0'のグループというように3個のグループとして解釈してしまいます。 もちろんこれだと動作しません。どうしてもスペースを使いたいのであれば(例えば、複数のポイントIDに対してマッチングを行ないたい場合)、ダブルクォテーションで閉じてください。

これは、Blast SOPやDelete SOPでよく使用します。 例えば、カラーの赤成分が1( @Cd.x==1 )のポイントをすべて削除したり、IDが5、10、23のどれか( @id="5 10 23" )のポイントをすべて削除したい時です。 これに関するヘルプは、以下のドキュメントに記述されています:

https://www.sidefx.com/ja/docs/houdini16.5/model/groups

VEXによるアトリビュートエクスプレッションのグループ

前のサンプルではアトリビュートをグループで使用しました。 ここでは別の方法を紹介し、グループエクスプレッションのアトリビュートを使用します。 例えば、(VEX)Attribute Wrangle SOPを使用した時、'mygroup'グループに属さないポイントだけに対してコードを走らせることができます:

if (!i@group_mygroup) {
    // 何かの処理
}

つまり、'@group_'接頭辞を使用することでグループを参照することができます。 この構文については難しく考えなくて大丈夫です。 文字通りに@の後に'group'とアンダースコアをタイプして、グループ名をスペースなしでタイプします。

ここでは、他のテクニックを織り交ぜて条件式の中でそのアトリビュートをコールしています。 そのアトリビュートを整数に型変換('@'の前の'i'がそれです)し、その結果を否定しています。

Wrangleノードと同様の方法でグループを作成することもできます。 Wrangleノード内で以下のようにアトリビュートを作成し、グループ化したいポイントのそのアトリビュート値を1に設定すると、Houdiniが自動でアトリビュートを作成します。例:

if (@ptnum>chi('threshold')) {
 i@group_mygroup=1;
}


以下の面白いアニメーションgifを見てください:

Group wrangle.gif

私は、これを1年前に学習しては忘れ、再度学習し忘れを繰り返しました。 あなたも最初にこのページを読んではまた忘れて数カ月後にまた学習することでしょう。 :)

これについては、ヘルプで(たくさんの手軽なVEX Tipsと共に)非常に簡潔に触れられています:

https://www.sidefx.com/ja/docs/houdini16.5/vex/snippets#traverse

インスタンス

Houdiniシーン: File:instancer.hip

Instance sm.gif

(Edit, update 30 sep 2015)

今だとこのシーンと説明はただ単に必要以上に複雑過ぎるものとなってしまいました。 昔のバージョンのHoudiniでは、例えば、メモリ消費量が少なくてレンダリングが高速だけど編集に制約があるのがインスタンス、フルに編集ができるけどメモリ使用量とCPU負荷が大きいのがCopy SOPというように、別々に扱われていました。

今では、Copy系SOPが両方のメリットを実現できています。 このサイト内の他のページで詳細を説明していますが、そのトリックは、例えばCopy Stamp SOPのStampタブにある'Pack Geometry Before Copying'トグルを有効にすることです。これによってCopy系SOPが高速なインスタンスモードに変わります。 出力されるジオメトリは、コピーされたジオメトリをパックした単一ポイントです。そのパック自体は編集不可です。このようにすることで、非常に膨大な数のジオメトリを高速且つ簡単にセットアップすることができます。

どこかのタイミングでこのサンプルを整理するつもりですが、今のところはそのままにしています。

(end edit)

Houdiniのインスタンスは非常にシンプルです。Instanceオブジェクトは、パラメータで指定したジオメトリを受け取り、そのInstanceオブジェクトの中にあるポイントに対してそのジオメトリをインスタンス化します。作成したばかりのInstanceオブジェクトには1個のポイントだけが含まれているので、そのポイントを削除して必要な数のポイントを作成したり、Object Merge SOPを使って他のどこかのポイントを取り込む必要があります。

他に設定すべき事は、Point Instancingパラメータ(デフォルトはoff)をFast point instancingまたはFull point instancingに設定することです。 たいていの場合はFast point instancingで十分ですが、インスタンス毎にシェーディングプロパティをフル制御したい場合にはFull point instancingに設定します。

待って!まだあります!

Instanceオブジェクト(Copy系SOPも)は、ポイントのアトリビュートを探します。 これらのアトリビュートによって、基本的な移動/回転/スケールを制御したり、さらにマテリアルの割り当てをすることができます。 アトリビュートの一覧はドキュメントにあります:

https://www.sidefx.com/ja/docs/houdini16.5/copy/instanceattrs

このサンプルでは、Attrib VOP(H14ではPoint VOPと呼びます)を使って'pscale'と'orient'のアトリビュートを作成し、time, sin, pointidを組み合わせてそれらのアトリビュートを駆動させました。一部の重要なパラメータはアニメーション制御したいのでプロモートさせています。 C4DでいうMographスタイルの楽しい時間です。完成するとこんな感じです。trippy animated gifs

インスタンスの向きを変える

ここでは、私の友達からの質問の回答を載せています:

Houdiniシーン: File:instance_onto_normals.hip

Instance with normal.gif

ジオメトリ上にインスタンスを配置し、インスタンスの向きをそのジオメトリの法線に合わせることができるのか質問を受けました。 答えはYesです。

このシーンでは、グリッドを作成し、それをMountain SOPに接続して、Offsetパラメータをアニメーションさせてグリッドを揺らせました。 次に、Facet SOPを接続して、'Post-Compute Normals'トグルを有効にしました。これによって、各ポイントに法線が生成され、フレーム毎に法線が更新されます。

次に、Instanceオブジェクトの中に入って、Object Merge SOPを使ってそのジオメトリを取り込み、さらにインスタンス化したいジオメトリを+Z軸を上向きにして用意し、Instanceオブジェクトがそのジオメトリを参照するようにしました。

ご褒美として、Facet SOPの後にAttrib VOPを接続して、ポイントIDベースで少しランダムさを追加したよ。 なぜかというと、時間の経過と共に法線が微震しなかったから。

サンディ君、楽しんでね!

エフェクターを使ったインスタンス

Effector.gif

シーンをダウンロード: File:effector.hipnc

C4Dが得意としている事で、明らかにXSIのICEなのですが、Houdiniでも挑戦してみました。

このサンプルでは、Nullとグリッドを作成しました。 HoudiniのNullは手軽に中心点を配置することができるので、VOPネットワークに組み込みやすいです。 私は、そのNullのスケールを後でそのNullポイントから参照できるように、そのスケールをチャンネル参照したアトリビュートをそのポイントに追加しました。

Point VOPでは、そのNullポイントを2番目の入力に接続し、各グリッドポイントからNullポイントまでの距離を取得しました。 この距離を使って'scale'アトリビュートを駆動させて(これをNullのスケールにもリンクしました)、Instanceオブジェクトがそのアトリビュートを直接受け取るようにしています。

エフェクターウェーブを使ったインスタンス

Cube waves.gif

シーンをダウンロード: File:box_waves.hipnc

このフォーラムの投稿に対する回答です。

前のサンプルと同様で、各ポイントからNullまでの距離を計算した後で異なる処理をしているだけです。 このサンプルでは、その距離をランプパラメータにマッピングして、そのランプを減衰したノコギリパターンにセットアップしました。

ランプパラメータは、数学的に考えなくても色々な動きを表現できる便利な手法です。 パラメータに値の範囲が0から1のアトリビュートを追加して、それをランプパラメータに渡し、便利なUIからそのランプを編集することで、簡単にあなたの求めている事を表現することができます。

スケール、回転、カラーのエフェクター

Effector img vex vops.gif

シーンをダウンロード: File:effectors_vex_and_vops.hip

これをやってみると、夢中になっちゃいますよ。 誰かがHoudiniでC4Dスタイルのランダムエフェクターの作成方法を質問していました。 前のサンプルだとランダム化とアニメーションを追加する方法を示していなかったので、ここにその方法を載せています。

完璧に有効な方法を2通り載せています。 1つがVOPsとAttribute Transfer SOPを使ったほぼノードベースのサンプル、もう1つがVEX Wrangleを使ったサンプルです。

VOP版のサンプルは以下のとおりです:

  1. 格子状ポイント、そしてエフェクトの駆動に使用する'エフェクター'ポイントを作成します。
  2. 格子状ポイントのweightアトリビュートを0に設定し、エフェクターポイントのweightアトリビュートを1に設定します。
  3. Attribute Transfer SOPのブレンド減衰のパラメータを使って、エフェクターポイントから格子状ポイントにweightを転送します。
  4. Point VOPのネットワークの中で、ポイント毎にランダムカラー(またはスケールや回転)をセットアップし、それをweight値を使ってブレンドします。
  5. Copy SOPを使って、各ポイントに小さな立方体をコピーします。


VEX版のサンプルも同様ですが、ごちゃごちゃしたネットワークをコードで済ませることができます:

  1. 格子状ポイント、そしてエフェクトの駆動に使用する'エフェクター'ポイントを作成します。
  2. Wrangleで以下の処理をします...
  3. 各ポイントからエフェクターポイントまでの距離を測定します。
  4. その距離を欲しい減衰距離に合わせて、それをランプに渡して減衰形状を調整し、それを0から1の範囲の値に合わせます。
  5. ポイント毎にランダムなpscale/Cd/orientアトリビュートを作成します。
  6. 計算したウェイト値を使ってブレンドします。
  7. Copy SOPを使って、各ポイントに小さな立方体をコピーします。


他のサンプルと同様に、これもCopy SOPが認識する特定のPointアトリビュートのメリットを生かして、それらのアトリビュートを使ってコピージオメトリを修正しています。このサンプルでは、カラーに@Cd、均一スケールに@pscale、回転に@orientを使用しています。 アトリビュートの完全リスト: https://www.sidefx.com/ja/docs/houdini16.5/copy/instanceattrs

VOPの方がおそらく表面的には単純であっても、なんだかんだVEXを使用してしまいます... なぜかって? 一箇所で重要な処理をすべて行なえてしまうのは非常に便利であり、VEXを書けば書くほど勉強にもなります。 :)

Instance SOPを使ったインスタンス

Instance img randcolour.jpg

シーンをダウンロード: File:instance_sop.hip

Copy SOPに異なる形状を駆動させる方法は、すべての形状をSwitch SOPに接続して、それをCopy SOPの左側の入力に接続して、そのSwitch SOPのスイッチ番号をスタンプすることです。これで動作しますが、そのスタンプの複雑さがまったく好きではないし、コピーの数が増えるほどスタンプのパフォーマンスが良くありません。

最近のodforceのスレッドでInstance SOPのことが書かれていました。 このSOPは、@instancepathアトリビュートを検索して、そのパスのジオメトリを使用します。 このアトリビュートには、ディスク上の.bgeoファイルを指定したり、op:構文を使ってネットワーク内の何かを指定することができます。 つまり、そのSOPこそが私が使いたかったノードであり、そのノードが上手く動作しました。

このサンプルシーンのように、ポイントに色を付けてPrincipledシェーダのようなパックを認識する新しいシェーダを使用すれば、レンダリングでそれが反映されます。

以下のWrangleコードは、instancepathアトリビュートにランダムなパスを設定しています:

string geo[] = {
'op:/obj/geo1/a',
'op:/obj/geo1/b',
'op:/obj/geo1/c',
};

int inst = int(rand(@ptnum,ch('seed'))*len(geo));
s@instancepath= geo[inst];


面白いことに、Instance SOPの中に入ると、そこには.... Copy SOP、Switch SOP、スタンプエクスプレッションが使用されています。 私の'思いついた'方法とまったく同じ処理が行なわれています!

つまり結局の所、実際には非常に高速になるってわけではないです。 Macbook Proで100万ポイントをテストしたところ、スタンプの処理で約30秒、レンダリングで最初のピクセルが表示されるまでのIFDの出力に30秒かかりました。 まぁ、いいでしょう。スタンプを自分でセットアップするよりは複雑じゃなくて良いです。 でも、もっと効率的な方法があるにちがいない....

Attribute from Texture

Attrib from tex.gif

シーンをダウンロード: File:texture_attributes.hipnc

セットアップは以下のとおり:

  1. ポリゴングリッドを用意します。
  2. Point VOPを使って、ノイズでポイントを動かします。
  3. UV Project SOPを使って、uv座標を作成します。
  4. もう1つPoint VOPを追加し、ここで'Texture' VOPでテクスチャを読み込み、そのカラーを@Cdに接続し、その輝度を@pscaleとして出力します。
  5. Delete SOPを使って、ポイントを残してポリゴンを削除します。
  6. Copy Stamp SOPを使って、各ポイントに円盤をコピーします。ポイントにCdとpscaleのアトリビュートが追加されているので、その円盤にもそのカラーとサイズが反映されます。


このセットアップは、ポイントが移動してもテクスチャは静的なままになっています。 テクスチャをポイントに引っ付けたいのであれば、UV Project SOPを最初のPoint VOPの前に接続してください。

私は初めてHoudiniを学習した時に同様のことを挑戦しました。 その時は、シェーダを使ってテクスチャを適用していたので、SOPs/VOPsでそのカラーにアクセスできなくて困りました。 ビューポートにはテクスチャが表示されているのに、なぜそれが動作しなかったんだろう?

その理由は、マテリアルはレンダリング時にそのプロパティを適用するのであって、Houdini SOPコンテキスト内で適用されているわけではないからです。 ただ、場合によってはマテリアルのテクスチャを適用できる時があり、なぜかレンダリングでも動作したのですが、SOPsでUVを制御することができません。 また、それが適用できた場合にはレンダラーは暗黙的にuvを作成します。

テクスチャのビューポート表示は、追加レイヤーです。つまり、一部のマテリアルのGLSL機能です。 これは便利な表示機能ですが、あくまで表示用です。テクスチャから何かを取得したい場合には、上記のサンプルののように明示的にそれを読み込む必要があります。

コピーとパックプリミティブを使った目玉

Eyeballs.gif

シーンをダウンロード: File:eyeball.hipnc

面白いばかげたことをしてみました。 まず最初に1個の目玉を作成し、それをパックプリミティブ(Houdiniは、それをフルポリゴンメッシュではなく単一ポイントとして扱うようになります)にしました。 次に、以下の処理をしたごちゃごちゃしたPoint VOPネットワークを作成しました:

  • 各目玉が原点から背く(法線があれば、その法線方向の)トランスフォームマトリックスを作成します。
  • 目玉をギョロギョロ動かしたいので、ノイズ駆動によるトランスフォームマトリックスを作成します。
  • すべての目玉が+Z軸を向くトランスフォームマトリックスを作成します。
  • 制御しやすくするために、すべてのマトリックスをクォータニオンに変換します。
  • すべての目玉が原点を基準にランダムにギョロギョロするように法線向きとランダム向きを追加します。
  • timeによって0から1のランダムパルスを生成するノイズカーブを作成します。
  • それを使うことで、法線軸のランダム回転とZ軸回転を簡単にブレンドさせることができます。
  • 各目玉のZ座標で駆動させたいくつかFit Rangeノードを使用することで、同時にすべての目玉を切り替えるのではなく、後方から前方へ波を打つようにします。
  • その結果の回転を受け取り、それをマトリックスに変換して、それを'transform' Intrinsicアトリビュートに格納します。


matrix->quaternion->matrix->intrinsic-transformのテクニックの方が賢明ですが、そうする必要がなかったことを後で気づきました。 Copy Stamp SOPの前にポイント上にorientアトリビュートとupアトリビュートを入れておけば、面倒なことしなくても同じ結果を得ることができました。 まぁ、いいでしょう。パックプリミティブの制御方法を知ることができたんだし。

コピーとインスタンスに対する明示的な回転/向きの制御

このページのいくつかのサンプルで、これについて少し触れていますが、よくある質問は使用頻度が多いこともあり、ここに書いておいた方が役に立つと思いました。

インスタンスとコピーはどちらも入力ポイントから、ある優先度に基づいて特定のアトリビュートを検索します。その優先度は以下のページにリストされています:

https://www.sidefx.com/ja/docs/houdini16.5/copy/instanceattrs

優先度が高いアトリビュートは@orientで、そのアトリビュートがデフォルトで使用されるようになっています。

@orientはクォータニオン(4個の値で構成されたベクトル)である必要があります。 クォータニオンはオイラーよりも安定しています(ジンバルロックやオイラーの反転の問題がありません)。 クォータニオンは手動で制御するのには扱いにくいのですが、幸いなことに、そんなことをする必要はありません。

トランスフォームマトリックスとクォータニオンは、非常に簡単に双方に変換することができます。 トランスフォームマトリックスはクォータニオンよりも少し編集しやすいです(とはいえ、非常に編集しやすいというわけでもないのですが)。

軸による回転は人間にはわかりやすくて直感的なので、それから始めて、それをマトリックスに変換するVEXコールを使用し、次にそれをクォータニオンに変換するのが良いでしょう。思った以上に簡単です。

例えば、平坦な地面があって、そこにたくさんの木のコピーをY軸によるランダムな回転を付けてばら撒きたいとします。 以下は、Copy Stamp SOPの前に配置したPoint Wrangle SOPのコードです:

float angle = rand(@ptnum) * 360;
angle = radians(angle);
vector axis = {0,1,0};
matrix3 m = ident();

rotate(m, angle, axis);

@orient = quaternion(m);


このコードを1行1行解説します:

 float angle = rand(@ptnum) * 360;

'angle'という変数を定義し、ポイントIDをシード値として乱数を取得しました。 rand()は0以上1未満の値を返すので、その値に360を乗算して全方位の範囲を取得します。

 angle = radians(angle);

後で'rotate'関数を使用したいので、angleの単位をラディアンに変換します。

 vector axis = {0,1,0};

回転軸を定義します。ここではY軸を定義しました。ジオメトリに対して処理をする時は、代わりに法線をよく使用します(例えば、vector axis = @N; )。

 matrix3 m = ident();

'm'マトリックス変数を作成します。ident()は単一行列を返します。要するに、回転量と移動量が0でスケールが1のマトリックスを返します。 'matrix3'という型はトランスフォームマトリックスであり、'matrix'という型は4x4行列で、拡張された行列はシアーやパース変換で扱います。 ここでは不要です。

 rotate(m, angle, axis);

この関数は、'axis'ベクトルを基準に'angle'ラディアンだけ回転させたマトリックスを'm'に適用します。 つまり、'm'が回転マトリックスになりました。rotateの結果を変数に割り当てる必要はありません。その回転が直接mに適用されます。

 @orient = quaternion(m);

'orient'アトリビュートを作成し、'm'マトリックスをクォータニオンに変換して、それをorientアトリビュートに割り当てます。

単純ですよね?

OK。いくつか注意事項があります。 まず第一に、Wrangleでクォータニオンアトリビュートを定義するには、Wrangleにこのアトリビュートが4成分のベクトルであることを伝えるために以下のように'p'接頭辞を付ける必要があります:

p@myvalue

'p'は'pleaseの略でq'じゃなくpだよと言い聞かせてます。そう思うようにしてます。

ただし、@orientは@P, @N, @Cdと同様に特別なアトリビュートで、Wrangleはそれらのアトリビュートが何なのかどう処理すべきなのかを知っているので、型宣言を省略することができます。

また、私の場合は普段は型をきちんと宣言せず、型宣言と変数の割り当てを1行に収めたい人です。さらにスライダで何もかも駆動できるようにch関数をよく利用します。私はおそらく超怠け者だから、'type foo = blah'みたいにタイプするのが面倒くさくて何にでも@attrsを使います。例:

@angle = radians( rand(@ptnum)* ch('range'));
v@axis = chv('axis');
matrix3 m = ident();
rotate(m, @angle, @axis);
@orient = quaternion(m);


これをプリセットに保存するといいかも。私はまだプリセットとして保存したことないですけどね。 というのも、毎回タイプしてVEXを覚えるようにしてるから、それでいいのだ。

For-Eachノードを使って凸凹や街の区画を作成する方法

Greeble.gif

シーンをダウンロード: File:greeble.hipnc

H15以前のFor-Eachノードは、Houdiniドキュメントでさえ'まぁ... Solver SOP使った方が'って書いてるくらい少し使いづらいです。 今は手軽に扱えるようになったのですが、それを理解するのに時間がかかります。

Solver SOPと同様に、For-Eachノードはサブネットで、色々な方法でループさせます。以下のようにしてループさせることができます:

  • グループを作成します。
  • ポイント/プリミティブ単位でアトリビュートを作成し、指定した閾値で値を可変させます。
  • Fora-EachノードのForパラメータをEach Primitive/Pointに設定します。


'For-Each'ノードの中に'Each'ノードがあります。 これは、グループ、アトリビュート、プリミティブ/ポイントのインデックスのパラメータを持ったプレースホルダーです。 ループされる度ににこのノードが更新されるので、他のノードでこれらのパラメータをチャンネル参照することで、そのループ毎に異なる処理を行なうことができるようになります。

このサンプルでは、街の区画として分割する平面を作成しました。 それを別々のプリミティブに分割し、小さすぎるプリミティブを削除して、プリミティブ毎にループするように設定した'For-Each'ノードにそれを接続しました。

その中で、各ポリゴンをランダムにビルサイズのグリッドに分割してから、それらをランダムに押し出してブロックにしました。 'For-Each'ノードの前にSort SOPを接続し、それをtimeでランダムに駆動させることで、フレーム毎にビルのレイアウトを変えることができます。

このサンプルは、前のセットアップよりも多くのHScriptエクスプレッションを使っています。For-Eachループでは、このスタイルで行なうことが多いです。

For-Eachループはマルチスレッドで動作しないので、パフォーマンスを重要視する場合には避けるべきです。 頭の中にこのようなテクニックがあることを覚えておいてください。

H15からの新しいループは、まだ何か変ですが、サブネットに入る必要がなくなったのが良いし、何かの単位値でループをテストしやすくなったのも良いです。 この新しいループのサンプルをいつか作成しようと思いますが、今のところ、マスタークラスを見るのがそのループの挙動を知る上で一番良いと思います:

https://vimeo.com/142534639 - H15 Masterclass | Loops with Jeff Lait

エッジ沿いにポイントをスライドさせる方法

Houdiniシーン: File:slide_points_along_edges.hipnc

Slide points along edges sm.gif

前述でSolver SOPが良いと言いましたが、それを使わずに何かを移動させるようなことができると気持ちよくないですか。 このサンプルは全体がプロシージャルなので、前フレームにもキャッシュにも依存していません。素晴らしい。

ここでの目的は、何かのジオメトリを与えると、ポイントを蟻のようにエッジ沿いに動かすことです。 ではやってみましょう。

  1. Grid SOPを作成し、そのConnectivityパラメータを'Rows'に設定してワイヤーにします(フェースのないポリゴンエッジは、それがポリゴンで構成された1次カーブとして考慮されます)。
  2. Resample SOPを使ってポイント数を増やします。
  3. Mountain SOPを使ってワイヤーを揺らします(Mountain SOPはグリッドのワイヤーが若干ずれていないと何の効果も起きないので、ノード間にPoint Jitter SOPを挿入しました)。
  4. Point VOPを作成して、その中にSnippet VOPを配置して、(ptnumとnumvtxを接続して)各ワイヤーの長さに基づいたu座標アトリビュートを作成します: u = vertexprimindex(0, ptnum) / float(numvtx);
  5. ワイヤーの見た目を良くしたいので、そのuアトリビュートに基づいたランプを使ってワイヤーに色を付けます。
  6. ワイヤー上にポイントをばら撒きます。それらのポイントにはワイヤーのuアトリビュートとID(sourceprimと呼びます)が継承されます。
  7. すべてのポイントに対して-1から1までの'delta'という新しいランダムアトリビュートを追加します。
  8. Point VOPを使って、ワイヤーに沿ってポイントをスライドさせます。


そのPoint VOPの内容をまとめました:

  1. timeを受け取りdeltaで乗算します。これによって、delta値が1ならそのポイントがフルスピードで移動します。-1なら逆方向にフルスピードで移動します。中間値なら遅く移動します。
  2. uを受け取り、そこにtimeを加算して1で割った余りを求めます。これによってu座標が滑らかに変化しますが、0または1(ワイヤーの始点または終点)に到達すると、ワイヤーの一方の端点に即座にワープするようになります。
  3. sourceprim、更新したu座標、オリジナルのワイヤージオメトリを'Primitive Attribute' VOPに接続します。このノードは、指定したプリミティブの指定したUV座標における選択したアトリビュートの値を返します。ここではアトリビュートにPを指定したので、そのワイヤーのu座標の位置が返されます。
  4. そのPの位置をポイントポジションとして設定します。


サンプルシーンでは、Instanceオブジェクトの中にこのセットアップを取り込み、Instance Objectパラメータにボックスを、Point InstancingパラメータをFast point instancingに設定しました。また、このセットアップにさらにSwitch SOPを使っていくつかの他のジオメトリを接続して、色々な形状を表示できるようにしました。

この投稿に基づいて他のバリエーションのサンプルを作成しました。これは、1方向に複数パスに沿ってポイントを動かします。

Arrows curves.gif

シーンをダウンロード: File:arrows_on_paths.hipnc

この大きな違いは、

  • ランダムなdeltaアトリビュートの基準値が0ではなくて1なので、どのポイントも1方向に移動します。
  • 一番最後に、Trail SOPを使ってvを計算しています。このvはHoudiniのVelocityの標準アトリビュートです。Copy系SOPやインスタンスノードは、このアトリビュートを取得することができるので、コピーの移動方向が求まります。


他のバージョンのもあります:

Traffic.gif

シーンをダウンロード: File:paths_attrib_interpolate.hipnc

もっと素晴らしい方法を常に探し求めてエフェクトを表現するのが楽しいです。 Attribute Interpolate SOPを使用すれば、変形するジオメトリに対して散乱ポイントを引っ付けることができます。 Scatter SOPには、各ポイントの散乱先のプリミティブ番号やその位置でのUV情報をアトリビュートに格納するためのオプションがあります。 Attribute Interpolate SOPでそれらのアトリビュートを指定することで、ポイントをジオメトリ上のそれに該当する位置に引っ付けることができます。

ここでは、私は別の用途でそれを使用しました。 静的なUVと変形ジオメトリに対してではなく、静的なジオメトリと移動するUVに対して使用しました。 閉じたカーブ上に散乱させた各ポイントのUVが0から1の範囲でループするように設定することで、それらのポイントがカーブ上を移動するようになります。 いくつかのビル、単純な車の形状を追加すると、ぎりぎり通行できる車の群衆シミュレーションができました。楽しんでね!

立方体の行進

Cubesmarch.gif

シーンをダウンロード: File:cubes_marching_v02.hipnc

行進する立方体について説明します。[マーチングキューブ法]ではありません。このwikiの画像何に見えます?魔法使いをレンダリングしてるのかなぁ?

'カーブに沿って物体を動かす'お遊びはこれでおしまいだと願いますが、これは前々からやってみたかったエフェクトで(2001年のシーグラフでこのような立方体を使った短編映画を見てからやってみたかったです!)、最後にこれを紹介できて非常に満足しています。

この考え方は前のサンプルと同じで、カーブを用意して、そこにポイントをばら撒いて、uvを取得し、uアトリビュートをアニメーションさせて、そのuを補間することでポイントをそのカーブに沿わせて動かし、そのポイントにパックボックスをコピーします。

ボックスを回転させるために、ポイントVelocityを取得し、そのVelocityとY軸の外積を計算して、それらのベクトルに直交する回転軸を取得し、@vの長さを使って回転速度を決定します。さらに、ポイント毎にその回転速度を1/@pscaleで乗算して、大きなボックスは遅く、小さなボックスは速くなるようにしました。

2つ目のPoint Wrangle SOPでは、各パックボックスの境界を取得して、その境界の底面が地面に乗るように持ち上げました。 いくつかの理由で、1個のWrangleですべてを処理させることができませんでした。一回の処理では関連のある処理を設定して取得することができない状況があるのをいくつか知っています。干渉を回避するためには、特定の処理を意図的にVEXコールの後に実行させると良いです。 トランスフォームの設定と境界の取得は、まさにその状況です。

余談になりますが、パック境界は浮動小数点の配列として保存されています。 その配列内のどの値が高さオフセットだと知ることができたのでしょうか?実は知りませんでした。 単に0から始めて、ボックスがちょうど地面に乗るまで数値を上げました。幸運なことに、その値が最初の3つの数値だったのです。 :)

VOPsを使ったプリミティブ単位の回転

Prim rot vop.gifPrim rot vop network.gif

シーンをダウンロード: File:per_prim_rot_vop.hipnc

フォーラムにあった面白いチャレンジ。 ここでのテクニックは、ローレベルのインスタンスVOPを私達の要求に合わせて使用したことです。 'インスタンスのトランスフォームを作成する'ことがまさにそれで、通常のインスタンスアトリビュートを入力(orient,rot,scaleなど)としてマトリックスを作成します。マトリックスを作成してしまえば、ポイントをそのマトリックスで乗算することで、ポイントを正しくトランスフォームさせることができます。

この目的のゴールは、プリミティブのトランスフォームのピボット、向き/回転を変更することです。 ピボットは各プリミティブの中心なので、'Primitive Attribute' VOPを使用して現行ポイントが属するprimnumを照会し、そのプリミティブの暗黙的なUVの中心(つまり、0.5/0.5)のP位置を返します。

回転に関しては、法線を軸に回転させたいのであれば、'Primitive Normal' VOPを使えば法線を抽出することができます。しかし、ここでは法線と直交する軸(接線または従法線)を基準に回転させたいです。この軸を取得する方法は、各フェース上の2点を結んだベクトルを定義することです。'Primitive Attribute' VOPを2個使用して、uv(0,0)とuv(0,1)におけるポイントを照会し、その2個のポイントを減算することで、回転軸として使用可能なベクトルを取得することができます。

軸を定義したら、現行フレームで回転量を駆動させます。では、インスタンストランスフォームで必要となるクォータニオンの回転はどのようにすればよいでしょうか?私はここでズルをして誰かの答えを探しました。'rotate' VOPが肝です。私はそのVOPに必須ではないものの入力にmatrixコネクタがあることに驚きました。 ここでは、そのVOPのangleとaxisを接続し、その出力を'Matrix3 to Quaternion' VOPに接続し、最後に、これをMake Instance Transform VOPのorientコネクタに接続します。

MultiplyやAddなどの色々なVOPを追加してパラメータをプロモートして、ひらひらと揺れる神秘的な羽の効果の制御を楽しんでみてください。

ポリゴンの展開

Folding prims.gif

シーンをダウンロード: File:grow_polys_v06.hipnc

これはodforceで何度も見受けられるので、やってみる価値があると思いました。 同様のエフェクトを実現した動画がVIMEOの非常に詳しいチュートリアルにあります。 これは、その方法をカンニングせずに自分でできるのか興味がありました。

このセットアップでは、いくつかのジオメトリを取得し、各プリミティブの中心にポイントを生成し、それらのポイントを繋げて新しいワイヤーフレームを生成します。Find Shortest Path SOPを使用して、1点からそれ以外のすべてのポイントまでのパスを生成して、いくつか便利なアトリビュートを生成します。 @prevptはそのパスの前のポイントのIDで、@costは開始ポイントから現行ポイントまでにかかったステップ数です。 開始ポイント近くのポイントの@costの値は小さく、遠くのポイントほどその値が大きくなります。

それらのアトリビュートをオリジナルのプリミティブに転送すれば、展開のテクニックが使用できます。 @costをタイマーとして使用し、開始したプリミティブすべてを削除し、@costに基づいて時間と共にそれらのプリミティブを順々に削除を取り消していきます。

そのプリミティブを表示させたら、そのプリミティブの位置、@prevpt位置を検索し、それを使って共有エッジ上に回転軸、ピボットとして使用する中点を作成します。それを使えば、前のサンプルと同様の方法でプリミティブを回転させることができます。私は、rot matrix->quaternion->instance matrix->outの手順を踏まなくてもできると考えましたが、挑戦する度に、その回転が常にワールド空間のX/Y/Z軸に平行になりました。これは平面ではうまく動くのですが、他のジオメトリだとうまく動きません。

このhipファイルは、VEX Wrangleを使って重い処理をすべて行ないましたが、すべてをVEXに実装する前にPoint VOPを使って色々と試作とデバッグをしました。

私がテストしたいくつかのケースでは非常に上手く動作しましたが、トーラスだと原因がよくわかりませんがうまく動作しませんでした。 ジオメトリを変更した際には、'Connect Adjacent Pieces' SOPをいくつか調整する必要があり、主にジオメトリに対して開始ポイントからのパスを計算するのに十分な数のポイントになるようにします。

オブジェクトの折り畳み(トランスフォーマーのキューブエフェクト)

Pack fold img.gif

シーンをダウンロード: File:pack_fold.hipnc

これは、以前に挑戦した内容を作り直しました。基本的には前のサンプルと同じテクニックを使用しています。つまりソルバベースではないのでシミュレーション不要で、パックしたボロノイ分割を使用して、どの入力の形状でも動作するようにしています。ワークフローは以下のとおり:

  1. 形状を受け取ります。
  2. Use 'Points from Volume' SOPを使って、その形状内で均等に格子点を作成します。
  3. Voronoi Fracture SOPを使用します。
  4. Assemble SOPを使って、それらをパックプリミティブに変換します。
  5. それらのポイントだけを残して、'Connect Adjacent Pieces' SOPを使ってConnection TypeをAdjacent Pointに設定して、それらすべてのポイントを結んだエッジでできた格子細工を作成します。
  6. 'Find Shortest Path' SOPを使って、その格子細工の中心点までのパスを生成します。
  7. パス毎にu座標とポイントを作成し、そのポイントをパスに沿って中心方向へアニメーションさせます。ランプまたは距離を使ってポイント毎にランダムにタイミングを遅延させます。
  8. このアニメーションする新しいポイントをオリジナルのパックプリミティブのポイントに合わせて、アニメーションを転送します(思っていたよりも若干手間がかかりました)。


このモーションは思っていたほど滑らかではありませんが、始めるには良いでしょう。 これは、ソリッドベースで実際のエフェクトを作成する際に必要でした。

VOP(とVEX)を使ってエッジを作成する方法

Soot creatures.gif

シーンをダウンロード: File:soot_vex_vs_vops.hipnc

AfterEffectsプラグインのPlexusのような最先端モーショングラフィックスの試みとして始め、最終的に、千と千尋の神隠し(英語タイトルがSprited Away)に出てくるようなススワタリみたいになりました。

このサンプルでは、プリミティブの作成、ポイントクラウドのルックアップ、if/whileサブネットの操作が必要です。

このシーン内のVOPネットワークは、何層も入れ子になっているので少し理解しにくいです。 ここで覚えておいて欲しい事は、VOPネットワークは同時にすべてのポイントを並列で処理するので、それを踏まえて論理を組み立てる必要があります。 'if'サブネットがすべてを順序立てるのに最も近い方法のように思います。そのため:

  1. 現行ポイントが'soot'グループに属していれば継続、そうでないならスキップします(つまり、Trueならボディ部分を処理、Falseならポイントが他のポイントまでエッジを伸ばします)。
  2. Point Cloud Open VOPを使い(このサンプルでは、ディスク上に事前に保存されたポイントクラウドではなくて、現在の入力ジオメトリを指します)、Search Radius(検索半径)パラメータを非常に大きな値に、Number of points(返されるポイントの最大数)パラメータを8に設定します。
  3. そのVOPが見つけたポイント毎にwhileループを実行します:
    1. プリミティブを作成します。これはポイントの配列を受け取り、それらのポイント間にエッジを作成します。
    2. ボディとなるポイントを追加します。
    3. このループで見つかったポイントのIDを取得します(ポイントが見つからなければ-1を返します。つまり、プリミティブにポイントが追加されません)。
    4. 足となるポイントを追加します。
    5. 次のwhileループを実行し、見つかった次のポイントへループします。


このセットアップを見直してみると、これこそがまさにVOPがかさばってしまういい例だと思いました。 これまでVOPによるセットアップを構築してみて、私のようにVEXやWrangleに非常に抵抗があった人でも、コードだと以下のようにどれほど簡潔に記述できるのかわかると思います:

float maxdist = ch('maxdist');
int numlegs = chi('legs');
int pts[];
int prim;
int i;

if (@group_soot==1) {
  pts = nearpoints(0,@P,maxdist,numlegs);
  for (i=1; i<len(pts); i++) {
     prim = addprim(0,'polyline');
     addvertex(0,prim,@ptnum);
     addvertex(0,prim,pts[i]);
  }
}

まず最初に、このコードでは変数をいくつか用意し、ifブロック内でforループを記述しています。 このif文はポイントが'soot'グループに属しているかどうかをチェックしています。 そのグループに属していれば、nearpoints()を使って、'maxdist'距離内にある最近接の'numlegs'個のポイントを探します。 そして、見つかったそれらのポイントをループさせています。 ここでは、そのfor文が一般的な'for(i=0...'ではなく'for(i=1...'で始まっていることに注意してください。 これは、nearpoints()が現行ポイントを最近接ポイントの1つとして見つけるのが理由です。 その現行ポイントはまったく必要ではありません。幸いにもnearpoints()は検索されたポイントを距離順(一番近いポイントが最初)で格納するので、その1番目のポイントだけを無視すればよくて(VEXによる配列の保存はインデックスが0から始まるので、その1番目のポイントはpts[0]に相当します)、単純に2番目のポイント( 'pts[1]')へスキップしてください。

forループでは、とにかく空っぽのポリラインプリミティブを作成してから、そのプリミティブに2個の頂点を追加します。 その1個の頂点がボディのポイントである現行ポイント(@ptnum)、もう1個の頂点が足のポイントであるpts[i]です。

すべてのポイントがすべてのポイントに接続できるようにするために初期グループテストをスキップすれば、Plexusスタイルのモーショングラフィックスを生成することができます。すべてのポイントをピクピク動かし、境界ボックスモードで色を付ければ、VIMEOで見かけるモーショングラフィックスのプロの道に進むことができるでしょう:

Edge grow anim.gif

シーンをダウンロード: File:webbing_v01.hip

'Connect Adjacent Pieces' SOPを'Adjacent Points'モードにすると自動的にエッジが作成されるので、なんて便利なんだろぉと最近気づきました。 このSOPの中身を覗いてみると、このサンプルのWrangleノードのコードと似ていることがわかります。早く気づけばよかった。

ボロノイによるクラスタと粉砕

Voronoi.gif

シーンをダウンロード: File:voroni_cluster_v06.hipnc

このビデオを紹介したodforceの投稿を見て、実験してみることにしました。

Vladimir Lopatin氏の言っていることを代弁すると、通常ならボロノイ粉砕には散乱ポイントをソースに使用します。それだと、いかにもボロノイって感じの細胞状の見た目になってしまいます。もっと規則的な構造でポイントを与えれば、粉砕パターンを幅広く表現することができます。 さらに、クラスタオプションを使っていくつかのボロノイ破片を融合させれば、もっと面白いパターンを表現することができます。

このパターンは、おおまかに3つに分類することができます:

  • ジオメトリパターンは、入力ポイントの構造で決まります。例えば、格子点なら千鳥状に密接に配置されたレンガパターンです。
  • テクスチャパターンは、VOPテクスチャによってクラスタアトリビュートを出力して、それをボロノイクラスタと融合に利用します。
  • クラスタパターンは、'Cluster Points' SOPを使って部分的にランダムにポイントを照合して、それらのポイントをボロノイクラスタと融合に利用します。


設定を色々試して、その結果のパターンがどうなるのか楽しんでみてください。これらのサンプルのほとんどをアニメーションさせており、どれもあれこれ変化するようにフレーム毎にランダム化させています。

張力と皺

Tension wrinkles.gif

シーンをダウンロード: File:tension_and_wrinkles.hipnc

Measure SOPは、プリミティブの周長または面積を計算します。 プリミティブを変形させる前にその周長を測定して、変形後に再度測定して、それらの差分を計算すれば、そのプリミティブが収縮したのか伸長したのか(それがプラスなのかマイナスなのか)を調べることができます。

これをカラーにマッピングすることで、その伸縮度合いを表示させることができ、そのカラーを使って皺を生成することができます。

このサンプルでは、Ripple SOPを使ってベースとなる歪みのモーションを低解像度形状に適用し、Point Deform SOPを使って、その3番目の入力のDeformed Point Latticeにその形状を接続して、高解像度形状を変形させています。

ここで注目すべき2つの事:

1. 2つの接続からアトリビュートをVOPネットワークに取り込む。"Get Attribute" VOPでそれらのアトリビュートを制御します。このVOPには入力を選択するためのドロップダウンメニューがあります(デフォルトはFileで、VOPネットワークのFisrt Input/Second Input/Third Input/Fourth Inputを選択することができます)。ここには、検索するアトリビュート名(例えば、"rest_p")、アトリビュートタイプ、ポイント番号などを指定することができます。静的な形状と変形させた形状はどちらも同じポイント数で同じID、つまり、各静止ポイントと各変形ポイントが一対一に対応しているので、"ptnum"をインデックスとして使用することができます。

Get attribute.png

2. エッジの張力を調べる。前述のとおり実際にはHoudiniはエッジを扱わないので、カーブまたはポリラインからどのようにして張力を調べればよいでしょうか?他の同様の張力を使ったサンプルでは面積を使い、このサンプルでは周長を使用しました。サンプルのネットワーク内の上部付近にあるEnds SOPでUnrollを指定することで可能な限りエッジだけを残したままポリゴンを削除することができます。驚いたことに、Measure SOPでも周長を計算できるではありませんか。 このMeasure SOPを使って作成した"perimeter" PointアトリビュートをAttribute Promote SOPのAverageモードを使ってポイントにプロモートさせます。それで十分上手く機能するように思います。単一エッジをスケールした時に何か問題が起こりそうな気がしたのですが、実際には何も問題なかったです。ですよね?

私のこのサンプルは非常にレベルが低いテクニックを使っており、odforceフォーラムのSebkaine氏は、uとvの方向のストレスを計算したり、他にもすごいテクニックを使って非常にすばらしいセットアップを作っていました:

http://forums.odforce.net/topic/22613-how-to-get-edge-length-for-tension-map/?p=134693

ポイントを使ったカーブの生成

Curve create.gif

シーンをダウンロード: File:curve_create_02.hip

この質問にそれが可能であると回答されています。 Add SOPを使用すれば1回の処理で複数のポイントを作成することができ、そして'Enter'を押すとビューポート内に小さな移動コントローラが表示されるので、それを使ってそれらのポイントを動かすことができます。

それらのポイントをグループ化し、For-Each Subnetworkを使って、その中にさらにAdd SOPを追加してそれらのポイントをラインで接続します。 この時のAdd SOPはPolygonsモードです。ポリラインを滑らかなカーブに変換するためにConvert SOPを使用します。

音声を読み込んでアニメーションする波形を作成するCHOPs

Chops waveform.gif

シーンをダウンロード: File:chop_waveform.hipnc

これは、ProTrackerやWinampというメディアプレーヤーにあるような典型的なオシロスコープエフェクトです。 CHOPのドキュメントがあまり詳しく書かれていなかったので、odforceにそれに関連したサンプルを探しました。 このサンプルは、それに修正を加えたものです。

  1. たくさんのポイントを使ってラインを作成します。ここでは1000個のポイント使用しました。
  2. CHOP Networkを作成し、その中にFile CHOPを追加して.wavファイルを指定します。Null CHOPを追加して名前をOUTに変更し、そこにFile CHOPを接続します。
  3. 上位階層に移動し、ポイント別に時間を格納した'ptime'Pointアトリビュートを作成します。このアトリビュートには、音声サンプルをキャプチャできるほど十分に小さい値を設定します。たいていの音声ファイルは1秒あたり44000サンプルなので、思うように出力を得られるようにするには、この値を1000分の1秒あたりに設定する必要があります。
  4. 次に'sample' Pointアトリビュートを作成します。このアトリビュートには、choptエクスプレッションを使って"OUT"という名前のNull CHOPからサンプル時間を格納します: chopt('/path/to/chop/OUT/chan0', $PTIME)
  5. これで面白い事ができます。


CHOPが非常に遅くなる可能性があると警告が出て、さらに、VEXコールを使わないでCHOPからデータを読み込むことにさらに不安になりました。 でも心配無用で、これは十分高速でした。CHOP内でたくさんの余計な音声チャンネルを作成したり、重い音声処理をした場合にはスローダウンしてしまいそうです。 このようにサンプルを読み込んでそれをポイントにマッピングさせると、2000ポイントでリアルタイムに、20000ポイントで妥当なレベルで再生されました。

カーブを少し滑らかにしたいのであれば、役立つノードがあります。例えば、Resample SOPを使ったり、Pass Filter CHOPを使ってLow Cutoffを下げて高周波数を除去することができます。

おそらく音声の再生を有効にしたいことでしょう:

  1. 右下のスピーカーアイコンをクリックします。
  2. Scrubタブを選択します。
  3. Fileパラメータに音声ファイルを設定するか(これが一番うまく動作すると思います)、または、CHOPパラメータにOUT Null CHOPを設定します。
  4. Audio Panelダイアログを閉じて、Global Animation Optionsダイアログ(スピーカーアイコンの右側のアイコン)を開いて、'Integer Frame Values'を無効にします。


音声を再生している間は、少なくとも私が使用したサンプルだと、HoudiniがLinuxだと少しカクついたり、Windowsだとクラッシュした感じになりました。 Flipbookを作成する時に表示されるRender FlipbookダイアログのFlipbookタブにあるAudio Filenameパラメータを設定した方が安定しているので、クラッシュに遭遇したら、そちらを提案します。

軌跡

Trails noise.gif

シーンをダウンロード: File:trails_noisey_v01.hipnc

Trail SOPは、基本的にはNukeやAfter Effectsで使用するような時間の残響エフェクトですが、それらの残響に面白い方法でアクセスすることができます。 最も単純なエフェクトは、時間軸に沿った入力ジオメトリの複製です。最も良く使われるエフェクトは、モーションブラー用のVelocityの計算です(前フレームにおけるすべてのポイントポジションを取得し、その差異を記録して、それをMantraのモーションブラーに反映されます)。このサンプルでは、他の軌跡モードの1つを使ってカーブを生成しています。

カーブ長に合わせてカラーをマッピングするために、UV Texture SOPを使ってTexture Typeを'Rows & Columns'に設定しました。 このテクニックによって、各プリミティブに対して連続的にUとVをマッピングすることができるので、まさに求めていたことができます。 これにより、Color SOPを使ってColor TypeをRamp from Attributeに設定し、Attributeにuを指定することができるようになります。

藁のような軌跡を表現できるように、Point VOPを使ってカーブポイントに基づいてフローノイズを生成し、u値に基づいてそれをスケールし、それをカーブポイントに加算します。球近辺のノイズを0にスケールダウンし、軌跡の終わりでノイズをフル強度にスケールします。

最後に、カーブ長に合わせてアルファもマッピングし、それらのカーブを線形カーブから滑らかなNURBSカーブに変換します。

このセットアップ内ではいくつか細かな調整をしています。 UVはデフォルトで頂点上に投影されるますが、ポイント上に投影する必要があったのでAttribute PromoteでUVアトリビュートを頂点からポイントに転送しました。 また、UVアトリビュートはベクトル(おそらくuvw)として作成されるので、Color SOPで使用する時に紛らわしいです。 そのため、単一floatアトリビュートとして'u'を作成し、UVWから1番目の値をuにコピーしました。 最後に、その軌跡自体はカーブではなく単一エッジによるメッシュです。HoudiniではそれらのメッシュをダイレクトにNURBSに変換させてくれなかったので、明示的にメッシュをポリラインに変換し、そのポリラインをカーブに変換する必要がありました。

最後のAttribute Delete SOPは、いつものあれです。Merge SOPでラインを結合した時に各ラインに異なるアトリビュートがあると警告が出るのでそうしています。 :)

Add SOPを使ってポイントからラインを作成する

Add sop screenscap.gif

シーンをダウンロード: File:add_sop_examples.hipnc

基本的にあなたが最終的にやりたい事がたくさんのポイントを受け取てそれらのポイントを繋げることとしましょう。 前述のとおりVEXを使う方法もありますが、ここではコードを使わない方法としてAdd SOPを使います。 最も基本的な例は以下のとおりです:

  1. Grid SOPを作成します。
  2. Scatter SOPを使って、Force Total Countを100に設定して100個のポイントをグリッド上にばら撒きます。
  3. Add SOPを追加します。
  4. Polygonsタブに切り替えて、'By Group'サブタブを選択します。
  5. すべてのポイントが繋がってポリラインになりました。ポイント番号が接続順を決定します。


すべてのポイントを繋げましたが、他にもAdd SOPのAddモードをGroups of N pointsに、Nを2に設定することで、ポイントを2個おきに繋げてラインを生成することもできます。

ばら撒いたポイントの順序を意図的にランダムにすれば、Groups of N pointsモードによって少しカオスなラインを生成することができます。 Add SOPの前にSort SOPを接続すると便利です。例えば、ポイント順をX軸に沿って並べれば、その結果のラインはだいたいX軸に垂直になります。 同様に、Sort SOPの'Spatial Locality'モードを使用すると、近接ポイントが近いポイント番号になるようにポイント順を並べ替えようとするので、その結果、ごちゃごちゃした配置が軽減されます。

Add SOPと併用すると便利な他のノードは、Cluster Points SOPです。これは、Sort SOPのSpatial Localityモードと同様ですが、作成したい'近接ポイント'のクラスタ(点群)の数を定義することができて、そのクラスタのインデックスが@clusterアトリビュートに格納されます。これによって、Add SOPのBy AttributeモードのAttribute Nameに'cluster'を指定できるようになります。すると、クラスタ単位でラインが繋がります。

まだ他にもAdd SOPに@idを使用するテクニックがあります。同様に、ばら撒かれたポイントに対してPoint Wrangle SOPを使って@id=@ptnumを記述し、次にDuplicate SOPを使ってポイントすべてをY方向に移動してY軸で回転させたコピーを作成します。それをAdd SOPに接続して、By AttributeモードでAttribute Nameに'id'を設定すると、ポイントを押し出したような感じで各オリジナルポイントから各コピーポイントまで繋がったラインが生成されます。

この最後の例はパーティクルシステムで役立ちます。 私がMayaで気に入っていた機能はパーティクルタイプのストリーク指定です。これはHoudiniにはありませんが、自身で作成するのが容易です。 パーティクルには必ず@idアトリビュートが付いているので、パーティクルの後にTrail SOPをデフォルト設定のまま追加し、Add SOPを追加してPolygonsタブのBy Groupサブタブを選択して、Attribute Nameに'id'を指定すれば、パーティクルの軌跡がラインで繋がるようになります。 そうです、Trail SOPを使えばこれをダイレクトに処理することができます(もしくは、もっと良い方法はパーティクルに対して何も処理しないことです。単にモーションブラーを有効にするだけで、各パーティクルの@vから自動的にストリークが生成されます)が、必要に応じて手動でそれを作成する方法を知っておくのも楽しいです。

Sweep SOPを使って正しいUVを取得する

Sweep uvs.gif

シーンをダウンロード: File:sweep_uvs.hip

Sweep SOPを使用する時に覚えておいて欲しいお決まりごとをいくつか紹介します。 入力の断面カーブと背骨カーブにUVが付いている場合、Sweep SOPはその片方のカーブからuを、もう片方のカーブからvをコピーすることができます。 しかし99%の確率で思ったように動作しません。以下に重要な事を記述しておきます:

  • Sweep SOPは、'One Primitive at a Time'または'Cycle Primitives'のどちらかである必要があります。
  • 入力の断面が閉じていない場合、出力されるサーフェスは閉じないので、後でFuse SOPを実行する必要があります。
  • 入力の断面が閉じている場合、UVの継ぎ目がおかしくなります。
  • この場合、断面上の頂点のUVを使用してください。すると、Sweep SOPは断面カーブの頂点から@uv.vが、背骨カーブのポイントから@uv.uをコピーするようになります。
  • 断面のUVに対して@uv.vではなくて@uv.uを使用するようにしておきたいので、Sweep SOPの前に断面のUとVを入れ替える必要があります。

ボリューム

ボリュームのページHoudiniVolumesを用意しました。SDFやVDBなどの説明はそこにあります。

Attribute Wrangle SOPとIQ氏のカラーランプ

http://i.imgur.com/BFpsjcO.gif

シーンをダウンロード: File:iq_colour_ramp.hipnc

4つの入力カラーと短いエクスプレッションを使って面白いカラーを生成する方法についてInigo 'Shadertoy' Quilez氏によるこの記事を見つけました。この類の事は、Attribute Wrangle SOPが適しています。 VOPをわざわざ組むことなく少しのVEXコードだけでこのような表現が可能です。Wrangleノードのいいところは、簡易的にUIを作成できることです。 例えば、まだスライダが存在していない状態で'myslider'という名前のfloatスライダの値を'foo' float値に割り当てたとします:

float foo = chf('wavelength');


...コードウィンドウの右側にある小さなプラグアイコンをクリックすると、Houdiniが自動的にそのスライダを生成してくれます。 簡単にインタラクティブなコントロールを作成することができるのです。以下は、このWrangleノードのコードです:

vector a = chv('base');
vector b = chv('gain');
vector c = chv('wavelength');
vector d = chv('phase');

v@Cd = a+b*cos(2*3.1415*(c*@uv[1]+d));


このコードで処理している事は、各パラメータを制御するための4個のスライダを作成し、Cdアトリビュートの作成/割当をして、iq氏のサイトのエクスプレッションを適用しています。色々いじってみると超楽しいです。

このエクスプレッションの挙動を理解できるようにするために、私はカーブを3本コピーし、それをr、g、bの値に分割して、そのカラーをP.yにマッピングしました。

パックプリミティブ

もし読むより見る方が好きな方は、このBulletマスタークラスの最初の概要が上手く説明されています: https://vimeo.com/80840429

パックプリミティブとは、単一ポイントでジオメトリを表現したものです。 例えば、2800ポイントある豚さんの頭をパック化して、それを単一ポイントに割り当てたものです。 パックプリミティブはMayaのシェイプトランスフォームまたはHoudiniの/objネットワークのノードのようなものとほぼ考えることができますが、これはSOPコンテキスト内で扱います。

Pack pig.gif

パックプリミティブは色々な面で役立ちます:

  • これまで重かった膨大なジオメトリを読み込んで制御することができます。Houdiniはパック毎に1ポイントしか処理しないので、通常の豚さんの50,000コピーだとHoudiniが大幅にスローダウンしてしまったものが、50,000個のパックした豚さんならあっさり処理することができます。
  • たとえシェイプの数が少なくても、/objコンテキストで20個のトランスフォームを扱うよりはSOPで20個のポイントを扱う方が簡単です。
  • Mantraはパックプリミティブをインスタンスとして扱うので、少ないメモリ且つ短い時間で多くのジオメトリをレンダリングすることができます。
  • リジッドボディにパックプリミティブを使用することができるので、膨大な数のジオメトリをシミュレーションすることができます。
  • パックは階層構造に対応しているので、葉っぱをパック化して、それらを枝に配置して、その枝を配置して、その枝をパック化して、それらを木に配置して、その木をパック化して、たくさんの木を配置するといったことができて、そのような方法で容量の節約や個々の編集が容易になっています。
  • 必要に応じてパックをアンパックすることができます(通常なら眉をひそめますが、4000本のパックした木から特定の2本の木だけをアンパックして編集することができるのが素晴らしい)。
  • 階層構造のAlembicファイルがパックプリミティブとして扱われるので、すべてがうまく順応します。
  • アンパックすることなくパックプリミティブを編集できるツールが増えています。例えば、パックプリミティブのサブオブジェクトにマテリアルや他の特定のレンダリングプロパティを割り当てることができます。


いくつかの方法でパックプリミティブを作成することができます:

  • Pack SOP
  • Copy Stamp SOPのStampタブにある'Pack Geometry Before Copying'トグル。
  • File SOPでは、'Load'ドロップダウンメニューからジオメトリをパックプリミティブとして読み込むことができます。
  • Object Merge SOPには、マージする前にパックするオプションが用意されています。
  • Alembic SOPには、パック、階層パックなどで読み込むためのオプションがいくつか用意されています。
  • Assemble SOPは、粉砕ジオメトリを紐付けるのによく使用し、このSOPには'Create Packed Geometry'トグルが用意されています。


Assemble SOPは、その中身の挙動を見ることができるので、それをいくつかの手順で模倣することができます。 例えばボロノイ破壊をする場合、そのAssemble SOPの中を見れば、それらの破片を手動でパックプリミティブに変換する方法を調べることができます:

  1. Connectivity SOPを作成して、Connectivity TypeをPrimitiveに設定して島を区別します。
  2. Primitive Wrangle SOPを追加し、次のコードを入力します: "s@name=itoa(i@class);"
  3. Pack SOPを追加し、'Name Attribute'トグルを有効にします。


Connectivity SOPは'class'アトリビュートを生成します。このアトリビュートには、最初の島のすべてのプリミティブには0、次の島は1、その次は2というように値が格納されます。Pack SOPでは、パック毎に固有の識別子が文字列として必要なので、VEX Wrangleを使って'class'アトリビュートを'name'という文字列アトリビュートに型変換しました。最後に、そのPack SOPで'name'を照会してパック化を実行します。

Pack SOPから直接'class'アトリビュートを照会すればいいじゃないかと思うかもしれませんが、文字列アトリビュートが必要なので、整数型から文字列型に変換する手順を踏みました。

なんで整数から文字列に変換する関数の名前がitos()ではなくitoa()なんだろう?わかんねぇ。

パックプリミティブの制御

パックプリミティブが形状を表現したポイントと考えれば、そのポイントを動かせば、その形状も動くことになります。 Edit SOPを使って直接ポイントを編集したり、Transform SOPやVOPネットワークを使って簡単にパックプリミティブを移動させることができます。

ただし、回転とスケールはまた別の問題になります。 ポイントは回転やスケールを持たないので、パックプリミティブを回転してもスケールしても何の効果もありません。 それでは、どうやって制御すればよいでしょうか?

Geometry Spreadsheetのプリミティブビューを見てみると、おそらく今まで気づいてなかったかもしれませんが、そこに'intrinsics'ドロップダウンメニューがあります。カーブ長、境界ボックスなどのこれらのほとんどは読み込み専用アトリビュートなのですが、パックプリミティブに関してはそうでないアトリビュートがいくつかあります。興味深いことに'transform'がそれです。ドロップダウンメニューから'transform'を有効にすると、それがトランスフォームマトリックスであることがわかります。これは読み込み、書き込みのどちらも可能なので、このアトリビュートを使えば、パックプリミティブの回転とスケールを修正することができます。

Primitive Wrangle SOPで以下のコードを記述すると、パックプリミティブがY軸で回転します:

matrix3 m = ident();
float angle = @Time;
vector axis = {0,1,0};

rotate(m, angle, axis);
setprimintrinsic(0, "transform", @primnum, m);


以前に挙げたサンプルと同様に、まず最初にデフォルトのトランスフォームマトリックスを'm'(ここには単位行列を指定したいのでident()をコールしました)として宣言し、回転角度をangle、回転軸をaxisとして宣言します。次に、実際にrotate()をコールして、そのマトリックスのmを回転させます。そして、このマトリックスをintrinsic-transformアトリビュートに格納します。

'@transform = m'のように記述するのが理想なんですが、intrinsicアトリビュートはそのように設定することはできず、setprimintrinsic()をコールします。他のVEX関数と同様に、この関数には、制御したいジオメトリ(0またはWrangleの入力)、アトリビュート名('transform')、制御したいプリミティブ番号(@primnum)、値(m)を指定する必要があります。

面倒くさいように思いますが、スケーラビリティを考えたらパフォーマンスの恩恵があるのでその価値はあります。

これは、パックプリミティブのトランスフォームを作成したでそれを修正するためにあることに注意してください。 Copy系SOPに接続するためのポイントをセットアップして、そのCopy系SOPで自動的にパックを生成させることの方が多いです。 この場合では、通常のコピー/インスタンスと同じように回転とスケールを定義した方が簡単です。つまり、前述のサンプルで説明しているように@orient, @pscale, @rotなどを使用します。

インスタンスアトリビュートを使ってパックプリミティブをトランスフォームさせる

Pointinstancetransform.gif

ほとんどの場合、Copy to Points SOPの'Pack and Instance'を有効にしてパックプリミティブを作成することが多いです。 ジオメトリのコピー元のポイントに@orientや@scaleなどの通常のインスタンスアトリビュートが付いていれば、パックプリミティブがトランスフォームされます。

Copy to Points SOPのにそのようなアトリビュートを作成または修正したり、他の手段(Assemble SOP,Pack SOP)によってパックジオメトリを作成した場合、そのパックプリミティブは更新されません。この場合には、肩をすくめて、前のTipで説明したように'transform' Intrinsicアトリビュートを設定します。 私はなぜこれがだめなのかそれとも良い方法があるのか今まで考えたことがなかったです。

FXスーパーバイザーで親愛なるAnimal LogicリーダーのMiles Green氏と疑問のあったパックプリミティブのアニメーション問題について話した時、彼は足を組んで'なるほど、パックプリミティブがインスタンスアトリビュートを使用できるように他のIntrinsicを設定してみてはどうかね'と言いました。 調べてみると確かにそのIntrinsicがありました。すべてのパックプリミティブに対して'pointinstancetransform'を1に設定します:

setprimintrinsic(0,'pointinstancetransform',@ptnum,1);


すると、ほーら、パックプリミティブがトランスフォームされました。 それを知るまでは、まるでアホのように'transform' Intrinsicアトリビュートを設定していました...

TIME PASSES

まぁ、注意しましょう。Matt Ebb氏は、このテクニックを使ったいくつかの問題点を指摘しました:

  • 'pointinstancetransform'は'transform'を乗算しており、置換しているわけではないということ。
  • orientアトリビュートとパックのtransformアトリビュートの両方を使用すると、DOPのコンストレイントで混乱が生じる可能性があること。
  • DOPsはすべてのトランスフォームを'transform' Intrinsicに書き出しているので、その向きやスケールなどをそのままにしないと混乱する可能性があること。
  • このテクニックを使った後日、このアトリビュートをアクティブにすると、そのプリミティブをトランスフォームできなかったことに気づきました。結局手動でやり直しました。


あぁ。パックプリミティブ vs フラグメント vs Alembic vs Intrinsic vs インスタンスアトリビュート vs DOPsは今でも理解するのに少しトリッキーで、希望としては、それらが整理されて今後のHoudiniのバージョンで統一されたらいいのに...

循環キーフレーム

私を怒りで震えさせることがあるとすれば、それは、キーフレームの範囲外の設定におけるHoudiniの挙動です。 その設定の機能があるっちゃあるけど、いつも忘れてしまうほどにどこにあるのかわからないほどにわかりにくくて、あちこち20分ほど探すはめになります。 これは私のためのメモです。Alt-Eを覚えておくように。 循環キーフレームはAlt-Eだぞ、Matt。Alt-Eだ。いいか?覚えたかr?循環?それはAlt-Eだ。

ALT-E。

または、メニューから選択したいのであれば、 チャンネルを右クリック->Channels and Keyframes -> Edit Displayed Channels Properties...

Cycle keyframes.gif

Level of Detail(詳細レベル)のプレビュー

Lod pig sm.gif

シーンをダウンロード: File:lod_pig.hipnc

これは、LODをカメラからの距離に応じて変更するコードです。 パックした高/中/低の詳細レベルの3匹の豚さんの頭に対して、各豚さんに@startと@endのアトリビュートを定義してカメラからの距離の範囲を設定しました。 これら3匹の豚さんをマージして、以下のWrangleを使って、カメラからの距離に応じて豚さんを削除しました:

matrix m = optransform('/obj/cam1');
vector cam = cracktransform(0,0,0,0,m);
float d = distance(cam,@P);
if (d<@start || d>@end) {
  removepoint(0,@ptnum);
}


Agent SOPを介したFBXのインポート

Alembicを使用すれば、それを/objコンテキストに読み込むのかSOPコンテキストに読み込むのが選択することができます。 /objコンテキストに読み込んだ場合、親子関係でオブジェクトノードが作成され、Alembicの各部分が各ノードに格納されます。 これは理に適ってはいるのですが、FXの作業では一般的にあまり役に立ちません。 Alembic SOPでは、それらのオブジェクトすべてを大きな箱にパックジオメトリとして格納し、各パックジオメトリに@pathアトリビュートを付けることができます。

困ったことに、FBXにはそれに相当する機能がありません。メインメニューのFileからFile -> Import -> Filmbox FBXを選択することしかできないです。

そうですよね???

いつも忙しいdiscord/odforceのAtom氏が素晴らしい回避策を見つけました。 Agent SOPを配置し、InputパラメータをFBXに設定して、ファイルを指定します。 するとなんてことでしょう。アニメーションが付いたFBXジオメトリが単一パック形状として取り込まれたではありませんか。 それをアンパックするとすべてのパックジオメトリが展開され、そこに@nameアトリビュートが付いています。ありがとうAtom!

Edge Transport SOPを使ったプリシージャルな増殖

Edge transport.gif

シーンをダウンロード: File:edge_transport_clip_growth2.hiplc

ここでは、Jake Rice氏とHenry Foster氏からのテクニックを組み合わせて、いつも人気のある増殖/感染の表現を別の方法で実装しましたが、シミュレーションが不要です。 新しいEdge Transport SOPは、特定の位置からポイントまでの距離を計算します。Rest SOPを使ってポイントの実際の位置を格納してから、Edge Transport SOPで求めた距離をポイントのY位置に設定して、一時的にすべてのポイントをY軸沿いに並べます。 Y軸沿いに並べられたそれらのポイントを2個のClip SOPを使って必要なポイントだけを残して、再度@Pに@restを設定すれば、下図のような素晴らしいルックを表現することができます。

この投稿は、シンガポールからシドニーへ飛行している時に作成して更新したおまけです。地上から10kmの高さのWifiはすごいね!

PolyExtrude SOPとアトリビュート

Polyextrude attrib sm.gif

シーンをダウンロード: File:polyextrude_by_attribs.hip

PolyExtrude SOPには、押し出し距離、内側オフセット量、捻じれ量を調整するパラメータスライダがあります。 Local Controlタブには、たくさんのトグルパラメータがあります。Distance Scale/Inset Scale/Twist Scaleのトグルを有効にすると、ポリゴンフェース上のアトリビュートが検索されて、そのアトリビュート値でその効果が乗算されます。デフォルトのアトリビュート名は、zscale、insetscale、twistscaleですが、必要に応じて名前を変更することができます。

このサンプルでは、ランダムに動きまわるメタボールを作成し、それを赤で塗って、Attribute Transfer SOPを使って球の半分くらいにソフト減衰するようにカラーを転送させました。 次に、この赤に基づいた@zscaleアトリビュートを作成し、それを使ってPolyExtrude SOPを駆動させました。 非常に簡単なのにカッコいいルックが得られます。ManVsMachine/Aixsponza/Panoplyが私に数秒で仕事のオファーを送ってくれることを期待しています... ;)