2023年2月現在最新のVAT3.0の使用方法については以下の記事をご覧ください。
はじめに
この記事では、Houdiniから出力したVAT(Vertex Animation Texture)ファイル群をTouchDesignerで読み込み、再生する方法を解説します。
HoudiniのLabs Vertex Animation Texture
ノードには、Unreal EngineやUnityなど、ゲームエンジン向けに設定のプリセットが用意されていますが、今回はTouchDesigner側のアニメーション再生機能の実装ができるだけシンプルになるように、手動でパラメーターの設定を行い、書き出し作業を行います。
そのため、他のゲームエンジン用に書き出されたVATファイルをそのまま読み込んだり、本記事を元に作成したVATファイルを他の開発環境で読み込んでも、正しく動作しません。
なおTouchDesignerのVertex Shaderの実装方法は、Houdiniから書き出されたUE4のMaterial Blueprintを参考にしています。
検証環境
- Windows 10 Pro 1909
- TouchDesigner 2020.27390 (Commercial License)
- Houdini Indie 18.5.408
- SideFX Labsインストール済み( https://www.sidefx.com/ja/products/sidefx-labs/sidefx-labs-tools/ )
サンプルプロジェクト
以下のURLからダウンロードしてください。
https://drive.google.com/file/d/1kchKxh3PpYGo8r71aJLTNSbax0fvW5IX/view?usp=sharing
実装
HoudiniのLabs Vertex Animation Textures
ノードのMethod
の設定によって、書き出されるファイルやデータが変わるので、TouchDesigner側の実装方法も変わってきます。
Houdini 18.5では、以下の4つのMethodが使用できます。
Method | 解説 |
---|---|
Soft (Constant Topology) | キャラクターアニメーションなど、各頂点の位置は変わるが頂点数は変わらず、エッジやプリミティブの構成も変化しないアニメーションを書き出す際に選択 |
Rigid (Rigid Body Topology) | 破砕アニメーションなど、変形しない複数のピースのアニメーションを書き出す際に選択 |
Fluid (Changing Topology) | 流体アニメーションなど、頂点の位置も頂点数も変わるアニメーションを書き出す際に選択 |
Sprite (Camera Facing Cards) | パーティクルアニメーションなどを書き出す際に選択 |
以下、Soft, Rigid, FluidのMethodごとに、書き出し設定とTouchDesignerでの実装方法を解説していきます。
Soft (Constant Topology)
Houdiniでアニメーション作成
人間の3Dモデルにmixamoでループする歩行アニメーションをつけました。
フレームによってポイントの色も変化させています。
Houdiniから書き出し
outにLabs vertex Animation Textures
ノードを作成します。
パラメーター設定
-
User interface - Normal
を設定 -
Start/End
にアニメーションの開始フレーム、終了フレームを設定 -
Method
をSoft (Constant Topology)
に設定 -
SOP Path
で書き出したいSOPを選択 -
Frame Range
にアニメーションのフレーム数を設定 -
Normalize data range to 0-1 space
をオフにする - 必要であれば、
Target Texture Size
を任意のサイズに変更 -
Paths/Normal
のチェックボックスをオンにする - SOPが色情報を持っている場合、
Paths/Color
のチェックボックスをオンにする -
Settings/Position Coord
をXYZ
,+X +Y +Z
に変更する -
Scale
を1
に設定する -
Render
を押して書き出し
書き出されたファイル群の解説
以下のファイルが出力されます。
Paths/Project
, Paths/Component
にチェックを入れ、任意のパスとファイル名で書き出すこともできます。
ファイル名 | 解説 |
---|---|
[Project]/meshes/[Component]_mesh.fbx | VATテクスチャ参照用のUVが追加されたFBX |
[Project]/textures/[Component]_pos.exr | 最初のフレームからの頂点位置の変化量を格納したテクスチャ |
[Project]/textures/[Component]_norm.exr | 各フレームでの法線を格納したテクスチャ |
[Project]/textures/[Component]_col.exr | 各フレームでの色情報のテクスチャ |
[Project]/materials/[Component]_data.json | アニメーション再生時に使用する、各種パラメーターのJson |
また、書き出されたFBXには、以下の頂点アトリビュートが追加されています
SOPアトリビュート名 | GLSLアトリビュート名 | 解説 |
---|---|---|
uv(3), uv(4) | UV[1].xy | VATテクスチャ座標 |
TouchDesignerでアニメーション再生機能の実装
書き出されたFBXとテクスチャを読み込み、マテリアルを作成します。
サンプルプロジェクト内のVAT_Soft_Simple
では、Phong MAT
のVertex Shaderに、アニメーション再生機能を追加しました。
Uniform変数numOfFrames
には、HoudiniのLabs vertex Animation Textures
ノードのFrame Range
の値を渡します。
// Phong_VAT_Soft
uniform vec4 uDiffuseColor;
uniform vec4 uAmbientColor;
uniform vec3 uSpecularColor;
uniform float uShininess;
uniform float uShadowStrength;
uniform vec3 uShadowColor;
// 頂点位置の変化量テクスチャ
uniform sampler2D VAT_Pos;
// 法線のテクスチャ
uniform sampler2D VAT_Norm;
// 色のテクスチャ
uniform sampler2D VAT_Col;
// アニメーションの時間(0-1)
uniform float time;
// アニメーションの総フレーム数
uniform int numOfFrames;
out Vertex
{
vec4 color;
vec3 worldSpacePos;
vec3 worldSpaceNorm;
flat int cameraIndex;
} oVert;
void main()
{
// 現在のフレームを計算
float frame = clamp(time, 0.0, 0.99999) * numOfFrames;
// VATテクスチャ読み込み用のUVを作成
float u = uv[1].x;
float v = uv[1].y - (floor(frame) / numOfFrames);
vec2 UV = vec2(u, v);
// 頂点の位置の変化量を読み込み、初期位置に加算
vec3 newPos = P + texture(VAT_Pos, UV).xyz;
// 色を読み込み
vec4 newCol = texture(VAT_Col, UV);
// 法線を読み込み
vec3 newNorm = texture(VAT_Norm, UV).xyz;
// Positionを設定
vec4 worldSpacePos = TDDeform(newPos);
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(newCol);
// 法線を設定
vec3 worldSpaceNorm = normalize(TDDeformNorm(newNorm));
oVert.worldSpaceNorm.xyz = worldSpaceNorm;
#else // TD_PICKING_ACTIVE
TDWritePickingValues();
#endif // TD_PICKING_ACTIVE
}
実行結果
Rigid (Rigid Body Topology)
Houdiniでアニメーション作成
Rigid Bodiesシェルフ内のRBD Fractured Objectをもとにして、Voronoi Fractureで分割したSOPから破砕アニメーションを作成しました。
Houdiniから書き出し
パラメーター設定
-
User interface - Normal
を設定 -
Start/End
にアニメーションの開始フレーム、終了フレームを設定 -
Method
をRigid (Rigid Body Topology)
に設定 -
SOP Path
で書き出したいSOPを選択 -
Frame Range
にアニメーションのフレーム数を設定 -
Normalize data range to 0-1 space
をオフにする - 必要であれば、
Target Texture Size
を任意のサイズに変更 - SOPが色情報を持っている場合、
Paths/Color
をオンにする -
Settings/Position Coord
をXYZ
,+X +Y +Z
に変更する -
Settings/Rotation Coord
を+X+Y+Z+W
に変更する -
Scale
を1
に設定する -
Render
を押して書き出し
書き出されたファイル群の解説
以下のファイルが出力されます
ファイル名 | 解説 |
---|---|
[Project]/meshes/[Component]_mesh.fbx | ピースごとの中心点を格納したCdアトリビュート, VATテクスチャ参照用のUVアトリビュートが追加されたFBX |
[Project]/textures/[Component]_pos.exr | 中心点の位置を格納したテクスチャ |
[Project]/textures/[Component]_rot.exr | 中心点の回転(クォータニオン)を格納したテクスチャ |
[Project]/textures/[Component]_col.exr | 色情報のテクスチャ |
[Project]/materials/[Component]_data.json | アニメーション再生時に使用する、各種パラメーターのJson |
書き出されたFBXには、以下の頂点アトリビュートが追加されています
SOPアトリビュート名 | GLSLアトリビュート名 | 解説 |
---|---|---|
Cd(0), Cd(1), Cd(2) | Cd.xyz | ピースごとの中心点の初期位置 |
uv(3), uv(4) | UV[1].xy | VATテクスチャ座標 |
TouchDesignerでアニメーション再生機能の実装
サンプルプロジェクト内のVAT_Rigid
を参照してください。
注意点としては、Houdiniからの書き出し時にNormalize data range to 0-1 space
をオフにしていても、FBXのCdアトリビュートに含まれるピースの中心点の位置情報は0-1に正規化されてしまうため、HoudiniのVATノードのPivot Min / Max
の値をUniform変数で渡し、シェーダー内で初期フレームの中心点を計算する必要があります。
// Phong_VAT_Rigid
uniform vec4 uDiffuseColor;
uniform vec4 uAmbientColor;
uniform vec3 uSpecularColor;
uniform float uShininess;
uniform float uShadowStrength;
uniform vec3 uShadowColor;
// ピースの中心点の位置情報テクスチャ
uniform sampler2D VAT_Pos;
// 回転情報のテクスチャ
uniform sampler2D VAT_Rot;
// 色のテクスチャ
uniform sampler2D VAT_Col;
// アニメーションの時間(0-1)
uniform float time;
// アニメーションの総フレーム数
uniform int numOfFrames;
// 中心点の最小値
uniform float pivot_Min;
// 中心点の最大値
uniform float pivot_Max;
out Vertex
{
vec4 color;
vec3 worldSpacePos;
vec3 worldSpaceNorm;
flat int cameraIndex;
} oVert;
// ベクトルをクォータニオンで回転
vec3 rotateVectorByQuatenion(vec3 v, vec4 quat) {
vec3 c1 = cross(quat.xyz, v.xyz);
vec3 m1 = v.xyz * quat.w;
vec3 a1 = c1 + m1;
vec3 c2 = cross(quat.xyz, a1);
vec3 m2 = c2 * 2.0;
return m2;
}
void main()
{
// 現在のフレームを計算
float frame = clamp(time, 0.0, 0.99999) * numOfFrames;
// VATテクスチャ読み込み用のUVを作成
float u = uv[1].x;
float v = uv[1].y - (floor(frame) / numOfFrames);
vec2 UV = vec2(u, v);
// 初期フレームでの中心点を計算
vec3 pivot = Cd.rgb;
pivot *= (pivot_Max - pivot_Min);
pivot += pivot_Min;
// テクスチャからデータを取得
vec3 pivot_Pos = texture(VAT_Pos, UV).xyz;
vec4 pivot_Rot = texture(VAT_Rot, UV).xyzw;
vec4 color = texture(VAT_Col, UV);
// 現在のフレームの中心点の移動を反映した、頂点の位置を計算
vec3 newP = P - pivot;
newP = rotateVectorByQuatenion(newP, pivot_Rot);
newP -= pivot;
newP += pivot_Pos;
newP += P;
// 法線を回転させる
vec3 newN = rotateVectorByQuatenion(N, pivot_Rot) + N;
// Positionを設定
vec4 worldSpacePos = TDDeform(newP);
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(color);
// 法線を設定
vec3 worldSpaceNorm = normalize(TDDeformNorm(newN));
oVert.worldSpaceNorm.xyz = worldSpaceNorm;
#else // TD_PICKING_ACTIVE
TDWritePickingValues();
#endif // TD_PICKING_ACTIVE
}
実行結果
Fluid (Changing Topology)
Houdiniでアニメーション作成
流体シミュレーションをFLIPで作成しました。
Houdiniから書き出し
パラメーター設定
-
User interface - Normal
を設定 -
Start/End
にアニメーションの開始フレーム、終了フレームを設定 -
Method
をFluid (Changing Topology)
に設定 -
SOP Path
で書き出したいSOPを選択 -
Frame Range
にアニメーションのフレーム数を設定 -
Normalize data range to 0-1 space
をオフにする -
Packed normal into Position Alpha
のチェックボックスをオフにする - 必要であれば、
Target Texture Size
を任意のサイズに変更 -
Target Prim Count
にプリミティブの最大数を設定 -
Paths/Normal
のチェックボックスをオンにする - SOPが色情報を持っている場合、
Paths/Color
のチェックボックスをオンにする -
Settings/Position Coord
をXYZ
,+X +Y +Z
に変更する -
Render
を押して書き出し
書き出されたファイル群の解説
以下のファイルが出力されます
ファイル名 | 解説 |
---|---|
[Project]/meshes/[Component]_mesh.fbx | VATテクスチャ参照用のUVが追加されたFBX |
[Project]/textures/[Component]_pos.exr | 頂点の位置を格納したテクスチャ |
[Project]/textures/[Component]_norm.exr | 法線を格納したテクスチャ |
[Project]/textures/[Component]_col.exr | 色情報のテクスチャ |
[Project]/materials/[Component]_data.json | アニメーション再生時に使用する、各種パラメーターのJson |
書き出されたFBXには、以下の頂点アトリビュートが追加されています
SOPアトリビュート名 | GLSLアトリビュート名 | 解説 |
---|---|---|
uv(0), uv(1) | UV[0].xy | VATテクスチャ座標 |
TouchDesignerでアニメーション再生機能の実装
サンプルプロジェクト内のVAT_Fluid
を参照してください。
Softの時と大体同じですが、VATテクスチャの参照に使うUVが、uv[1]ではなくuv[0]になっています。
(なお、サンプルではHoudiniから色情報を書き出してはおらず、Vertex Shader内で単色を設定しています)
// Phong_VAT_Fluid
uniform vec4 uDiffuseColor;
uniform vec4 uAmbientColor;
uniform vec3 uSpecularColor;
uniform float uShininess;
uniform float uShadowStrength;
uniform vec3 uShadowColor;
// 頂点位置の変化量テクスチャ
uniform sampler2D VAT_Pos;
// 法線のテクスチャ
uniform sampler2D VAT_Norm;
// アニメーションの時間(0-1)
uniform float time;
// アニメーションの総フレーム数
uniform int numOfFrames;
out Vertex
{
vec4 color;
vec3 worldSpacePos;
vec3 worldSpaceNorm;
flat int cameraIndex;
} oVert;
void main()
{
// 現在のフレームを計算
float frame = clamp(time, 0.0, 0.99999) * numOfFrames;
// VATテクスチャ読み込み用のUVを作成
float u = uv[0].x;
float v = uv[0].y - (floor(frame) / numOfFrames);
vec2 UV = vec2(u, v);
// 頂点の位置を読み込み
vec3 newPos = texture(VAT_Pos, UV).xyz;
// 法線を読み込み
vec3 newNorm = texture(VAT_Norm, UV).xyz;
// Positionを設定
vec4 worldSpacePos = TDDeform(newPos);
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(vec4(0.5, 0.75, 1.0, 1.0));
// 法線を設定
vec3 worldSpaceNorm = normalize(TDDeformNorm(newNorm));
oVert.worldSpaceNorm.xyz = worldSpaceNorm;
#else // TD_PICKING_ACTIVE
TDWritePickingValues();
#endif // TD_PICKING_ACTIVE
}
実行結果
おわりに
後日、応用編を書く予定です。
- 法線マップを使用するための方法(Tangentアトリビュートを作成する方法)
- 破砕シミュレーション以外のRigidの使い方
- テクスチャのサイズを減らす方法(法線情報をPositionのアルファに書き込み、TD側で読み出す方法)
- Fluidのアニメーションをきれいにする方法
などのトピックを考えています。