TouchDesignerでHoudini VAT3.0を使用する 3 (Fluid)
2023-02-01
2023-02-01
TouchDesigner上で、HoudiniのLabs Vertex Animation Textures 3.0ノードから書き出したファイル群を使用し、Fluidアニメーションを実行する方法を解説します。
サンプルプロジェクト
https://github.com/yasuhirohoshino/TouchDesigner_VAT3.0/tree/main/VAT_3_Fluid
動作環境
- Windows 10 22H2
- Houdini 19.5.493
- SideFX Labs 19.5.493インストール済み
- TouchDesigner 2022.31030 (Commercial License)
Houdini側の設定
OBJ Network
内に、書き出したいGeometryを用意します。(各フレームで頂点数が違っていても問題ありません)ROP Network
内に、Labs Vertex Animation Textures
ノードを作成します。
Labs Vertex Animation Texturesの設定
基本設定
Mode/Target Engine
をDynamic Remeshing(Fluid)
,Custom
に変更します。Start/End
で書き出したいアニメーションの開始フレームと終了フレームを設定します。- ノード作成時には、Expressionによりタイムラインの開始フレームと終了フレームが自動で設定されています。
Input Geometry
に書き出したいジオメトリのパスを設定します。
Settings
Lookup Table Format
がNon-HDR(Any Format asRGBA8 in Engine)
,.png
になっていることを確認します。Texture Format
がHDR(EXR/TIFF as RGBA 16/32 in Engine)
,.exr
になっていることを確認します。- 必要であれば
Target Texture Width
を適宜変更します。
Export
- 必要であれば
Export Path
で書き出し先のフォルダ名を設定します。 Include
に、書き出したいデータがすべて含まれているか確認します。(もしColorデータが必要なければ、Geometry, Position(s), Rotation
に変更してColor以外を書き出すことなどもできます。)
Target Engine
Coordinate System
をY-X-Z Clockwise (Right-Handed Y-Up)
に変更します。1 Metre in Engine Units
を1
に設定します。
書き出し
Settings
に戻り、Render Pass
をFirst Pass(Geometry, Lookup Table, Data/Material)
に設定し、Render All
を押します。- 書き出しが完了したら、
Render Pass
をSecond Pass(Animation Textures)
に設定し、再度Render All
を押します。
書き出されたファイル群
ファイル名 | 説明 |
---|---|
geo/[AssetName]_mesh.fbx | VATテクスチャ参照用のUVが追加されたFBX |
tex/[AssetName]_lookup.exr | fbxのUVを、以下のテクスチャ群からデータを取り出すためのUVに変換するテクスチャ |
tex/[AssetName]_pos.exr | 頂点位置を格納したテクスチャ |
tex/[AssetName]_rot.exr | 各フレームでの法線の回転情報を格納したテクスチャ |
tex/[AssetName]_col.exr | 各フレームでの色情報のテクスチャ |
TouchDesigner
サンプルファイルを参照してください。
注意点
- 手動で
総フレーム数
を指定する必要があります。 - MAT OPで読み込むVATの各テクスチャは、
Extend U
,Extend V
をHold
にしてください。 - 書き出されたテクスチャを読み込む際の、
Movie File In
TOPのPre-Multiply RGB by Alpha
はOff
にしてください。
Vertex Shader
以下、サンプルプロジェクトで使用しているVertex Shaderを記載します。
out Vertex
{
vec4 color;
vec3 worldSpacePos;
vec3 worldSpaceNorm;
flat int cameraIndex;
} oVert;
// FBXのUVから各フレームでのVAT参照用UVに変換するためのルックアップテーブル
uniform sampler2D VAT_Table;
// 頂点位置のテクスチャ
uniform sampler2D VAT_Pos;
// 法線の回転用テクスチャ
uniform sampler2D VAT_Rot;
// 色のテクスチャ
uniform sampler2D VAT_Col;
// アニメーションのフレーム
uniform float currentFrame;
// アニメーションの総フレーム数
uniform int numOfFrames;
// ベクトルをクォータニオンで回転
vec3 rotateVectorByQuatenion(vec3 v, vec4 quat) {
vec3 m1 = v * quat.w;
vec3 c1 = cross(quat.xyz, v);
vec3 a1 = m1 + c1;
vec3 c2 = cross(quat.xyz, a1);
vec3 m2 = c2 * 2.0;
vec3 a2 = m2 + v;
return a2;
}
// Positionを取得
vec3 getVATPosition(vec2 uv) {
vec3 currentPos = texture(VAT_Pos, uv).xyz;
return currentPos;
}
// Colorを取得
vec4 getVATColor(vec2 uv) {
vec4 currentCol = texture(VAT_Col, uv);
return currentCol;
}
// Normalを取得
vec3 getVATNormal(vec2 uv) {
vec3 n = vec3(0.0, 1.0, 0.0);
vec4 currentQuat = texture(VAT_Rot, uv).xyzw;
vec3 currentNormal = rotateVectorByQuatenion(n, currentQuat);
return currentNormal;
}
void main()
{
vec3 position = vec3(0.0);
vec4 color = vec4(0.0);
vec3 normal = vec3(0.0, 1.0, 0.0);
vec3 tempUV = uv[0];
// 頂点がVAT参照用のUVを持っている場合
if(tempUV.x != 0.0 && tempUV.y != 0.0) {
// ピクセルをサンプリングする間隔を計算
float stride = 1.0 / numOfFrames;
// 現在のフレームを取得
float frame = floor(currentFrame);
// Lookup Tableからテクスチャ参照用のUVを作成
vec4 tableData = texture(VAT_Table, vec2(tempUV.x, tempUV.y - (frame / numOfFrames)));
vec2 currentUV = vec2((tableData.r + tableData.g / 255.0), 1.0 - (tableData.b + tableData.a / 255.0));
// 頂点位置を取得
position = getVATPosition(currentUV);
// 法線を取得
normal = getVATNormal(currentUV);
// 色を取得
color = getVATColor(currentUV);
}
// Positionを設定
vec4 worldSpacePos = TDDeform(position);
vec3 uvUnwrapCoord = TDInstanceTexCoord(TDUVUnwrapCoord());
gl_Position = TDWorldToProj(worldSpacePos, uvUnwrapCoord);
#ifndef TD_PICKING_ACTIVE
int cameraIndex = TDCameraIndex();
oVert.cameraIndex = cameraIndex;
oVert.worldSpacePos.xyz = worldSpacePos.xyz;
// 色を設定
oVert.color = TDInstanceColor(Cd * color);
// 法線を設定
vec3 worldSpaceNorm = normalize(TDDeformNorm(normal));
oVert.worldSpaceNorm.xyz = worldSpaceNorm;
#else // TD_PICKING_ACTIVE
TDWritePickingValues();
#endif // TD_PICKING_ACTIVE
}