のんびりしているうちに…また Blender のバージョンが上がりました。
今回は Collada の出力機能が改善されている上、幾つかのバグが修正されているようです。
動作テストを兼ねて色々遊んでみたいと思います。
さて、何をすると面白いかと考えながら
ニコニコ動画を見ていると .pmd ファイルとか読み込めたら面白いかなぁと思うようになりまして
様のこのページより「門を開く者アリス」モデルを落とさせていただきました。
これを
様のこのページのMMDモデル(.pmd, .pmx)インポーター/エクスポーターにて Blender に読み込もうと思います。
Blender 2.64 で読み込んでみた結果がコチラ↓
Blender から Collada ファイルを書き出して Kimberlite で読み込んだ結果がコチラ↓
ここで、ボーンやスキンメッシュの読み込みに未対応であることに気付きました。
これができないと .pmd ファイルを読んでもあまり面白くないので実装することにします。
<作業>
ひとまず簡単なスキンメッシュモデルを用意してテストします。
2番目のボーンを X 軸方向に移動させた結果がコチラ↓
合っている…のか?
ひとまず置いておいて、ボーンの描画、選択、ピックが出来るようにしてみます。
<作業>
Collada でボーンを出力するとJOINTノードとして書き出され、その中には matrix タグが作られます。
上図のボーンを作成して出力した場合について調べます。
ノードの matrix を使って Y軸方向に伸びる単位長さの円柱を描画すると…
こう↓なります。
で、スキンメッシュ用に親子関係をメッシュと築くと、バインドポーズ逆行列がJOINTノードごとに定義されます。
この逆行列で Y軸方向に伸びる単位長さの円柱を描画すると…
こう↓なります。
matrix タグの行列とバインドポーズ逆行列をかけた結果がコチラ↓
親の行列を順にかけた結果がコチラ↓
ん?どういうことなんだ?
多分にデータを誤解して受け取っているはず、どこかの格納順が間違っているとかだろう。
仕切りなおしてもう一度簡単なボーンを配置して matrix を確認する。
matrix の中身がコチラ↓
matrix を使って描画した結果がこちら↓
決まりですね…単純なバグです。
double 配列から行列に変換する工程で格納場所を間違えている模様です。
<バグ修正>
matrix タグに格納されている 16 の倍精度実数は行列 M11, M21, M31, M41, M12 ... と
1列目、2列目という順番で要素が書きだされていました。
(自分はここを行ごとに出力されているものと勘違いしていました)
さて、いくつか入出力テストをしていてわかったことを次に示します。
matrix タグの行列にはスケール成分は入らない。(回転と移動のみになります。)
単位行列のとき、ボーンの先端は原点からY軸正方向に伸びます。(これは Blender の座標系での話、DirectX なら +Z 方向。)
相対姿勢行列です、自分の回転移動をして、親の回転移動をかけることで正しいボーンの位置となります。
ボーンの長さは子ボーンの位置によって決まります。
描画時は順番に matrix をかけることで、ボーンの姿勢を正しく求めることができます。
Blender から DirectX の座標系に変換するには姿勢行列をオイラー角、並進移動ベクトルに分解し
XYZオイラー回転をXZYオイラー回転に直し
並進移動ベクトルをXYZからXZYに直します。
姿勢行列をオイラー角、並進移動ベクトルに分解する計算は次のページを参考にしました。
Blender は XYZ 回転なので、式のパターンが異なるのでそのまま利用せず計算することに注意。
では、仕切りなおして簡単なテストから…
↓
合っている様子。
↓
合っている様子。
↓
合っている様子。
↓
合っていない…
フレームの matrix は次のとおり(順番に注意すること、行列を転置して考えること。)
行列から言えることは、Z軸で90度ロールして、Y軸で-90度回転と読める。
オイラー角回転に分解した結果が次のとおり(DirectX 座標系に変換後の値)
もともとの回転角度はXYZ の順番で -π/2, 0, π/2 ということになる。
これも、結果としては、Z軸で90度ロールして、Y軸で-90度回転したものと同じ…
ん?ロール回転方向が逆かな…行列の分解というより角度をそのままDirectXに渡すのが問題なのかも。
整理のためにロールなしで次の配置を行う。
↓
これは正しいように見える。
回転角度はXYZ の順番で 0, 0, π/2 ということになる。
あ…今更だけど、回転行列からオイラー角を出して、オイラー角から同じ回転行列を作ることはできても
オイラー角が回転行列作成時のオイラー角ではない(正確には求められない)ので
オイラー角を使って座標系をまたぐ変換はできないことに気づく。
バカだから…許してください。
つまりオイラー角を使って 右手座標系Zup XYZ 回転を左手座標系 Yup XZY回転に変換したところで
同じ姿勢は求められません。(変換できる姿勢とできない姿勢があるという方が正しいかな。)
では、どうやって Collada のジョイントノードの matrix から DirectX で使用する姿勢行列を求めるのでしょうか?
答えは簡単、そのまま Blender の行列を受け取り、使用する時に Decompose して
Scale, Quaternion, Translation に分割し、Y, Z の値を入れ替え、Quaternion の W 成分の符号を反転する。
↓
ロールを含めても問題なく姿勢の再現ができることを確認しました。
出来れば Converter 側で片付けたかったのですが、Decompose のために SharpDX を参照するのも気が引ける…
いえ、やっぱり Converter 側に実装することにします。
<実装>
組み込みました。
SharpDX の dll を Converter の隣におくことになりましたが動作確認までできました。
また、BindPose 行列を前にかけると全て単位行列になることも確認できました。
ここまで来ればスキンメッシュアニメーションの実装が見えてきます。
<実装>
スキンメッシュのシェーダとボーンの描画を同時に確認した結果です。
正しく処理できている様子。
ポーズモードでボーンの姿勢を変更して、変更したボーンを出力すること、これを描画で確認できました。
何かポーズを決めてボーンを出力し、このボーンに影響を受けた結果を表示してみます。
<実装>
確認用にボーンだけ描画する機能を付けました。
ノードオブジェクトを一緒に描画した結果がこちら↓
では、Blender でボーンに適当な姿勢を設定して Collada ファイルを出力します。
この Collada ファイルを読み込んで表示した結果がコチラ↓
ボーンだけにした結果がコチラ↓
問題は確認されませんでした。
スキンメッシュの描画は正しく行われているようです。
では、ボーンを物理オブジェクトに置き換えてアニメーションさせてみたいと思います。
ちょっとデバッグに時間を使ったので、次回に行いたいと思います。
お楽しみに!
2012/11/17 初記。
2012/11/30 更新。