Houdini 20.0 VEX

VEX言語リファレンス

VEXの構文、データタイプなどの詳細を説明します。

On this page

コンテキスト

VEXプログラムは特定の コンテキスト 用に記述します。例えば、オブジェクトのサーフェスカラーを制御するシェーダはsurfaceコンテキスト用に記述します。ライトからの輝度を決めるシェーダはlightコンテキスト用に記述します。チャンネルデータを作成またはフィルタリングするVEXプログラムは、chopコンテキスト用に記述します。

コンテキストが変われば、利用可能な関数、ステートメント、グローバル変数も変わります。

VEXを使用できるコンテキストの概要は、VEXコンテキストを参照してください。

シェーディングコンテキスト(surface、displacement、lightなど)用に記述する場合、シェーディングコンテキスト固有の情報も読んでください。

ステートメント

VEXはC言語と同様の通常のステートメントをサポートしています。また、特定のコンテキスト内でのみ利用可能なilluminancegatherのループなどのシェーディング固有のステートメントもサポートしています。

組み込み関数

VEXには、組み込み関数のライブラリが豊富にあります。いくつかの関数は、特定のコンテキスト内でのみ利用可能です。

VEX関数を参照してください。

ユーザ定義関数

関数はC言語と同様に定義します: 戻りのタイプ、関数名、括弧で括った引数のリスト、その後にコードブロックを指定します。

同じタイプの引数は、タイプを再度宣言することなくカンマ(,)区切りのリスト内に宣言することができます。他のタイプの引数はセミコロン(;)で区切らなければなりません。

int test(int a, b; string c) {
    if (a > b) {
        printf(c);
    }
}

同じ名前で引数の数やタイプが異なる、または戻りのタイプが異なる関数を オーバーロード することができます。

オプションのfunctionキーワードを使って関数定義を導入することで、タイプの曖昧さを回避することができます。

function int test(int a, b; string c) {
    if (a > b) {
        printf(c);
    }
}
void print(basis b) { 
    printf("basis: { i: %s, j: %s, k: %s }\n", b.i, b.j, b.k); 
} 
void print(matrix m) { 
    printf("matrix: %s\n", m); 
} 
void print(bases b) { 
    printf("bases <%s> {\n", b.description); 
    printf("  "); print(b.m); 
    printf("  "); print(b.n); 
    printf("  "); print(b.o); 
    printf("}\n"); 
} 

basis rotate(basis b; vector axis; float amount) { 
    matrix m = 1; 
    rotate(m, amount, axis); 
    basis result = b; 
    result.i *= m; 
    result.j *= m; 
    result.k *= m; 
    return result; 
} 
void rotate(basis b; vector axis; float amount) { 
    b = rotate(b, axis, amount); 
} 

メモ

  • ユーザ関数は、参照される前に宣言しなければなりません。

  • 関数は、コンパイラで自動的にインラインされます。そのため、 再帰関数は動作しません 。再帰アルゴリズムを書くには、代わりにシェーダコールを使います。

  • RenderManシェーディング言語と同様に、ユーザ関数に渡すパラメータは常に 参照 です。つまり、ユーザ関数内の修正は、それをコールした関数の変数に影響があります。

  • ユーザ関数の数に制限はありません。

  • 関数内に2つ以上のreturnステートメントを記述することができます。

  • 直接グローバル変数にアクセスすることができます(RenderManシェーディング言語と違い、externでグローバル変数を宣言する必要はありません)。 しかし、グローバル変数にアクセスしないことを推奨します。なぜなら、これは、あなたが作成した関数が1つのコンテキスト(そのグローバル変数が存在するコンテキスト)内でのみしか動作しなくなるからです。 代わりに、グローバル変数をパラメータとして関数に渡してください。

メイン(コンテキスト)関数

VEXプログラムには、戻りのタイプがコンテキストの名前である関数を1つ含めなければなりません。その関数が、Mantraでコールされるプログラムのメイン関数となります。コンパイラはファイル毎に1つのコンテキスト関数を必要とします。

この関数には、必要な情報の計算とグローバル変数の修正の作業(組み込み関数またはユーザ定義関数をコールする)をさせるべきです。コンテキスト関数から値を返すreturnステートメントは使わないでください。各コンテキストで利用可能なグローバル変数用に関しては、特定のコンテキストのページを参照してください。

コンテキスト関数への引数は、もしあれば、プログラム用のユーザインターフェースになります。例えば、VEXプログラムを参照するシェーディングノードのパラメータがそれです。

コンテキスト関数のパラメータと同じ名前のジオメトリアトリビュートが存在すれば、そのアトリビュートは、パラメータの値を上書きします。これによって、ジオメトリ上のアトリビュートをペイントすることでVEXコードを制御することができます。

surface
noise_surf(vector clr = {1,1,1}; float frequency = 1;
           export vector nml = {0,0,0})
{
    Cf = clr * (float(noise(frequency * P)) + 0.5) * diffuse(normalize(N));
    nml = normalize(N)*0.5 + 0.5;
}

Note

コンテキスト関数へのパラメータは、VEXで特別な方法によって扱われます。 パラメータと同じ名前のジオメトリアトリビュートを使ってパラメータの値を上書きすることができます。 この特別な場合を除いては、パラメータはシェーダのスコープ内では、“const”とみなされるはずです。 つまり、パラメータの値を修正することは不当です。 コンパイラは、これが起きるとエラーを発生します。

exportキーワードを使えば、元のジオメトリに対して修正したいパラメータのフラグを立てることができます。

ユーザインターフェースのプラグマ

Houdiniでこのプラグマから生成したユーザインターフェースは最小限(基本的には変数名とデータタイプに基づいた汎用テキストフィールド)のインターフェースです。例えば、frequencyパラメータなら、ある範囲内のスライダのUIに、clrはカラーピッカーUIとして指定したい場合があります。これらのことがユーザインターフェースコンパイラのプラグマを使うことで可能です。

#pragma opname        noise_surf
#pragma oplabel        "Noisy Surface"

#pragma label    clr            "Color"
#pragma label    frequency    "Frequency"

#pragma hint    clr            color
#pragma range    frequency    0.1 10

surface noise_surf(vector clr = {1,1,1}; float frequency = 1;
           export vector nml = {0,0,0})
{
    Cf = clr * (float(noise(frequency * P)) + 0.5) * diffuse(normalize(N));
    nml = normalize(N)*0.5 + 0.5;
}

演算子

VEXには標準のC言語の演算子をC言語と同じ優先順位で持っています。以下にC言語と違う箇所を説明します。

乗算は、2つのベクトルまたはポイント間で定義されます。乗算は、エレメント対エレメントで計算を実行します(内積や外積とは違います。crossdotを参照してください)。

ほとんどの演算子が非スカラーデータタイプ用に定義されています(つまり、ベクトルにマトリックスを乗算すると、ベクトルがマトリックスでトランスフォームします)。

2つの異なるタイプを演算子で組み合わせた時の曖昧な状況では、結果は、2番目(右側)の値のタイプになります。例えば、整数とベクトルを以下の順番で加算すると、結果はベクトルになります。

int + vector = vector

ドット演算子

ドット演算子(.)を使えば、vectormatrixstructの個々のコンポーネントを参照することができます。

ベクトルでは、コンポーネントの名前が固定されています。

  • .x または .uvector2の1番目のエレメントを参照します。

  • .x または .rvectorvector4の1番目のエレメントを参照します。

  • .y または .vvector2の2番目のエレメントを参照します。

  • .y または .gvectorvector4の2番目のエレメントを参照します。

  • .z または .bvectorvector4の3番目のエレメントを参照します。

  • .w または .a はvector4の4番目のエレメントを参照します。

u,v/x,y,z/r,g,bの文字の選択は任意です。 ベクトルはポイントやカラーのどちらかを保持するわけではないので、どちらの文字でも構いません。

マトリックスに関しては、1組の文字を使用することができます。

  • .xx は、[0][0]エレメントを参照します。

  • .zz は、[2][2]エレメントを参照します。

  • .ax は、[3][0]エレメントを参照します。

さらに、ドット演算子を使用することで、ベクトルのコンポーネントを“Swizzle(かき混ぜる)”させることができます。例えば、

  • v.zyxは、set(v.z, v.y, v.x)と等価です。

  • v4.bgabは、set(v4.b, v4.g, v4.a, v4.b)と等価です。

Note

Swizzle(かき混ぜる)させたベクトルに値を割り当てることはできません。 あくまで読み込み専用です。 そのため、v.zyx = bはできず、反対にv = b.zyxはできます。

比較

比較演算子(==, !=, <, <=, >, >=)は、その演算子の左側と右側を比較します。この演算子は、stringfloatintのタイプだけに対応します。 演算結果は、intタイプです。

文字列マッチング演算子(~=)は、演算子の両側に文字列がある場合にのみ定義することができ、両側の2つの値を使ってmatch関数をコールすることと等価です。

論理演算子(&&, ||, !)とビット演算子(& |, ^, ~)はintのタイプのみに対応します。

優先順位テーブル

テーブルで上にある演算子ほど優先度が高いです。

優先度

演算子

関連性

説明

15

()

LtR

関数コール、エクスプレッションのグループ化、構造体メンバー

13

!

LtR

論理否

13

~

LtR

1の補数

13

+

LtR

単項のプラス(例えば、+5)

13

-

LtR

単項のマイナス(例えば、-5)

13

++

LtR

インクリメント(例えば、x++)

13

--

LtR

デクリメント(例えば、x--)

13

(type)

LtR

型変換(例えば、(int)x)

12

*

LtR

乗算

12

/

LtR

割算

12

%

LtR

剰余

11

+

LtR

加算

11

-

LtR

減算

10

<

LtR

未満

10

>

LtR

より大きい

10

<=

LtR

以下

10

>=

LtR

以上

9

==

LtR

等号

9

!=

LtR

不等号

9

~=

LtR

文字列マッチ

8

&

LtR

ビット演算のAND

7

^

LtR

ビット演算のXOR

6

|

LtR

ビット演算のOR

5

&&

LtR

論理積

4

||

LtR

論理和

3

?:

LtR

条件演算子(例えば、x ? "true" : "false")

2

= += -= *= /= %= &= |= ^=

RtL

変数割当て

1

,

LtR

引数の区切り文字

オペレータタイプの相互作用

  • floatintを演算した場合、その結果のタイプは、その演算子の左側のタイプになります。つまり、float * int = floatint * float = intです。

  • ベクトルをスカラー値(intまたはfloat)で加算、乗算、割算、減算した場合、VEXは、コンポーネント毎に演算が適用された同じサイズのベクトルを返します。例:

    {1.0, 2.0, 3.0} * 2.0 == {2.0, 4.0, 6.0}
    
  • 異なるサイズのベクトルを加算、乗算、割算、減算した場合、VEXは、大きいサイズのベクトルを返します。この演算はコンポーネント毎に適用されます。

    重要: 小さい方のベクトルの“足りない”コンポーネントは、{0.0, 0.0, 0.0, 1.0}のように充たされます。

    {1.0, 2.0, 3.0} * {2.0, 3.0, 4.0, 5.0} == {2.0, 6.0, 12.0, 5.0}
    

    これは、想定外の結果を招くことがあります。例:

    // vector2の3番目の要素は0として処理されますが、
    // 4番目の要素は1.0として処理されています。
    {1.0, 2.0} + {1.0, 2.0, 3.0, 4.0} == {2.0, 4.0, 3.0, 5.0}
    

    異なるサイズのベクトルを組み合わせる場合、それらのコンポーネントを抜き出して、それぞれのコンポーネントを“手動”で演算させることで、想定内の結果を得ることができます。

データタイプ

Warning

デフォルトでは、VEXは32ビットの整数を使用します。もしAttribCast SOPを使ってジオメトリアトリビュートを64ビットに変換した場合、VEXコード内のアトリビュートを操作する時にはVEXは静かに余分なビットを破棄してしまいます。

VEXエンジンは32ビットモードまたは64ビットモードのどちらかで走ります。 32ビットモードでは、すべての浮動小数点、ベクトル、整数が32ビットです。 64ビットモードでは、それらが64ビットになります。 数学で精度を混在させることができるようにdoubleタイプもlongタイプもありません。

アンダースコアを使用すれば、長い番号を区切ることができます。

タイプ

定義

サンプル

int

整数値

21, -3, 0x31, 0b1001, 0212, 1_000_000

float

浮動小数点のスカラー値

21.3, -3.2, 1.0, 0.000_000_1

vector2

2つの浮動小数点値。これらの値を使えば、テクスチャ座標(しかし、通常ではHoudiniはベクトルを使用します!)や複素数を表現することができます。

{0,0}, {0.3,0.5}

vector

3つの浮動小数点値。これらの値は、位置、方向、法線、カラー(RGB または HSV)を表現するために使用することができます。

{0,0,0}, {0.3,0.5,-0.5}

vector4

4つの浮動小数点値。これらの値は、同次座標の位置、アルファ付きのカラー(RGBA)を表現するために使用することができます。これはクォータニオンの表現でよく使用されます。VEXでのクォータニオンはw/x/y/z順ではなくx/y/z/w順で配置されています。これは、同次座標を使ったクォータニオンやポジションのどちらにも当てはまります。

{0,0,0,1}, {0.3,0.5,-0.5,0.2}

array

値のリスト。詳細は、配列を参照してください。

{ 1, 2, 3, 4, 5, 6, 7, 8 }

struct

名前を付けた値の固定セット。詳細は、構造体を参照してください。

matrix2

4個の浮動小数点値。これらの値は2D回転マトリックスを表現します。

{ {1,0}, {0,1} }

matrix3

9個の浮動小数点値。これらの値は3D回転マトリックスまたは2Dトランスフォーメーションマトリックスを表現します。

{ {1,0,0}, {0,1,0}, {0,0,1} }

matrix

16個の浮動小数点値。これらの値は3Dトランスフォーメーションマトリックスを表現します。

{ {1,0,0,0}, {0,1,0,0}, {0,0,1,0}, {0,0,0,1} }

string

文字列。詳細は、文字列を参照してください。

"hello world"

dict

stringを他のVEXデータタイプにマップさせた辞書。詳細は、辞書を参照してください。

bsdf

Bidirectional Scattering Distribution Function (双方向散乱分布関数)。BSDFの情報は、PBRシェーダの記述を参照してください。

構造体

Houdini12では、struct(構造体)キーワードを使って新しく構造化されたタイプを定義することができます。

C+11のメンバー初期化と同様に、構造体定義内でメンバーデータにデフォルト値を割り当てることができます。

2つの暗黙コンストラクタ関数が構造体毎に作成されます。 1つ目の関数は、構造体内に宣言された初期化引数の順番で、それらの引数を受け取ります。2番目の関数は、引数を受け取りませんが、すべてのメンバーをデフォルト値に設定します。

#include <math.h> 

struct basis { 
    vector i, j, k; 
} 

struct bases { 
    basis m, n, o; 
    string description; 
} 

struct values {
    int uninitialized;        // メンバーデータが初期化されていません
    int        ival = 3;
    float fval = 3.14;
    float aval[] = { 1, 2, 3, 4.5 };
}

basis rotate(basis b; vector axis; float amount) { 
    matrix m = 1; 
    rotate(m, amount, axis); 
    basis result = b; 
    result.i *= m; 
    result.j *= m; 
    result.k *= m; 
    return result; 
}

// 構造体変数を宣言します
basis b0;        // デフォルト値(この場合では0)を使って初期化します
basis b1 = basis({1,0,0}, {0,1,0}, {0,0,1});        // コンストラクタを使って初期化します
basis b2 = { {1,0,0}, {0,1,0}, {0,0,1} };         // 明示的な構造体として初期化します

// M_PIやPIを使用することができます
b1 = rotate(b1, {0,0,1}, M_PI/6);

Note

構造体はソースファイルで使用する前に定義しなければなりません。

構造体関数

構造体内部で関数を定義することで、コードを整理し、オブジェクト指向プログラミングの制限された形式を可能にすることができます。

  • 構造体関数内では、thisを使って構造体インスタンスを参照することができます。

  • 構造体関数内では、構造体フィールドを変数であるかのように名前で参照することができます(例えば、basisthis.basisのショートカットです)。

  • ->矢印オペレータを使って構造体インスタンスの構造体関数をコールすることができます(例えば、sampler->sample())。 this->method()を使って構造体の他のメソッドをコールすることができるように構造体関数内部に記述します。

struct randsampler {
    // フィールド
    int seed;

    // メソッド
    float sample()
    {
    // 構造体関数は名前でフィールドを参照することができます。
    return random(seed++);
    }
} 

cvex shader()
{
    randsampler sampler = randsampler(11);
    for (int i = 0; i < 10; i++)
    {
    // ->を使って構造体インスタンスのメソッドをコールします。
    printf("%f\n", sampler->sample());
    }
}

Mantra固有のタイプ

Mantraには、事前定義された構造体タイプがいくつかあり、シェーディング特有の関数で使用します。

light

Mantraシェーディングコンテキストのみで定義されています。これは、光源のハンドルを表現した構造体です。 この構造体には、メソッドがあります:

  • illuminate(…) 光源のvm_illumshaderプロパティにバインドされたVEXサーフェスシェーダを呼び出します。

IFDで、ray_property light illumshader diffuselightingray_property light illumshader mislighting misbias 1.000000のような行を見ることがあるでしょう。

これらのステートメントは、ライトオブジェクトに対してilluminate()メソッドがコールされた時に呼び出されるシェーダを定義しています。

material

Mantraシェーディングコンテキストのみで定義されています。これは、オブジェクトに割り当てられたマテリアルを表現した不透明度の構造体です。

lpeaccumulator

Mantraシェーディングコンテキストのみで定義されています。これは、Light Path Expressionsのアキュムレータ(累算器)を表現した構造体です。 この構造体にはメソッドがあります:

  • begin() - アキュムレータを構築して初期化します。

  • end() - 後始末をして破棄します。

  • move(string eventtype; string scattertype; string tag, string bsdflabel) - 現行イベントに基づいて内部状態を修正します。空っぽの文字列を渡すと、'any(どのイベントも可)'と想定されます。

  • pushstate() - 内部状態をスタックにプッシュします。

  • popstate() - 内部状態をスタックからポップします。move()の“アンドゥー”にはpushstate()を使用します。

  • int matches() - 現行の内部状態が、ユーザが定義したライトパスエクスプレッションのどれかに合致すると、ゼロ以外の値が返されます。

  • accum(vector color, …) - 入力カラーを中間バッファに累積させます。さらに、オプションの接頭辞を指定することで、LPE画像平面で宣言された接頭辞と比較されます。累積させるには、すべての接頭辞が合致しなければなりません。

  • flush(vector multiplier) - 中間バッファとmultiplierを乗算し、その結果を画像平面に加算します。中間バッファは、variance(偏差)アンチエイリアスを可能にするために存在しています。(例えば、multiplierは1/number_of_samplesです。)

  • int getid() - lpeaccumulatorに割り当てられている整数IDを返します。

  • lpeaccumulator getlpeaccumulator(int id) - IDからlpeaccumulatorを返します。getid()と併用することで、シェーダ境界を越えてlpeaccumulatorを渡すことができます。

キャスト(型変換)

変数の型変換

これは、C++やJavaの型変換と同様です。あるタイプの値を他のタイプの値に変換します(例えば、intからfloat)。

これは以下のような場合に必要になる時があります:

int a, b;
float c;
c = a / b;

このサンプルでは、コンパイラは整数の割算をします(タイプの計算を参照してください)。そうではなくて、浮動小数点の割算をしたかったので あれば、明示的にabをfloatに変換します:

int a, b;
float c;
c = (float)a / (float)b;

これで型変換が行なわれます。これにより、コードのパフォーマンス性能が問題になる場合があります。

関数のキャスト(型変換)

VEXは、引数のタイプだけでなく戻り値のタイプに基づいて関数を割り当てます(C++やJavaのように)。 同じ引数のタイプでも戻り値のタイプが異なる関数を明確にコールするためには、 関数をキャスト(型変換) します。

例えば、noise関数は異なるパラメータタイプを受け取り、異なるタイプを返すことができます。つまり、noiseはfloatまたはvectorのどちらかを返すことができます。

コード:

float n;
n = noise(noise(P));

VEXはfloat noise(vector)またはvector noise(vector)のどちらかに割り当てることができます。

関数コールをキャスト(型変換)するには、以下のようにtypename( ... )で関数を囲みます:

n = noise( vector( noise(P) ) );

これは関数コールのように見えますが、内部で関数コールを明示しているだけで、パフォーマンスのオーバーヘッドがありません。

関数のキャスト(型変換)は、関数コールを直接的に指定したタイプの変数に割り当てた時に暗黙的に行なわれます。 以下のエクスプレッションは、どちらも同じで、関数のキャスト(型変換)はコードをより簡潔にするために省略することがあります:

vector n = vector( noise(P) );        // 不要な関数キャスト(型変換)
vector n = noise(P);

Note

VEXが、あなたがコールしようとしている関数の引数やタイプを決めることができない時、曖昧なエラーとして候補となる関数を出力します。 適切な戻り値を選択して、それが選択されるように関数キャスト(型変換)を追加してください。

関数のキャスト(型変換)が任意のタイプの変換を生成しない(単にコールする関数を選択するだけ)ので、それを使ったパフォーマンス低下はありません。 パフォーマンスの低下を避けるためにできるだけ変数のキャスト(型変換)よりも関数のキャスト(型変換)を使う方が良いです。

コメント

VEXはC++スタイルのコメントを使用します:

  • 1行コメントは頭に//を付けます。

  • 自由形式のコメントは、/*で始めて、*/で終えます。

予約語

break, bsdf, char, color, const, continue, do, dict, else, export, false, float, for, forpoints, foreach, gather, hpoint, if, illuminance, import, int, integer, matrix, matrix2, matrix3, normal, point, return, string, struct, true, typedef, union, vector, vector2, vector4, void, while

VEX

言語

次のステップ

リファレンス