This explanation is currently available in Japanese.
ひとことで
メッシュの頂点の動きをあらかじめ全フレームぶん計算し、画像(テクスチャ)の画素として焼き込んでおき、描画するときに頂点シェーダーがその画像から自分の位置を読み出して再生する方法です。骨の計算(スキニング)も物理計算も実行時にはいらないので、何千体ものキャラクターや、複雑な破壊・流体の動きを、とても軽く再生できます。
ゲームでの使いどころ
- 群衆:スタジアムの観客、戦場の兵士、街の人々。GPU インスタンシングと組み合わせ、1 回の描画命令で数千体を動かします。
- 魚群・鳥の群れ・虫:単純な泳ぎや羽ばたきを大量に。
- 事前計算した物理:建物が崩れる、旗がはためく、水がはねるといった、リアルタイムでは重いシミュレーションを Houdini などで計算して焼き込み、ゲームでは再生だけする。
- 遠景のキャラクター:近くはスキニング、遠くは VAT、という LOD の使い分け。
しくみ
-
焼き込み(オフライン):アニメーションの各フレームで、メッシュの全頂点の位置(と法線)を計算し、画像の 1 行に並べます。
- 横方向:頂点の番号(頂点が 1344 個なら幅 1344)
- 縦方向:フレームの番号(24 フレームなら高さ 24)
- 画素の色:頂点の位置 を RGB に入れる(浮動小数点テクスチャ、または範囲を決めて 16 ビットに量子化)
-
頂点に番号を持たせる:各頂点に「自分の番号」を属性(または UV の空きチャンネル)として持たせます。
-
再生(実行時):頂点シェーダーで、今のフレーム番号と自分の頂点番号から画像の座標を計算し、位置を読み出して頂点を動かします。前後 2 フレームを読んで線形補間すると、少ないフレーム数でもなめらかです。
- ばらけさせる:インスタンスごとに再生の開始位置(オフセット)や速さを変えると、全員が同じタイミングで動く不自然さがなくなります。
法線も同じように焼き込むか、変形の少ない動きなら元の法線を使います。
実装のポイント
- テクスチャの形式:精度が必要なら RGBA の 16 ビット浮動小数点(half float)を使います。8 ビットに量子化する場合は、位置の範囲(バウンディングボックス)を別に保存して戻します。
- 頂点数とテクスチャサイズ:テクスチャの幅には上限(多くの環境で 4096〜16384)があります。頂点が多い場合は複数の行に折り返して格納します。
- フィルタは最近傍:隣の頂点の値が混ざらないよう、テクスチャのフィルタは NEAREST にし、補間はシェーダーで行います。
- 複数のアニメーション:歩き・走り・待機など複数の動きを、1 枚のテクスチャに縦に並べ、開始行と長さで切り替えます。
- 骨を焼き込む方式:頂点ではなく骨の行列を焼き込み、シェーダーでスキニングする方式(ボーンアニメーションテクスチャ)もあります。こちらは頂点数によらずテクスチャが小さく済みます。
つまずきやすい点
- メモリが大きい:頂点数 × フレーム数 × 画素のサイズになるので、長いアニメーションや高密度のメッシュではすぐに数十 MB になります。フレーム数を減らして補間する、遠景用に頂点を減らしたメッシュで焼く、などで抑えます。
- 動きの切り替えが苦手:焼き込んだ動き以外はできず、ブレンドや IK も基本的にできません。主役には使わず、背景や群衆に使います。
- 影と当たり判定:影を描くための深度用のシェーダーにも同じ頂点の読み出しを入れないと、影だけ動かなくなります。当たり判定は元の形のままなので、大まかな形状で代用します。
デモで試すこと
- 800 匹の魚が、すべて 1 回の描画命令で泳いでいます。上に表示している画像が、1 匹分の泳ぎを焼き込んだものです。横が頂点、縦がフレームで、色は頂点の位置です。
- 「焼き込むフレーム数」を 4 に下げると、泳ぎがぎこちなくなります(フレーム間は補間していますが、動きの形が粗くなる)。画像の縦の長さも短くなります。
- 「魚の数」を 1500 に上げても、骨の計算をしないので、ほとんど重くなりません。
- 「泳ぐ速さ」を変えると、画像を読む速さが変わり、尾びれを振る速さが変わります。魚ごとに読み始めの位置をずらしているので、全員が同じ動きでもそろって見えません。