【解説編】AssimpでFBXアニメーションを動かすための理論的な手順まとめ
はじめに
FBX形式の3Dモデルにアニメーションをつけて再生するには、「スキニング」などの基礎的な考え方がかかせません。
Assimpは多くの3Dファイル形式を読み込めるライブラリですが、FBX形式のアニメーションを動かすために必要な手順があります。
この記事(解説編)では、Assimpを使ってFBXモデルのアニメーションを動かすための エンジンに依存しない理論的な流れ を解説します。
特定のライブラリ(今回はOpenSiv3D)での実装例は、後編の「実装編」で紹介します。
初心者でもわかりやすいように、かみ砕いて説明するので、安心して読み進めて!
前提知識
とくにないです!
今回の記事で紹介することは、Assimpの公式ドキュメントに記載されています。
正確で詳しい情報が欲しい方はこちらを見ることを推奨します。
Assimpドキュメント
FBX形式のモデルをアニメーションするための全体の流れ
まずは簡単に、全体の流れを紹介します。
① Assimpを使ってFBXファイルを読み込む
② メッシュとボーンの情報を抽出する
③ アニメーションを更新する
④ スキニング処理(ボーンの変形処理)をする
⑤ メッシュを更新する
① Assimpを使ってFBXファイルを読み込む
Assimpは、様々な3Dファイル形式を共通のデータ構造に変換してくれるライブラリです。
具体的には、「aiScene」という構造体に変換します。
読み込む手順
1. Assimp::Importerクラスを使って、FBXファイルを読み込む
2. フラグを設定する。(これを設定することで、Assimpが自動的にモデルの「前処理」をしてくれるため、実装が楽になります。)
・フラグの例
aiProcess_Triangulate:ポリゴンを三角形に変換
aiProcess_JoinIdenticalVertices:重複する頂点を結合
aiProcess_LimitBoneWeights: 各頂点に影響するボーンの数を制限
aiProcess_ImproveCacheLocality: 頂点の並びを最適化
aiProcess_FlipUVs: UV座標のY軸を反転
aiProcess_ConvertToLeftHanded: 左手座標系に変換
FBXの読み込みをコードにするとこうなります。
const aiScene* scene = importer.ReadFile(fbxFilePath.narrow(),
aiProcess_Triangulate
| aiProcess_JoinIdenticalVertices
| aiProcess_LimitBoneWeights
| aiProcess_ImproveCacheLocality
| aiProcess_FlipUVs
| aiProcess_ConvertToLeftHanded
);
aiSceneについて
読み込みが成功すると、「const aiScene*」という型が返ってきます。
変な型に見えますが、読み込んだモデルのメッシュ、ボーン、アニメーションなど、3Dアニメーションに必要な全ての情報をまとめた構造体 です。
この型を使って、欲しい情報を取得していきます。
読み込まれるデータを簡単に紹介します。
mMeshes: メッシュの情報(頂点・法線・UVなど)
mMaterials: マテリアルの情報
mRootNode: aiSceneの階層構造
mAnimations: アニメーションの情報
② メッシュとボーンの情報を抽出する
①で読み込んだ 「const aiScene*」 から、メッシュとボーンの情報を取得します。
メッシュ情報取得の流れ
メッシュとは、モデルの形(頂点・法線・UVなど)のことです。
コードとしては、aiScene::mMeshesから取得します。
ボーン情報取得の流れ
ボーンとは、アニメーションを動かすための骨格のことです。
Assimpで読み込んだ段階でボーンは階層構造になっています。
この階層構造を再帰的にたどることで、ボーンの親子関係や初期変換を取得できます。
コードとしては、
aiScene::mRootNode を起点に、ノードの階層構造を再帰的処理を使ってたどります。
コードの詳細は後編に任せるとして、ここでは処理の流れだけ分かってもらえれば問題ないです。
③ アニメーションを更新する
今モデルが再生している時刻(例えば1.25秒)に合わせた、各ボーンの位置・回転・スケールを計算して更新します。
コードしては、各アニメーションデータは、aiScene::mAnimationsに格納されています。
この情報を元に、線形補間もしくは球面補間を利用して各ボーンを動かします。
とはいえ、この説明だけでは「それってどういうこと?」感じる方も多いと思います。
この説明だけで分からない人向けに、補足を入れます。分かる人は飛ばしてもらって構いません。
補足:キーフレームとアニメーションの補間について
キーフレームとアニメーションの補間について、長くなったので別記事にしてまとめました。
こちらで詳しく説明しているので、ご覧ください。
プログラムでアニメーションを再生するとは?キーフレームと補間の仕組み
④ スキニング処理(ボーンの変形処理)をする
スキニングとは、各頂点が複数のボーンから「どれだけ影響を受けるか(=ウェイト)」をもち、それに基づいて位置や法線を再計算することです。
つまり、③で取得したキーフレームの情報を元に、影響度合いを再計算し、反映させるとも言えます。
コードとしては、まず、あらかじめどのボーンがどれだけウェイトを持っているかのペアを作成しておきます。
その後、各頂点について、③で取得したボーンの変形情報とあらかじめ設定されたウェイトを使って、最終的な位置や法線を計算します。
簡単に言うと、各頂点に対して③で得た情報+ボーンの影響度合いを反映させるということです。
⑤ メッシュを更新する
④でスキニング処理が終わった頂点データがあります。
このデータを、モデルのメッシュに反映させます。
コードとしては、新しい頂点の配列を用意し、描画処理に渡します。
すると、次のフレームで次のアニメーションの動作をしたモデルが正しく描画されます。
①と②の読み込み処理を最初に行ない、
③~⑤の処理を毎フレーム実行することで、モデルがアニメーションをします。
なんとなくイメージが着いたでしょうか。
おわりに
この記事では、理論的な流れを中心に説明してきましたが、細かくて難解な部分はあえて省略しています。
完璧には分からなくても、なんとなく雰囲気は掴めたのではないでしょうか。
アニメーションの仕組みを知ることで、実装やデバッグの際に「なぜ動かないのか?」を論理的に考えられるようになります。
後編では、いよいよこの理論を元にした、具体的なコードを紹介します。
ぜひ続けてお読みください!