GPU スケルトン アニメーションのビデオ紹介:
GPU頂点アニメーションとGPUスケルタルアニメーションの実装原理、長所と短所の比較、パフォーマンスの最適化
数千人で同じ画面を実現するにはGPUアニメーションが必須です GPU頂点アニメーションの実装方法は以前の記事で紹介しています: 【Unity】レンダリングパフォーマンス攻略 GPUアニメーション、アニメーションレンダリングバッチ GPU Instance_skinmeshrenderバッチ - CSDN Blog
GPU 頂点アニメーションの長所と短所:
GPU 頂点アニメーションは、アニメーションの各フレームのメッシュの頂点/法線をテクスチャに保存し、シェーダーで頂点/法線を直接読み込んで使用します。
メリット:余分な計算がないのでパフォーマンスが高い。
欠点: モデルに複数の SkinnedMeshRenderer がある場合、最初にメッシュをマージする必要があります。生成されるアニメーション/法線マップは大きくなります。搭載された武器の切り替えはサポートされていません。
GPU スケルタル アニメーションの長所と短所:
GPU スケルタル アニメーションは、アニメーションの各フレーム内のすべてのボーンのマトリックス情報をテクスチャに保存します。各頂点は最大 4 つのボーンの影響を受けます。シェーダーでは、これら 4 つのボーンのマトリックスと、4 つのボーンに対応するスキン ウェイトが使用されます。位置と法線を変換して、ボーンの影響を受ける頂点/法線の値を取得します。
利点: アニメーション化されたテクスチャは非常に小さい; メッシュをマージする必要がない; 搭載武器の切り替えをサポート; 特定のアタッチメント ポイントの位置をリアルタイムで取得できる
短所: ある程度の計算が必要なため、頂点アニメーションよりもパフォーマンスが若干低くなります。
GPU スケルトン アニメーションの実装:
1. スケルトンデータを読み込み、アニメーションマップ、メッシュを生成
1. スキン アニメーションのボーン情報を取得します。
ルート ボーンは、SkinnedMeshRenderer の rootBone を通じて、または SkinnedMeshRenderer に関連付けられたすべてのボーンの Transform 配列であるボーン フィールドを直接使用して見つけることができます。
2. アニメーション カーブから各アニメーション フレームに記録されたボーンのトランスフォーム値を取得します。
例として、アニメーションの各フレームのボーン位置を取得します。
private Vector3 GetBonePositionAtTime(string bonePath, AnimationClip clip, float animTime)
{
var localPosXCurve = EditorCurveBinding.FloatCurve(bonePath, typeof(Transform), "m_LocalPosition.x");
var localPosYCurve = EditorCurveBinding.FloatCurve(bonePath, typeof(Transform), "m_LocalPosition.y");
var localPosZCurve = EditorCurveBinding.FloatCurve(bonePath, typeof(Transform), "m_LocalPosition.z");
Vector3 pos = Vector3.zero;
pos.x = AnimationUtility.GetEditorCurve(clip, localPosXCurve).Evaluate(animTime);
pos.y = AnimationUtility.GetEditorCurve(clip, localPosYCurve).Evaluate(animTime);
pos.z = AnimationUtility.GetEditorCurve(clip, localPosZCurve).Evaluate(animTime);
return pos;
}
3. ボーン マトリックスをアニメーション マップに書き込みます。
ボーンの数をオフセットとして、行列の最初の 3 行の値をアニメーション マップにそれぞれ書き込みます。
for (int boneIdx = 0; boneIdx < bones.Length; boneIdx++)
{
var bone = bones[boneIdx];
bool noBone = bone.GetComponent<MeshRenderer>() != null;
if (!noBone && bone.TryGetComponent<SkinnedMeshRenderer>(out var sMeshRender) && sMeshRender.rootBone == null)
{
noBone = true;
}
var boneMatrix = bone.localToWorldMatrix;
if (!noBone)
{
boneMatrix *= bonesW2LMatrices[boneIdx];
}
animBoneTex.SetPixel(boneIdx, curFrameIndex, boneMatrix.GetRow(0));
animBoneTex.SetPixel(bonesCount + boneIdx, curFrameIndex, boneMatrix.GetRow(1));
animBoneTex.SetPixel(bonesCount * 2 + boneIdx, curFrameIndex, boneMatrix.GetRow(2));
}
4. 各アニメーションの開始フレーム/終了フレーム、アニメーションの長さ、およびアニメーションがループで再生されるかどうかに関する情報を、アニメーション マップのピクセルの最後の列に書き込みます。
5. メッシュグリッドを生成します。
ボーン情報のアニメーション マップでは、シェーダーで対応するボーン情報を取得して頂点と法線を変換できるように、どのボーンが各頂点の影響を受けるかを知る必要もあります。
リソースを節約し、読み取りを容易にするために、頂点に関連付けられた 4 つのボーンと各ボーンのウェイトをメッシュの UV2 チャネルと UV3 チャネルにそれぞれ直接接続できます。
2. GPU スケルトン アニメーション シェーダーの実装:
1. アニメーション マップから現在のアニメーションの開始/終了フレームを解析し、ループされているかどうかに基づいて現在のアニメーション フレームを計算します。
2. 現在のフレームをアニメーション マップ サンプリングの V 座標として使用し、すべてのボーン マトリックスの各行の値をサンプリングして取得し、ボーン マトリックスを構築して頂点/法線を計算します。
3. カスタム関数を通じて変換された頂点座標と法線を取得し、GPU スケルタル アニメーション シェーダーに適用します。
これで GPU スケルタル アニメーション機能が完成しました。アニメーションを切り替えるときに、アニメーション Index と現在時刻 Time.time を渡すと、アニメーション クリップは自動的に開始フレームから再生を開始し、アニメーションがループするかどうかを完全にサポートします。ボーンにマウントされた武器の場合、MeshRenderer と SkinnedMeshRenderer の両方が完全にサポートされます。これは、マウントされた武器のノード Transform 自体もボーンとしてアニメーション マップに書き込まれ、Shader がボーンの Local2WorldMatrix を通じて頂点を自動的に変換するためです。武器は自然に骨と一緒に動きます。
3. 吊り下げ点の位置を取得する
たとえば、GPU アニメーション キャラクターは手に銃を持っています。弾丸を発射するとき、銃口に弾丸を作成して発射する必要があります。GPU アニメーションにはボーン トランスフォームがなくなったため、その位置を取得する方法銃口は?
GPU アニメーションは純粋な Shader 実装であるため、アニメーションの切り替えにはマテリアルの ClipId プロパティを変更するだけで済みます。ここで、x はアニメーション インデックス、y はアニメーション再生の開始時間 (Time.time) です。
アニメーション インデックスと再生開始時刻を使用すると、現在のアニメーションの再生時間を取得できます。再生時間に基づいて、アニメーションがどのフレームで再生されたかを計算できます。どのフレームを通じて、任意のアニメーションのマトリックスを読み取ることができます。アニメーション マップからボーンを取得するため、いつでもマウント ポイントの位置、回転、スケーリングを高パフォーマンスで取得できます。