TouchDesignerでVertex Animation Textureを用いたアニメーションについて

# こんにちは
簡単に自己紹介します。[株式会社たき工房](https://www.taki.co.jp/) でプログラマーをしていて、個人ではTouchDesignerで映像制作やVJをしています。その他、TouchDesignerのもくもく会を月に1回開催しています。
komakinex：[Twitter](https://twitter.com/komakinex)
TouchDesignerもくもく会など：[Peatix](https://techlab-tdmokumoku.peatix.com)

今年のアドベントカレンダーの1日目にOpenCVの記事を書いたのでよければ見てください！
[TouchDesignerでOpenCVを使った簡単な顔認識について](https://qiita.com/komakinex/items/5b84b88d537d393afc98)

今回は大量の物体をアニメーションさせることについての記事です。
ちょうど一年くらい前に、[Hiroyoshi Murata](https://twitter.com/muracchijp)さんのツイートでたくさんの人がバラバラに動いているのを見ました。
<blockquote class="twitter-tweet"><p lang="und" dir="ltr">20190104<a href="https://twitter.com/hashtag/TouchDesigner?src=hash&amp;ref_src=twsrc%5Etfw">#TouchDesigner</a> <a href="https://twitter.com/hashtag/creativecoding?src=hash&amp;ref_src=twsrc%5Etfw">#creativecoding</a> <a href="https://t.co/M4bxnHkOQD">pic.twitter.com/M4bxnHkOQD</a></p>&mdash; Hiroyoshi Murata (@muracchijp) <a href="https://twitter.com/muracchijp/status/1081229490179567618?ref_src=twsrc%5Etfw">January 4, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

単純なインスタンシングだと、どのモデルも同じ動きになってしまうので自然な表現とは言い難いです。
リプ欄を見てもらうとわかりますが、丁寧に教えていただきました。ただ、どういう仕組みで動いているのかなんとなくわかったのですが、実際に自分で作るとなると全然わからないままでした。
それから月日が経ち、VJで使ってみたいなと思い、村田さんの言葉を頼りに色々と調べて実装してみました。

# Vertex Animation Textureとは
先程のアニメーションの手法を頂点アニメーション、Vertex Animation Textureといいます。簡単にいうと、頂点数×アニメーションのフレーム数の解像度の画像に、頂点の位置情報（xyz座標）を色情報（RGB）として持たせて、その画像をシェーダで順番に読み取ることでモデルの頂点を動かすことです。

以下の記事が参考になりました。
・[インディーゾーンHoudini情報日本語ブログ - Vertex Animation Texture について](http://houdini.indyzone.jp/blog/vertex-animation-texture-%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6/)
・[土屋つかさの技術ブログは今か無しか - 頂点アニメーションとはなにか](https://t-tutiya.hatenablog.com/entry/2019/10/07/222651)
・[サルにもわかる Houdini - Vertex Animation Texture Unreal](http://ikatnek.blogspot.com/p/vertex-animation-texture-unreal.html)
・[テラシュールブログ - 【Unity】シェーダーで3Dモデルのアニメーションを行う Animation Texture Baker](http://tsubakit1.hateblo.jp/entry/2017/09/03/225713)

Vertex Animation Textureでモデルをアニメーションさせる一連の流れのことを、この記事ではVATと略すことにします。

# 出来上がり
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">クリスマスフェス！ <a href="https://twitter.com/hashtag/TouchDesigner?src=hash&amp;ref_src=twsrc%5Etfw">#TouchDesigner</a> <a href="https://t.co/2vnlRgR7rF">pic.twitter.com/2vnlRgR7rF</a></p>&mdash; komakinex (@komakinex) <a href="https://twitter.com/komakinex/status/1209768470746783746?ref_src=twsrc%5Etfw">December 25, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

かなりたくさんの人を、各々のタイミングでジャンプさせることに成功しました。


### サンプルファイル
↓ここにあります↓
https://github.com/komakinex/TD_AdventCalendar2019_VAT

### 動作環境
TouchDesigner 099 2019.20140
Windows10


# 準備


## 用意するもの
・TouchDesigner
・動かしたいモデル（alembic形式）
・Houdini
Apprenticeでも問題なく進められますが、レンダリングすると画像にウォーターマークが追加されるので、VATでアニメーションさせたとき一瞬変になります。
![コメント 2019-12-25 190640.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/a19a0d36-ab1f-f53f-b73f-58cb60c28059.png)
![TDMovieOut0.0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/e1a20127-623e-d9b4-3dda-77414c18ffed.gif)

Indieで書き出すとこうです。
![コメント 2019-12-25 190704.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/feac24d4-7d4f-5708-9df1-79a1ff06ec9e.png)
![TDMovieOut1.0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/5ed77b13-1ed9-27f4-8323-c362f957b88d.gif)


サンプルファイルにはどっちも入っています。

この記事ではフリーの人型モデル（obj形式）にmixamoでアニメーションをつけています。その場合追加で以下が必要です。
・Adobeアカウント（mixamo用、無料で登録できます）
・Blender


## 流れ
・mixamoでモデルにアニメーションをつけ、fbx形式で書き出す
・それをBlenderでabc形式で書き出す
・それをHoudiniで読み込んで、Vertex Animation Textureを書き出す
・TouchDesignerでGLSL MATを使って動かす

# 説明
動かしたいモデルがすでにabc形式の人は①と②を飛ばしてください。サンプルファイルを開きながら見てもらえるとわかりやすいと思います。

## ①mixamoでアニメーションをつける

![コメント 2019-12-11 165506.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/ce5b3086-da32-9587-5631-1fbacf978259.png)

ログインして、説明通りモデルをUploadして進めていきます

![コメント 2019-12-11 165924.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/dc9912a3-f730-e71d-0526-0588ad5b8366.png)

気に入ったアニメーションを見つけたらダウンロードします。どこも変更せずに大丈夫です。

## ②Blenderでabc形式で書き出す
![コメント 2019-12-25 194419.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/542abb8a-c563-6592-3f54-960f0734fe22.png)

①で書き出したモデルをインポートします。

![コメント 2019-12-25 194933.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/efcb6680-70c6-c38a-e6a0-157fc5c0e9f4.png)

Blenderの画面右下の`End`のフレーム数を、mixamoでつけたアニメーションと同じ数字にします。さっきのジャンプは97フレームのアニメーションでした。

![コメント 2019-12-25 195258.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/a31e81e6-9de0-a0b2-a663-76dbcf535fae.png)

その後、alembic形式でエクスポートします。

## ③HoudiniでVertex Animation Textureを書き出す

VATのためにGame Development Toolsetが必要なのでインストールします。
![コメント 2019-12-26 153401.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/c59cdf80-379a-ebab-498c-fafd1a1f9aac.png)

Shelves→Game Development Toolsetにチェックを入れ、Update Toolset→Updateをクリック、Successが出たらHoudiniを再起動します。

![コメント 2019-12-26 155327.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/485fa717-60f8-b126-fe4b-bdecef43b5d3.png)

![コメント 2019-12-26 154006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/48207923-c8ef-cf09-a087-b9765b12e9db.png)

モデルの準備をします。`Alembic SOP`に②のモデルを読み込み、TouchDesignerに合わせるため`Frames Par Second`を60にします。数字を打ち込んでも30に戻ってしまう場合`Frames Par Second`をクリックして、写真のようにテキストフィールドが伸びた状態で入力すると変更できます。

![コメント 2019-12-26 161000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/b9cc7037-1f6c-6a0d-dad0-729c7b3cc8fe.png)

フレームあたりの時間を2倍にしたので、全体のフレーム数を2倍にします。右下に97×2である194を入力します。再生して、アニメーションがちゃんと最後まで流れるか確認します。

![コメント 2019-12-26 161415.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/0b8be471-8943-6d3c-2ccd-9fa433c06fc5.png)

`Alembic SOP`のinfoを見てみると`Packed Alembics`が1になって、頂点やプリミティブなどが1つにまとめられていたので、`Convert SOP`でポリゴンに変換します。なってなければこのノードは不要です。

最後に`Null SOP`を繋ぎ、わかりやすいように名前を`OUT`に変えます。ここで一旦ファイルを保存します。保存しておくとVATがわかりやすいところに生成されます。

![コメント 2019-12-26 160256.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/0ecf7dfc-4114-55ac-6f1a-870a75c9de4b.png)

VATの書き出しに移ります。Game Development ToolsetのシェルフからVATをクリックすると、`out`の階層に`GameDev Vertex Animation Texture`が生成されます。
`Export Node`に先ほどの`OUT`を選択し、`Target Texture Size`にモデルの頂点数を入力します。この数値はVATの横幅に相当します。
`Normalize Data to 0-1 space`のチェックを外し、`Pack Normal into Alpha`にチェックが入っていれば、写真右上の`Render`をクリックします。するとファイルと同じ場所にexportフォルダが生成され、モデルとVATが入っています。

## ④TouchDesignerでVATする

サンプルファイルの`base`を見てください。

![コメント 2019-12-26 161916.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/aa7ce07f-bd7a-e253-491d-a4038069132f.png)

使うモデルはBlenderで書き出したAlembicのモデルでも、HoudiniでVATを書き出した時のoutput.abcのどちらでも構いません。BlenderでAlembicを書き出すと法線方向が変になっていたので、`Attribute Create SOP`の`Compute Normals`を`ON`にしています。
モデルに頂点の番号の情報を持たせたいので、`Point SOP`を用いて`Custom Attrib`に`pt`を追加しています。

では、`GLSL MAT`の内容に移ります。

```glsl:glsl1_vertex

uniform sampler2D uVAT;
uniform int uFrame;
uniform int uFrameSize;
uniform float uScale;

in int pt;

void main() 
{
	vec3 vP = texelFetch(uVAT, ivec2(pt, int(uFrame) % uFrameSize), 0).xzy;

	vec4 worldSpacePos = TDDeform((P + vP) * uScale);
	gl_Position = TDWorldToProj(worldSpacePos);
}
```
フレームごとに、テクスチャから読み込んだ位置を各頂点に足すことでアニメーションさせています。Houdiniで書き出されたVATはUE4向けで、座標系がTouchDesignerと異なります。Y軸とZ軸の入れ替えが必要なので`xzy`となっています。

これをインスタンシングします。サンプルファイルの`instancing`を見てください。

![コメント 2019-12-26 232049.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/47dac7b0-e300-fa01-fbf0-180fe797b13d.png)

オペレーターの組み方は`base`と同じです。

```glsl:glsl1_vertex

uniform sampler2D uVAT;
uniform int uFrame;
uniform int uFrameSize;
uniform float uScale;

in int pt;

float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

void main() 
{
	vec3 vP = texelFetch(uVAT, ivec2(pt, int(uFrame + gl_InstanceID * 1000 * rand(vec2(gl_InstanceID, 100))) % uFrameSize), 0).xzy;

	vec4 worldSpacePos = TDDeform((P + vP) * uScale);
	gl_Position = TDWorldToProj(worldSpacePos);
}
```

コードも基本は`base`と変わらないですが、モデルのアニメーションがずれるようにランダムな数値を返す関数を用います。インスタンシングされたモデルたちの固有値`gl_InstanceID`を用いて、アニメーションの再生に関わる`uFrame`にオフセットがかかるようにしています。オフセットが大きければ大きいほどアニメーションにばらつきが出るので`1000`をかけています。

以上で説明を終わります！冒頭のツイートにあった、たくさんのカラフルな人がジャンプしているサンプルが`sample`にあります。気になった人は見てください。


## 注意点
複数のCGソフトを横断する時、問題になるのが座標系とスケールです。
④で軽く触れましたが、Houdiniで書き出されたVATはUE4向けです。UE4はZ軸が上向き、
![コメント 2019-12-27 160952.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/39e52a27-c01e-b9aa-fe67-5837bf7d4365.png)
TouchDesignerとHoudiniはY軸が上を向いています。
![コメント 2019-12-27 160429.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/9d8ba8ef-b3ae-1334-1165-5bed09817565.png)

座標系は違うのに各軸の色が揃っているのは面白いなあと思いました。

またスケールについて、今回の説明ではややこしくならないように触れなかったのですが、Blenderでabc形式に変換する時、fbxをインポートするとスケールが0.01になっています。（BlenderもZ軸が上向きで、自動で90度回転されています。）
![コメント 2019-12-27 162635.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/a61615f8-0dde-1755-9ea4-b731ac2b1a41.png)
0.01された状態が、元のfbxと同じ大きさなのでそのままにしていました。

### スケールが食い違うとどうなるのか
![TDMovieOut3.0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/83234845-24a6-1f4b-2f18-4df5f85bcea9.gif)
アニメーションに対してモデルが大きいと、みんな控えめになったり
![TDMovieOut5.0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144702/1d040e4a-41c1-c73c-3089-4876d4e8adb4.gif)
なんのこっちゃわからんくなります。



# 参考文献
[Syuya(向井 秀哉) - 【UE4】VATの作り方と使い方 ～VATで桜を咲かせる～](https://note.com/syu_ya23m/n/n3084c636b3d4)
UE4向けにHoudiniでVATを書き出す流れが丁寧に書かれています。

[映像音響処理概説 2018 - 夏休み](http://satoruhiga.com/TDWS2018/summer-holiday/)
比嘉さんが公開している、東京藝術大学の授業の資料にVJのプロジェクトファイルがあります。
その中の、Main.toe→WORK→Scenes→camera_button7（birds）→scene→Birdに、アニメーションする鳥がVATでインスタンシングされています。

[ランダムな値を返す関数 on GLSL](https://qiita.com/shimacpyon/items/d15dee44a0b8b3883f76)
GLSLでランダムな値を扱う関数について書かれています。

[The Book of Shaders - ランダム](https://thebookofshaders.com/10/?lan=jp&fbclid=IwAR3sJiswy7y_sPBrdBSdq1iNhHSVSjiijfGWt1mo3aCatHFmENQ9PLKO91k)
これもランダムについてです。

[ks - 軸の話 〜3DCGにつきものの座標系の違い43種まとめ〜](https://note.com/it_ks/n/nb13311509f0a)
めちゃたくさんのCGソフトの軸について書かれています。



# 最後に
理論的には頂点情報以外もテクスチャに書き込むことができるので、発展させるともっといろんなことができるようになると思います。一年前は全然わからなかったことを、記事することができて嬉しいです！よくわからないところがあれば聞いてください。間違っているところがあれば教えてください！

告知です！2020年1月22日にたき工房でTouchDesignerのもくもく会を、1月29日にアウトプット会を開催します。もくもく会は今までと同じように各自もくもくする会で、アウトプット会はもくもく会でやったことからTouchDesignerを使った事例紹介まで、ライトニングトークみたいな感じにしたいと思っています。
もくもく会 #2のチケットはこちら→https://techlab-tdmokumoku02.peatix.com

今後もイベントの予定は、自分の[Twitterアカウント](https://twitter.com/komakinex)からアナウンスするので、チェックしてみてください！
Peatixをフォローしてもらえると、チケットがオープンした時に通知が行きます。
→https://techlab-tdmokumoku.peatix.com/


## おまけ

まったく関係ないですが、今年のM-1も非常に白熱した戦いでした。またそれとは関係なく、蛙亭のコントが面白いのでぜひ見てみてください！
https://youtu.be/1vR_bov1Rz0

それではみなさん、よいお年を！