LoginSignup
5
2

More than 1 year has passed since last update.

TouchDesignerのVATでNormal Mapを使用する

Last updated at Posted at 2020-12-28

2023年2月現在最新のVAT3.0の使用方法については以下の記事をご覧ください。

はじめに

この記事は、過去作成した記事(TouchDesignerでHoudiniから書き出したVATを使用する)の続きです。
HoudiniからのVATファイル群の書き出し設定などは、上記の記事と同様です。

検証環境

サンプルプロジェクト

以下のURLからダウンロードしてください。
https://drive.google.com/file/d/1kchKxh3PpYGo8r71aJLTNSbax0fvW5IX/view?usp=sharing

実装

Soft (Constant Topology)

soft_Houdini.jpgsoft_substance.jpg

UV展開した人の3Dモデルに、mixamoでアニメーションをつけました。
Normal MapはSubstance Painterで作成してみました。

TouchDesignerオペレーター解説

soft_TD.jpg

サンプルプロジェクトVATExamples.toe内のVAT_Soft_NormalMapを参照してください。
SOPにTangentアトリビュートを追加するため、以下の手順で操作を行っています。

  1. Houdiniから書き出されたFBXをインポート
  2. Attribute Create SOPを作成し、FBX COMP内のSOPをインプットする(サンプルプロジェクトではSelect SOPFBX COMP内のSOPを取得しています。)
  3. Attribute Create SOPのパラメーターCompute Tangentオンにする

なお、今回もPhong MATGLSL MATに変換し、Vertex Shaderを改造してアニメーション再生機能を実装しています。

Vertex ShaderでTangentの再計算

初期位置でのNormalから現在のフレームのNormalへの回転を表すクォータニオンを作成し、Vertex ShaderでT.xyzを回転させます。

phong_VAT_Soft_NormalMap_vert
uniform float uBumpScale;
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;

in vec4 T;
out Vertex
{
	vec4 color;
	mat3 tangentToWorld;
	vec3 worldSpacePos;
	vec2 texCoord0;
	flat int cameraIndex;
} oVert;

// あるベクトルから別のベクトルへの回転を表すクォータニオンを作成
vec4 makeFromToRotation(vec3 from, vec3 to){
	vec4 q = vec4(0.0);
	vec3 a = cross(from, to);
	q.xyz = a;
	q.w = sqrt((pow(length(from), 2)) * pow(length(to), 2)) + dot(from, to);
	return q;
}

// ベクトルをクォータニオンで回転
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 newPos = P + texture(VAT_Pos, UV).xyz;
	
	// 法線を読み込み
	vec3 newNorm = texture(VAT_Norm, UV).xyz;
	
	// 初期フレームのNormalと、現在のフレームのNormalから、Tangentを回転させるためのクォータニオンを作成
	vec4 quat = makeFromToRotation(N, newNorm);
	
	// Tangentを回転
	vec3 newT = T.xyz + rotateVectorByQuatenion(T.xyz, quat);

	{
		vec3 texcoord = TDInstanceTexCoord(uv[0]);
		oVert.texCoord0.st = texcoord.st;
	}
	
	// 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(Cd);
	
	// Normalを設定
	vec3 worldSpaceNorm = normalize(TDDeformNorm(newNorm));
	
	// Tangentを設定
	vec3 worldSpaceTangent = TDDeformNorm(newT.xyz);
	worldSpaceTangent.xyz = normalize(worldSpaceTangent.xyz);
	oVert.tangentToWorld = TDCreateTBNMatrix(worldSpaceNorm, worldSpaceTangent, T.w);

#else // TD_PICKING_ACTIVE

	TDWritePickingValues();

#endif // TD_PICKING_ACTIVE
}

結果

soft_normalmap.gif

Rigid (Rigid Body Topology)

rigid_Houdini.jpg
rigid_substance.jpg

UV展開した3Dテキストに破砕アニメーションを付けました。

TouchDesignerオペレーター解説

rigid_TD.jpg

サンプルプロジェクトVATExamples.toe内のVAT_Rigid_NormalMapを参照してください。
Tangentアトリビュート追加の手順hあ、Softの時と同様です。

  1. Houdiniから書き出されたFBXをインポート
  2. Attribute Create SOPを作成し、FBX COMP内のSOPをインプットする(サンプルプロジェクトではSelect SOPFBX COMP内のSOPを取得しています。)
  3. Attribute Create SOPのパラメーターCompute Tangentオンにする

Vertex ShaderでTangentの再計算

Attribute Create SOPで追加したTアトリビュートを、Rotationテクスチャに格納されたクォータニオンを使い、Normalを回転させるときと同じようにVertex Shaderで回転させます。

phong_VAT_Rigid_NormalMap_vert
uniform float uBumpScale;
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;

// アニメーションの時間(0-1)
uniform float time;
// アニメーションの総フレーム数
uniform int numOfFrames;

// 中心点の最小値
uniform float pivot_Min;
// 中心点の最大値
uniform float pivot_Max;


in vec4 T;
out Vertex
{
	vec4 color;
	mat3 tangentToWorld;
	vec3 worldSpacePos;
	vec2 texCoord0;
	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;
	
	// 現在のフレームの中心点の移動を反映した、頂点の位置を計算
	vec3 newP = P - pivot;
	newP = rotateVectorByQuatenion(newP, pivot_Rot);
	newP -= pivot;
	newP += pivot_Pos;
	newP += P;
	
	// Normalを回転させる
	vec3 newN = rotateVectorByQuatenion(N, pivot_Rot) + N;
	
	// Tangentを回転させる
	vec3 newT = T.xyz + rotateVectorByQuatenion(T.xyz, pivot_Rot);

	{
		vec3 texcoord = TDInstanceTexCoord(uv[0]);
		oVert.texCoord0.st = texcoord.st;
	}

	// 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(vec4(1.0));
	
	// Normalを設定
	vec3 worldSpaceNorm = normalize(TDDeformNorm(newN));

	// Tangentを設定
	vec3 worldSpaceTangent = TDDeformNorm(newT.xyz);
	worldSpaceTangent.xyz = normalize(worldSpaceTangent.xyz);
	oVert.tangentToWorld = TDCreateTBNMatrix(worldSpaceNorm, worldSpaceTangent, T.w);

#else // TD_PICKING_ACTIVE

	TDWritePickingValues();

#endif // TD_PICKING_ACTIVE
}

結果

rigid_normalmap.gif

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2