始めに
野球ゲームの観客だったり、最近ではVtuberのライブの観客だったりで多くのNPCを表示させてかつアニメーションもさせています。
どうやれば実装できるかと思っているとき、VAT(Vertex Animation Texture)という技術を知りました。
このVATを使うことでUnityのAnimatorよりも軽量にアニメーションをさせることが出来ます。
ということで、今回はVATについてまとめていこうと思います。
VAT(Vertex Animation Texture)
Vertex Animation Textureとはモデルの頂点座標や回転の情報が書き込まれたテクスチャを指します。このテクスチャを使用することでモデルにアニメーションをさせることが出来ます。
Houdiniの記事にはなりますが、こちらが参考になりました。
Animation Texture Baker
今回は0から作るのではなくgithubにあるプロジェクト「Animation Texture Baker」を使ってVATをしていこうと思います。
シェーダーでアニメーションをさせるまでの手順
先ほどのgithubプロジェクトを使っていきます。
モデルの準備
モデルは Assets/Horse/Model/Horse を使用します。 HorseをHierarchyに追加します。
コンポーネントの設定
次にHorse用の AnimatorController を作成して、Horseに元からあるAnimatorコンポーネントのControllerに設定します。作成したAnimatorControllerは何も設定しなくて大丈夫です。
次に Animation Clip Texture Bakerコンポーネントを追加して Assets/AnimationBaker/Shaders に入っている
Info Tex GenにMeshInfoTextureGen.computeを、Play Shaderに2つのshaderファイルのどちらかを設定します。
Clipsには実行したいアニメーションを追加していきます。今回は Assets/Horse/Animation にあるHorse_Idle、Horse_Run、Horse_Walkを追加します。
VATの作成
Animation Clip Texture Bakerを右クリックするとメニューの一番下にbake textureがあります。 こちらを選択することでVATとVATを使用したPrefabが生成されます。
また、作成されたPrefabファイルは Assets/BakedAnimationTex に保存されています。
AssetStoreにあるモデルで試す
次にこちらのゾンビのモデルで試してみます。 手順は先ほどと同じです。
結果はモデルがx軸に-90度回転したものが出来てしまいました。
続いて次のロボットのモデルで試してみます。 RobotのRigをHumanoidに設定して同様の手順を行います。
先ほどとは異なり、回転があっていました。
回転オフセットを追加する
いくつか試している際にモデルの回転があっていない現象がたびたびありました。
なのでVATを作成する際に回転のオフセットを設定できるよう改良してみました。
やっていることは単純で頂点座標テクスチャの値に回転行列をかける処理を追加しただけです。
// AnimationClipTextureBaker.csの91行目 infoTexGen.SetInt("VertCount", vCount); infoTexGen.SetFloat("AngleOffsetX", angleOffset.x); infoTexGen.SetFloat("AngleOffsetY", angleOffset.y); infoTexGen.SetFloat("AngleOffsetZ", angleOffset.z); infoTexGen.SetBuffer(kernel, "Info", buffer); infoTexGen.SetTexture(kernel, "OutPosition", pRt); infoTexGen.SetTexture(kernel, "OutNormal", nRt); infoTexGen.SetTexture(kernel, "OutTangent", tRt);
// MeshInfoTextureGen.computeの17行目 float AngleOffsetX; float AngleOffsetY; float AngleOffsetZ; #define Deg2Rad 0.0174532924 float4x4 eulerAnglesToRottationMatrix(float3 angles) { float cx = cos(angles.x * Deg2Rad); float sx = sin(angles.x * Deg2Rad); float cy = cos(angles.z * Deg2Rad); float sy = sin(angles.z * Deg2Rad); float cz = cos(angles.y * Deg2Rad); float sz = sin(angles.y * Deg2Rad); return float4x4( cz*cy + sz*sx*sy, -cz*sy + sz*sx*cy, sz*cx, 0, sy*cx, cy*cx, -sx, 0, -sz*cy + cz*sx*sy, sy*sz + cz*sx*cy, cz*cx, 0, 0, 0, 0, 1); } [numthreads(8,8,1)] void CSMain (uint3 id : SV_DispatchThreadID) { int index = id.y * VertCount + id.x; MeshInfo info = Info[index]; // OutPosition[id.xy] = float4(info.position, 1.0); float3 angle = float3(AngleOffsetX, AngleOffsetY, AngleOffsetZ); float4 position = mul(eulerAnglesToRottationMatrix(angle), float4(info.position, 1.0)); OutPosition[id.xy] = position; OutNormal[id.xy] = float4(info.normal, 1.0); OutTangent[id.xy] = float4(info.tangent, 1.0); }
結果、インスペクターから回転を指定することで作成されるPrefabの回転を合わせることが出来ました。
有効なモデルについて
いくつか試してみてSkinnedMeshRendererを使用しているモデルのみ有効のようです。
例えば、次のRobot SphereではSkinnedMeshRendererを使用していないためVATを作成するのは出来ませんでした。
また、VATの作成では1つのSkinnedMeshRendererを使用して1つのテクスチャを作成しているため複数のSkinnedMeshRendererが付いたモデルではその数分のVATを作成しなくてはいけません。
以下のアセットでは2つのSkinnedMeshRendererを使用しているので2つVATを作成して同じ座標に置くことで上手くいきました。
1つのSkinnedMeshRendererを使用しているモデルについては問題なくVATを作成することが出来ました。
Mini Legion Rock Golem PBR HP Polyart | Characters | Unity Asset Store
感想
VATによってCPU負荷を減らしてアニメーションを行うことが出来ました。
VATだけでの検証でしたが、これからGPUインスタンシングと組み合わせてみようと思います。
参考
こちらの記事とUnity Graphics Programming vol.3 を参考にしています。 また、Unity Graphics Programming vol.3では詳しい解説が載っているので知りたい方は拝見してください。