Houdini 20.0 VEX

VEXエクスプレッションの使用

On this page

概要

Houdiniのいくつかのノードでは、短いVEXエクスプレッションやVEXコードのスニペットを記述することができます。例えば、Attrib Wrangleジオメトリノード、Geometry WrangleGas Field Wrangleダイナミクスノード、パーティクルダイナミクスノードです。

これらのVEXエクスプレッションは、エレメント(ノードタイプに応じてポイント、パーティクル、プリミティブ、ボクセル)毎に実行されて、ノードを通過します。コードはノードのパラメータとジオメトリアトリビュートの値を読み込んで、特別な変数を設定して入力ジオメトリの値を変更することができます。

Tip

Python SOPも同様ですが、Python SOPはPythonスニペットを使ってジオメトリを編集することができます。

アドホック(一時的な)修正になぜVEXを使うのか?

パフォーマンスを良くするために、Houdiniは、ローカル変数と外部チャンネル参照によるHScriptエクスプレッションから、アトリビュートを扱ったVEXによるアドホックジオメトリ修正へと移行しています。

  • VEXとアトリビュートを使用した方が、HScriptエクスプレッションとローカル変数を使用するよりもパフォーマンスが非常に良いです。その方が高速で自動的にスレッド化と並列計算に対応します。

  • ローカル変数では、その名前が大元のアトリビュートの名前と同じでなかったり、ノード毎に異なることがあるので、そのようなローカル変数を扱うよりも実際に直接アトリビュートを扱えた方が使いやすいです。

  • HScriptエクスプレッションでは、ノード内でローカル変数のマッピングが行なわれていないアトリビュートの値を取得するのが面倒でした(例えば、point(opinputpath(".",0), $PT, "my_var", 0))。VEXでは、これが非常に簡単です:v@my_var。Houdiniで技術的な作業をするのにアトリビュートを伴うことが多いので、HScriptエクスプレッションよりもVEXエクスプレッションの方が実際に作業を単純にすることができます。

  • ネットワーク上の情報をアトリビュートに渡す方が、下流のノードで外部参照を使って上流のノードのデータを取得するよりも、並列処理の観点では本質的に馴染みやすいです。

  • 現在のところ、VEX処理はコンパイルSOPブロック内でサポートされているのに対し、ローカル変数を使ったHScriptエクスプレッションはコンパイルすることができません。

  • VEXには、ほとんどのHScriptエクスプレッション関数に相当する関数があり、Pythonライクな配列/文字列のスライスやPythonライクな辞書の使い勝手で、配列や文字列の処理といった事に使用するのが簡単です。

ユーザは、これまで以上に膨大で複雑なジオメトリを扱う時に、満足のいくパフォーマンスが得られるように、スレッド化と並列処理がより重要になってきます。 この単純な事実こそが、アドホックジオメトリ制御をするためにHScriptエクスプレッションの代わりにVEXを幅広く使えるようにした理由です。

おそらく、HScriptはVEXよりも手軽な特定のジョブに対して使うことになるでしょう。とはいえ、ジオメトリ制御に関しては、WrangleとVEX/VOPの方が先進的で、新しいワークフローを学ぶ価値があります。

構文

VEX snippet パラメータでは、VEXコードのスニペットを入力することができます。VEX関数のリストを参照してください。

VEXは“コンテキスト”の概念を持ちます。いくつかの関数は、特定のコンテキストでのみ利用可能です(例えば、SOPコンテキストのジオメトリ情報にアクセスするための関数)。VEXスニペットは、CVEXコンテキストで実行されます。

知っておくべき事

  • 各ステートメントはセミコロン(;)で 終わらなければなりません !

  • ///* ... */はコメントとして使うことができます。

  • VEXでは、sincosなどの三角関数は度ではなく、ラジアンを使います。

  • ベクトルのアトリビュートは、$VXではなく@v.xとして操作します。つまり、3つの別々の$VX$VY$VZの変数を取得するのではなくて、1つの@vベクトルの値からドット表記を使ってxyzのコンポーネントを取得することができます。

  • randは、ベクトル変数に適用するとベクトルノイズを生成します。これは、フォースの例と同様にフォースのすべてのコンポーネントが@idで均等にランダム化されることを期待しているのであれば、予期しない結果を招く可能性があります。ベクトルをスカラーに変換するには、float()キャスト(型変換)を使います。

  • ジオメトリアトリビュートの設定の方法が異なれば、その効果が異なるので、何が起きているのか分からないと混乱を招いてしまいます。詳細は、ジオメトリアトリビュートの設定の動作に関する解説を参照してください。

パラメータ値へのアクセス

スニペットでは、パラメータの内部名を変数名として使用することで、ノードのパラメータの値を読み/書きすることができます。 パラメータの内部名を調べるには、パラメータエディタのパラメータ名の上にマウスカーソルを置いてください。 ツールチップは、Parameter: 内部名を表示します。

例えば、Particle Color DOPでは、 Color パラメータの内部名はcolorです。

color = @Cd;

複数コンポーネントのパラメータは、ベクトルとしてアクセスします。例えば、 Position パラメータの内部名はtです:

// 位置を設定します
t = {0, 1, 0};

ドット演算子を使えば、パラメータの個々のコンポーネントにアクセスすることができます:

// X軸に沿って1ユニット分移動します。
t.x = t.x + 1;

Note

ユーザが作成したパラメータの値にアクセスするには、chv VEX関数を使用します。

ジオメトリのアトリビュートと情報にアクセス

スニペットでは、@attribute_nameを使ってアトリビュートの値を読み/書きすることができます。 例えば、P(ポジション)アトリビュートの読み書きをするには、VEXコードで@Pを使います。

  • Particle DOPは、パーティクルアトリビュートにアクセスすることができますが、それらを変更することが できません 。代わりに、Particle DOPはパーティクル毎にパラメータ値を変化させることでパーティクルに影響を与えています。パーティクルVEXエクスプレッションの記述を参照してください。

  • Volume Wrangleノードでは、@volume_nameを使ってボリュームの読み書きをすることができます。

  • VEXコードに@attributeを記述し、そのアトリビュートが存在しなかった場合、Houdiniは、そのアトリビュートを作成します。(Volume Wrangleノードは、この方法で新しいボリュームを作成 しません 。)

  • Houdiniにはスニペットで使用可能なアトリビュートライクな変数がいくつか備わっています。@elemnumには、現在処理されているエレメント番号が入っています。@numelemには、ジオメトリ/リスト内のエレメントの総数が入っています。下記のインデックス変数を参照してください。

  • 一部のノードには、ジオメトリに関して計算された情報を読み込み可能なアトリビュートライクな変数が備わっています。例えば、Volume Wrangleノード@centerを使用すると、ボリュームの中心を取得することができます。

  • Houdiniは、よく使用する一部のアトリビュートを適切なVEXデータタイプを使って型変換するようになっています。 以下の既知のアトリビュートのテーブルには、Houdiniが自動で型変換できるアトリビュートを載せています。

    Houdiniは、手動で別のタイプを指定しない限りは、他のすべての@参照を float だと想定します。 手動でアトリビュートのVEXデータタイプを指定するには、@記号の前にデータタイプを意味した文字を追加します。 例えば、fooアトリビュートを文字列に型変換するには、s@fooを使用します。

    Note

    @opinputn_nameを使って異なる入力にアクセスする場合には、この自動型変換は動作 しません 。その場合、データタイプを常に指定する必要があります。

    以下のテーブルには、利用可能なデータタイプとそれに呼応する文字を載せています。

    VEXタイプ

    構文

    float

    f@name

    vector2 (2 floats)

    u@name

    vector (3 floats)

    v@name

    vector4 (4 floats)

    p@name

    int

    i@name

    matrix2 (2×2 floats)

    2@name

    matrix3 (3×3 floats)

    3@name

    matrix (4×4 floats)

    4@name

    string

    s@name

    dict

    d@name

既知のタイプのfloat以外のアトリビュート

便宜的に、よく使用する以下のアトリビュートのタイプは指定する必要がありません(Houdiniは、それらのタイプをどうするべきか知っています)。 他のアトリビュートは、タイプを指定しない限りfloatだと見なされます(上記参照)。

例えば、VEXスニペットで、v@Cdとタイプする代わりに@Cdとタイプするだけでベクトルを指定することができます。

Tip

よく使用するアトリビュートに関する情報は、アトリビュートのページを参照してください。

VEXタイプ

アトリビュート名

vector (3 floats)

@P, @accel, @Cd, @N, @scale, @force, @rest, @torque, @up, @uv, @v

@center, @dPdx, @dPdy, @dPdz (Volume Wrangleを参照してください)。

vector4 (4 floats)

@backtrack, @orient, @rot

int

@id, @nextid, @pstate

@elemnum, @ptnum, @primnum, @vtxnum, @numelem, @numpt, @numprim, @numvtx (下記のインデックス変数を参照してください)。

@group_* (下記のグループメンバーシップへのアクセスを参照してください)。

@ix, @iy, @iz, @resx, @resy, @resz (Volume Wrangleを参照してください)。

string

@name, @instance

他の入力のアトリビュートにアクセスする方法

ノードに2つ以上の入力があれば、アトリビュート名の頭にopinputinputnum_を付ける(例えば、v@opinput1_P)ことで、別の入力のアトリビュートを取得することができます。 これは、その番号の入力(1番目の入力が0、2番目の入力が入力1など)と同じエレメント(ポイント/プリミティブ/頂点)からその名前のアトリビュートを読み込みます。

“同じエレメント”とは、他の入力と同じインデックスを持つエレメントのことです(例えばポイント番号10を処理する時、@opinput1_Pは、2番目の入力内のポイント番号10のPアトリビュートを取得します)。

しかしなら、ノードによっては、アトリビュートの値に基づいて“同じ”エレメントに合致させることができる“Attribute to Match”パラメータがあります。 例えば、“Attribute to Match”としてidを使用して、12に設定されたidアトリビュートのポイントを処理する場合、@opinput1_Pの記述によって、id12に設定されている2番目の入力のポイントのPアトリビュートが取得されます。 スニペットを記述するノードで、このパラメータを確認してください。

ジオメトリアトリビュートの設定

@構文(例えば、@foo = 12.5)を使用してジオメトリアトリビュートを設定することができます。 この方法は、VEXスニペット内でジオメトリアトリビュートを設定する方法よりも優先されます。

VEX関数内で利用可能なジオメトリは論理的には3つタイプがあります:

  • 入力ジオメトリ 。これは、例えば、point(0, …)を使用してアトリビュートが読み込まれる先のジオメトリです。

  • 現行ジオメトリ 。これは、VEX関数が実行されているポイント/プリミティブ上の現行ポイント/プリミティブです。これは、@foo構文を使用してアトリビュートが読み込み/修正されるジオメトリです。

  • 出力ジオメトリ 。これは、例えば、setpointattrib(0, …)を使用してアトリビュートが書き出される先のジオメトリです。

point()からの読み込みが@fooまたはsetpointattrib()を使用して書き込んだ変更内容を表示しないのは、これが理由です。 また、setpointattribによる変更が@foo =構文による変更を上書きするのも、これが理由です。

これらを別々にコピーしたジオメトリを使用することで、その処理の順番を変更することができます。 そうしなかった場合、たくさんのロッキングが必要になったり(パフォーマンスが低下)、コード内で競合状態が発生することになります。 この方法なら、ロッキングが不要なままに、変更内容を確実に決めることができます。

インデックス変数

スニペットで、ジオメトリ内のすべてのポイント/プリミティブをループさせたいことがよくあります。0からいくつまでの数字のリストを使ってループさせることもできます。 ループを使ってリスト内の現行エレメント番号を取得したり、リスト内のエレメントの総数を取得することは、役立つことが多いです。

@elemnum

現行エレメントの番号。

@elemnumは汎用的に(または、番号をループさせたい時に)使用することができます。(例えば)ポイントを操作したい時には、代わりに@ptnumを使用した方がわかりやすいですが、そのコードをプリミティブまたは頂点に対して操作するように変更してしまうと、動作しなくなる危険性があります。

@numelem

現行ジオメトリ/リスト内のエレメントの総数。

@numelemは汎用的に(または、番号をループさせたい時に)使用することができます。(例えば)ポイントを操作したい時には、代わりに@numptを使用した方がわかりやすいですが、そのコードをプリミティブまたは頂点に対して操作するように変更してしまうと、動作しなくなる危険性があります。

@ptnum

現行ポイントのポイント番号。スニペットでポイントをループさせる時に使用します。 頂点でループさせた場合、これは、その頂点に接続されているポイントです。 プリミティブでループさせた場合、これは、そのプリミティブ上の0番目の頂点のポイントです。

@primnum

現行プリミティブのプリミティブ番号。スニペットでプリミティブをループさせる時に使用します。 頂点でループさせた場合、これは、その頂点を所有するプリミティブです。 ポイントでループさせた場合、これは、そのポイントを含んだプリミティブです。プリミティブが存在しなかった場合は-1です。 そのポイントが2つ以上のプリミティブ内にある場合、どちらかのプリミティブが任意で返されます。

@vtxnum

現行頂点の頂点番号。スニペットで頂点をループさせる時に使用します。 ポイントでループさせた場合、これは、そのポイントに接続されている頂点です。頂点が存在しなかった場合は-1です。 そのポイントが2つ以上の頂点に接続されている場合、どちらかの頂点が任意で返されます。 プリミティブでループさせた場合、これは、そのプリミティブの0番目の頂点です。

線形頂点番号 。これは、 ジオメトリ内のすべての頂点 を数え、その番号の範囲は0からジオメトリ内の頂点の総数-1です。 この番号は、頂点の プリミティブインデックス とは違って、その頂点が属するプリミティブ内の頂点の番号です。

この頂点が属するプリミティブのプリミティブ番号は@primnumです。 任意の線形頂点番号が属するプリミティブ番号を取得するには、vertexprimを使用します。 線形頂点番号をその頂点が属するプリミティブ内の頂点インデックスに変換するには、vertexprimindexを使用します。 頂点をループさせる時、現行プリミティブ内の頂点の総数は@numvtxです。 任意のプリミティブ上の頂点の総数を取得するには、そのプリミティブ番号を使ってprimvertexcountをコールします。

例えば、ポリカーブの頂点のVertexアトリビュートにカーブ沿いに比例した値を設定したいのであれば、以下のコードを書きます:

# 線形頂点番号を取得して、それをプリミティブインデックスに変換します。
int vtx = vertexprimindex(0, @vtxnum)

# 頂点番号を最後のインデックスで除算することで、
# ポリカーブ長に沿った0.0から1.0までの範囲の値をアトリビュートに設定します。
f@prop = vtx / (float(@numvtx) - 1)

@numpt

スニペットでポイントをループさせた時の現行ジオメトリ内のポイントの総数。

@numprim

スニペットでプリミティブをループさせた時の現行ジオメトリ内のプリミティブの総数。

@numvtx

頂点、プリミティブ、ポイントをループさせた時の現行プリミティブ内の頂点の数。 Detailモードの時は、これは頂点の総数です。

Note

ほとんどの反復タイプでは、@vtxnum0 - @numvtx-1の範囲内の値ではありません!

Note

1番目のエレメント番号は0から始まるので、 最後の エレメントのエレメント番号は@numelem - 1です。

スニペットで現行ポイント付近にあるポイントのリストを取得する例:

int nbors[] = neighbours(0, @ptnum)

カーブ上の現行ポイントの 反対側 のポイントのアトリビュートを読み込む例:

vector opposite_color = point(0, "Cd", (@numpt - 1) - @ptnum )

配列

以下のように[]を追加することで、配列をバインドすることができます。

i[]@connected_pts = neighbours(0, @ptnum);

例えば、以下のコードはfooアトリビュートをベクトルとして読み込み、それをP(ポジション)アトリビュートにコピーします。 Pアトリビュートは、Houdiniが自動的にキャスト(型変換)する既知のアトリビュートの1つなので、Pアトリビュートのタイプを指定する必要がありません。

@P = v@foo;

以下のコードは、Cdアトリビュートのxコンポーネントをwhitewaterアトリビュートの値に設定します。 Cdアトリビュートも既知のアトリビュートの1つなので、Cdアトリビュートのタイプを指定する必要がありません。 不明なアトリビュートは自動的にfloatとしてキャスト(型変換)され、whitewaterはfloatのままでよいので、whitewaterアトリビュートのタイプを指定する必要がありません。

@Cd.x = @whitewater;

Tip

コードでアトリビュートを 最初に参照した時にのみ タイプ文字を指定しなければなりません。

また、明示的にアトリビュートのバインディングをプロトタイプすることもできます。 これは、アトリビュートがバインドされていない時に使われるアトリビュートのデフォルト値を指定することもできます。 アトリビュートが作成されると、このデフォルト値も設定されます。

Note

現在のところ、文字列アトリビュートは、作成時に適切にデフォルト値を設定しません。

これは、アトリビュートを変数として宣言することで実行されます。宣言は、行の先頭から記述しなければなりません。 1行につき、1つの変数のみしか宣言することができません。 デフォルト値は定数でなければなりません。 3*5のような計算値は、パラメータリストで無効な初期化として失敗します。

以下のコードは、ベクトルタイプのfooアトリビュートを作成します。そのアトリビュートが入力に存在しなかった場合は、デフォルト値が{ 1, 3, 5 }に設定されます。

vector @foo = { 1, 3, 5 };

この方法でアトリビュートを宣言した後は、v@fooの構文を使う必要はありません。@fooだけでタイプの指定が完了します。

この方法でプロトタイプしたアトリビュートは、インライン定義(例えば、v@foo)よりも優先度が高いです。 その後に定義したミスマッチなタイプやミスマッチなデフォルト値はエラーとしてみなされます。

詳細は、POPアトリビュートのページを参照してください。

アトリビュートの宣言

以下のコードのようにアトリビュートを使う前にアトリビュートのタイプとデフォルト値を指定することができます:

float @mass = 1;
vector @up = {0, 1, 0};

これは2つの方法で役に立ちます:

  • デフォルト値を変数に割り当てます。アトリビュート(例えば、@mass)が存在した場合、その割り当てが無視されます。アトリビュートが存在しなかった場合、その割り当てを使います。

  • アトリビュートのデータタイプを指定します。このコードのように@upアトリビュートのタイプを宣言した後は、v@upの代わりに@upを使うことができます。

等号記号(=)の右側で計算を実行することは できません 。以下の構文はエラーになります:

float @mass = 1 / area;  // エラー
vector @up = set(0, 1, 0);  // エラー

グローバル変数へのアクセス

HScriptエクスプレッションとは違い、$Fなどのグローバル変数を使うことができません。

VOPでは、GlobalsノードからTimeFrameなどの変数を接続することで、VEXスニペットでそれらの変数を使うことができます。

以下の暗黙的な変数を使うことができます:

@Time

Floatの時間($T)

@Frame

Floatのフレーム($FF)

@SimTime

Floatのシミュレーション時間($ST)で、DOPコンテキストにのみ存在します。

@SimFrame

Floatのシミュレーションフレーム($SF)で、DOPコンテキストにのみ存在します。

@TimeInc

Floatのタイムステップ(1/$FPS)

ジオメトリの作成

Attrib Wrangleなどの特定のノードでは、VEXスニペットを使ってジオメトリを作成することができます。

addpointaddprimaddvertexの関数は、ポイント、プリミティブ、頂点を作成することができます。setattribsetprimvertexを使えばジオメトリを修正することができます。removepointremoveprimを使えばジオメトリを削除することができます。グループメンバーシップを設定するには、setpointgroupsetprimgroupを使います。球のプリミティブのトランスフォームなどを修正するには、setprimintrinsicを使います。

setattribを使うよりもバインドされた変数(例えば、@name = val)を使った方が現在のエレメントのアトリビュートの設定が速いです。 他の エレメントのアトリビュートを設定する必要があるなら、setattribだけを使います。setattribを使って別のソースポイントからポイントを修正するなら、mode引数を"add"に設定して、結果を合成します。

ジオメトリ作成関数は、並行して実行することができます。すべての変更がキューになり、VEXコードが既存のジオメトリすべてに対して繰り返した 後に それらのキューが適用されます。つまり、setattribはバインドされた変数(例えば、@name = val)によって変更を 上書き します。

ジオメトリ作成関数の1番目の引数が、“geometry handle”で、ここには作成するジオメトリの場所を指定します(これは、現在のジオメトリへの書き出しの代わりとしてファイルへの書き出しをサポートするためにあります)。現在のジオメトリを指定するには、1番目の引数にはgeoself()を使います。

addpoint(geoself(), {0, 1, 0});

現在のところ、addprim関数は、ポリゴン("poly")やポリライン("polyline")を生成することができます。ポリゴンを作成する場合、addvertexを使って頂点をポイントに追加 しなければなりません 。Houdiniは、ポイントがあっても頂点がないポリゴンでは、多分クラッシュします。

int p1 = addpoint(geoself(), {0, 1, 0});
int p2 = addpoint(geoself(), {1, 1, 0});
int p3 = addpoint(geoself(), {1, 1, 1});

int prim = addprim(geoself(), "poly");
addvertex(geoself(), prim, p1);
addvertex(geoself(), prim, p2);
addvertex(geoself(), prim, p3);

ジオメトリ走査関数

VEXジオメトリ関数を参照してください。

グループメンバーシップへのアクセス

@group_groupnameの形式の特殊な仮想アトリビュートは、現在のエレメントに対してグループメンバーシップを取得または設定することができます。

現在のポイント/プリミティブ/パーティクルが名前付きグループ内にあるかどうかは、@group_name == 1をチェックすることで調べることができます。

@group_nameの仮想アトリビュートを設定することで、現在のポイント/プリミティブをグループに追加またはグループから削除することができます。 アトリビュートを1(またはゼロ以外の値)に設定すれば、現在のエレメントがそのグループに入ります。アトリビュートを0に設定すれば、そのグループから現在のエレメントを削除します。

ユーザ定義関数

VEX関数の構文を使えば、独自の関数をVEXスニペットの一部として定義することができます。 例:

float mysquare(float a)
{
  return a * a;
}

windresist = mysquare(windresist);

Include

コードスニペットで見つかった#include命令は、生成したVEX関数の外部に自動的に移動するので、期待した通りに動作します。

パラメータがアトリビュートのバインド用に存在するかどうかの検査は、プリ処理が実行された後のコードを単純に走査することで行ないます。 このプリ処理がコードスニペット上で のみ 実行されます。しかし、#includeファイルを処理 しません 。そのため、#includeに依存する#ifdef命令で混乱する可能性があります。

役立つ情報

  • マルチラインエディタで編集している時に⌃ Ctrl + Enterを押すと、その変更を“確定”してHoudiniを更新することができます。

  • VEXスニペットは、フレーム毎に(シミュレーションネットワークでは、タイムステップ毎に)実行されます。

  • returnステートメントを使えば、早期にスニペットを終了することができます。例えば、以下のVEXは、新しいパーティクルに対してwindresist0に設定するだけです:

    if (@age > 0) return;
    
    // @ageが0よりも大きい時は、上記のreturnで終了するので、この行は、
    // @age == 0のパーティクルに対してのみ実行されます。
    windresist = 0;
    

エラーメッセージのトラブルシューティング

コード

問題

force += 2

Syntax error, unexpected '}', expecting ';'

各ステートメントはセミコロン(;)で終わらなければなりません。

@v += force;

SRead-only expression given for read/write parameter

パーティクルノードは、パーティクルのアトリビュートを修正することができません。

x = { 0, @y, 0};

Syntax error, unexpected identifier, expecting '}'.

可変長引数を{}ベクトルコンストラクタに渡すことができません。代わりにx = set(0, @y, 0);のようにset()を使ってください。

x = set(0, $F, 0);

Doesn't animate.

$Fを評価中に、VOPネットワークが時間依存でないために、アニメーションしません。代わりに@Frameを使ってください。

VEX

言語

次のステップ

リファレンス