TouchDesignerでHoudini VAT3.0を使用する 5 (DynamicRemeshing応用)
2023-02-01
2023-02-01
VAT 3.0 DynamicRemeshing(Fluid)では、各フレームでトポロジーが異なるジオメトリのアニメーションを書き出すことが可能です。
これを応用し、多数の異なるジオメトリをVATファイル群として書き出し、TouchDesignerに読み込ませることもできます。
なお、VATの書き出しや読み込みについては、TouchDesignerでHoudini VAT3.0を使用する(Fluid)を参照してください。
サンプルプロジェクト
https://github.com/yasuhirohoshino/TouchDesigner_VAT3.0/tree/main/VAT_3_DynamicRemeshing
動作環境
- Windows 10 22H2
- Houdini 19.5.493
- SideFX Labs 19.5.493インストール済み
- TouchDesigner 2022.31030 (Commercial License)
1フレームごとに一つのオブジェクトを表示するようにする
indexアトリビュートを用意して書き出したいオブジェクトごとに値を設定しておき、Attribute Wrangle
ノードで、フレーム(@Frame
)の値に応じて不要なオブジェクトのPrimitiveを削除するようにします。
ジオメトリの一部に面法線を使用する
Houdini
そのまま書き出すと、Houdiniのビュー上で面法線になっている箇所も、TouchDesignerで読み込んだ際に点法線となってしまいます。
そのため、Facet
ノードで頂点(Vertex)をポイント(Point)に変換し、Attribute Promote
ノードを使って頂点(Vertex)の法線をポイント(Point)に転送しておく必要があります。
Facet
ノードを接続し、Unique Points
をオン
にします。Attribute Promote
ノードを接続し、Original Name
をN
、Original Class
をVertex
、New Class
をPoint
にします。
UV情報を書き出し、テクスチャマッピングを行う
Houdini
DynamicRemeshingではテクスチャマッピング用のUVは書き出されないため、ColorテクスチャのRGチャンネルにUV情報を書き込むなどの工夫をする必要があります。
サンプルプロジェクトでは頂点アトリビュートのUVをAttribute Rename
でCdとし、Attribute Promote
で頂点アトリビュートからポイントアトリビュートに転送しています。
Attribute Rename
ノードを接続し、Vertex
タブを選択、Attrubute From/To
にuv
,Cd
と設定します。Attribute Promote
ノードを接続し、Original Name
をCd
、Original Class
をVertex
、New Class
をPoint
にします。
TouchDesigner
なお、Normal Mapを使用する場合、TouchDesigner内のVertex Shaderでは、Rotテクスチャのクォータニオンを使用し、接線(Tangent)を作成する必要があります。
out Vertex
{
vec4 color;
mat3 tangentToWorld;
vec3 worldSpacePos;
vec2 texCoord0;
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 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;
}
// Tangentを作成
vec3 getVATTangent(vec2 uv) {
vec3 t = vec3(1.0, 0.0, 0.0);
vec4 currentQuat = texture(VAT_Rot, uv).xyzw;
vec3 currentTangent = rotateVectorByQuatenion(t, currentQuat);
return currentTangent;
}
void main()
{
vec3 position = vec3(0.0);
vec4 color = vec4(0.0);
vec3 normal = vec3(0.0, 1.0, 0.0);
vec3 tangent = vec3(1.0, 0.0, 0.0);
vec3 tempUV = uv[0];
vec3 newUV = vec3(0.0);
// 頂点がVAT参照用のUVを持っている場合
if(tempUV.x != 0.0 && tempUV.y != 0.0) {
// ピクセルをサンプリングする間隔を計算
float stride = 1.0 / numOfFrames;
// インスタンシングのカスタムアトリビュートをFrameとする
float frame = TDInstanceCustomAttrib0().x;
frame = mod(round(frame), numOfFrames);
// 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);
// 接線を取得
tangent = getVATTangent(currentUV);
// テクスチャマッピング用のUVをColorテクスチャから取得
newUV = getVATColor(currentUV).xyz;
}
// 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);
vec3 texcoord = TDInstanceTexCoord(newUV);
oVert.texCoord0.st = texcoord.st;
// 法線を設定
vec3 worldSpaceNorm = normalize(TDDeformNorm(normal));
// 接線を設定
vec3 worldSpaceTangent = normalize(TDDeformNorm(tangent.xyz));
// TBNMatrixを作成
oVert.tangentToWorld = TDCreateTBNMatrix(worldSpaceNorm, worldSpaceTangent, 1);
#else // TD_PICKING_ACTIVE
TDWritePickingValues();
#endif // TD_PICKING_ACTIVE
}