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_5

ジオメトリの一部に面法線を使用する

TD_1

Houdini

そのまま書き出すと、Houdiniのビュー上で面法線になっている箇所も、TouchDesignerで読み込んだ際に点法線となってしまいます。 houdini_td

そのため、Facetノードで頂点(Vertex)をポイント(Point)に変換し、Attribute Promoteノードを使って頂点(Vertex)の法線をポイント(Point)に転送しておく必要があります。

  1. Facetノードを接続し、Unique Pointsオンにします。 houdini_1
  2. Attribute Promoteノードを接続し、Original NameNOriginal ClassVertexNew ClassPointにします。 houdini_2

UV情報を書き出し、テクスチャマッピングを行う

TD_2

Houdini

DynamicRemeshingではテクスチャマッピング用のUVは書き出されないため、ColorテクスチャのRGチャンネルにUV情報を書き込むなどの工夫をする必要があります。

サンプルプロジェクトでは頂点アトリビュートのUVをAttribute RenameでCdとし、Attribute Promoteで頂点アトリビュートからポイントアトリビュートに転送しています。

  1. Attribute Renameノードを接続し、Vertexタブを選択、Attrubute From/Touv, Cdと設定します。 houdini_3
  2. Attribute Promoteノードを接続し、Original NameCdOriginal ClassVertexNew ClassPointにします。 houdini_4

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
}