TouchDesignerでHoudiniから書き出したVATを使用する


はじめに

この記事では、Houdiniから出力したVAT(Vertex Animation Texture)ファイル群をTouchDesignerで読み込み、再生する方法を解説します。

HoudiniのLabs Vertex Animation Textureノードには、Unreal EngineやUnityなど、ゲームエンジン向けに設定のプリセットが用意されていますが、今回はTouchDesigner側のアニメーション再生機能の実装ができるだけシンプルになるように、手動でパラメーターの設定を行い、書き出し作業を行います。
そのため、他のゲームエンジン用に書き出されたVATファイルをそのまま読み込んだり、本記事を元に作成したVATファイルを他の開発環境で読み込んでも、正しく動作しません。

なおTouchDesignerのVertex Shaderの実装方法は、Houdiniから書き出されたUE4のMaterial Blueprintを参考にしています。

検証環境

サンプルプロジェクト

以下の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ノードを作成します。

パラメーター設定

  1. User interface - Normalを設定
  2. Start/Endにアニメーションの開始フレーム、終了フレームを設定
  3. MethodSoft (Constant Topology)に設定
  4. SOP Pathで書き出したいSOPを選択
  5. Frame Rangeにアニメーションのフレーム数を設定
  6. Normalize data range to 0-1 spaceをオフにする
  7. 必要であれば、Target Texture Sizeを任意のサイズに変更
  8. Paths/Normalのチェックボックスをオンにする
  9. SOPが色情報を持っている場合、Paths/Colorのチェックボックスをオンにする
  10. Settings/Position CoordXYZ, +X +Y +Zに変更する
  11. Scale1に設定する
  12. 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から書き出し

パラメーター設定

  1. User interface - Normalを設定
  2. Start/Endにアニメーションの開始フレーム、終了フレームを設定
  3. MethodRigid (Rigid Body Topology)に設定
  4. SOP Pathで書き出したいSOPを選択
  5. Frame Rangeにアニメーションのフレーム数を設定
  6. Normalize data range to 0-1 spaceをオフにする
  7. 必要であれば、Target Texture Sizeを任意のサイズに変更
  8. SOPが色情報を持っている場合、Paths/Colorをオンにする
  9. Settings/Position CoordXYZ, +X +Y +Zに変更する
  10. Settings/Rotation Coord+X+Y+Z+Wに変更する
  11. Scale1に設定する
  12. 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から書き出し

パラメーター設定

  1. User interface - Normalを設定
  2. Start/Endにアニメーションの開始フレーム、終了フレームを設定
  3. MethodFluid (Changing Topology)に設定
  4. SOP Pathで書き出したいSOPを選択
  5. Frame Rangeにアニメーションのフレーム数を設定
  6. Normalize data range to 0-1 spaceをオフにする
  7. Packed normal into Position Alphaのチェックボックスをオフにする
  8. 必要であれば、Target Texture Sizeを任意のサイズに変更
  9. Target Prim Countにプリミティブの最大数を設定
  10. Paths/Normalのチェックボックスをオンにする
  11. SOPが色情報を持っている場合、Paths/Colorのチェックボックスをオンにする
  12. Settings/Position CoordXYZ, +X +Y +Zに変更する
  13. 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のアニメーションをきれいにする方法

などのトピックを考えています。