この記事ではHoudini VEXでの構造体の利用について紹介します。
SideFX社ドキュメントにもありますように、VEXでは、C言語同様、構造体がサポートされています。
https://www.sidefx.com/ja/docs/houdini/vex/lang.html#structs
VEXを使ってシェーダを記述する際には、コード内に直接構造体を定義することが可能です。
但し、SOP上でAttribute Wrangleノードを用いてVEXを利用する際には、この方法は使えません。
この通り、VEXpression上で構造体を定義しようとすると、エラーが出力されてしまいます。
実は、VEXpression上のVEXは実体として、以下のように、スニペッド単位で関数としてネストされています。
void _obj_grid_object1_attribwrangle1_attribvop1_snippet1() { struct DATA { vector v1; } DATA data; data.v1={0,0,1}; @Cd=data.v1; }
VEXではC言語と異なり、関数内での構造体の定義(ローカルな構造体の定義)を行うことができません。
従って、VEXpression上で構造体を定義しようとしても、VEXはこのような構造体定義ができませんので、エラーとなってしまいます。
AttributeWangleノードから構造体定義を行うには、Outer Codeというパラメータを利用し、関数の外に定義する必要があります。Outer CodeパラメータはAttributeWangleノードの中にあります。
Outer Codeの編集を行うには、ノードを右クリックしてAllow Editing of Contentsをクリックしてノードの中に入ります。
VOPコンテキストに移動しますので、snippet1ノードを選択します。
この中にOuter Codeパラメータがありますので、以下のように入力します。
この状態でジオメトリ階層に戻り、VEXpression上の構造体定義文を削除してください。
エラーが解決され、構造体を利用したCdアトリビュートの変更の反映が確認できます。
Outer Codeというパラメータに記述されたコードは、スニペットの関数の外に記述されます。
よってAttribute Wrangleノードからでも、構造体を利用することができるようになります。
また、もうひとつAttribute Wrangleノードにて構造体を定義する方法として、ヘッダファイルで定義する方法があります。
以下のような構造体定義文をdata.hとして、$HIP/vex/include/フォルダ下に保存します。
struct DATA { vector v1; }
そして、Wrangle側で以下のような形でIncludeする方法でも構造体定義が可能です。
#include "data.h" DATA data; data.v1={0,0,1}; @Cd=data.v1;
これは、#includeなどのプリプロセッサが記述されている際は、
Houdini側で自動で命令文をスニペットの外に移動するような仕様になっている為です。
こちらの方法の場合、いちいちノードを編集して中に入る必要がなくなるのと、
Houdiniからではなく、直接外部エディタでヘッダファイルを修正できるなどのメリットがあります。
OuterCodeの手法は簡単かつ、一度きりしか使わないような定義文にしか向きませんので、
スニペットの外で定義を行う必要がある内容を複数のノードで利用する場合や、本格的に記述を行う際はヘッダファイルを推奨します。
構造体配列の利用について
構造体配列を利用する際には別途注意が必要です。
2019年9月現在、SideFX社のドキュメント上では、どの関数が構造体配列で利用可能かを参照することはできません。
実は、ドキュメント上で<type>で記述されている関数であっても、構造体配列が利用できない関数が存在します。
構造体配列が利用できる関数はbsdf型配列に対応している関数のみとなります。
構造体配列で利用可能な関数は、Houdini command line tools (hcmd.exe)上で下記コマンドを入力することで、確認することができます。
vcc -X cvex | findstr bsdf\[\]
Houdini17.5.360での実行結果は以下のようになります。
bsdf getcomp( bsdf[]; int ) int isvarying( bsdf[] ) int len( bsdf[] ) bsdf pop( bsdf[] &; int ) bsdf pop( bsdf[] & ) void push( bsdf[] &; bsdf ) void push( bsdf[] &; bsdf[] ) void resize( bsdf[] &; int ) bsdf[] select( int; bsdf[]; bsdf[] ) bsdf[] set( bsdf[] ) bsdf setcomp( bsdf[] &; bsdf; int ) void split_bsdf( bsdf[] &; bsdf; float[] &; int; int; float; float[] ) void split_bsdf( bsdf[] &; bsdf; float[] &; int; int; float ) void split_bsdf( bsdf[] &; bsdf; float[] &; int; int ) void split_bsdf( bsdf[] &; bsdf; float[] &; int ) void split_bsdf( bsdf[] &; bsdf; float[] & ) void upush( bsdf[] &; bsdf )
この一覧で表示されている関数が、VEXにおいて構造体配列が利用できる関数となります。
上記の通り、push()/pop()やlen()などは利用できるものの、append() ,removevalue()などは利用できませんのでご注意ください。