前回の続きです。
ということで、ようやくSDEFの実装にかかります。
技術的にはこちらが参考になるものの、
MMDのSDEFには中心点以外にR0,R1なる謎のパラメータがあります。
それらに関する情報も含めて、
どうやって実装すべきなのか?
ネットの情報を漁ってみました。
そして、みつけたのが、
こちら、こちら、そしてこちらの3つ。
ヒット数は少ないものの、
いずれも具体的なコードが書かれているので、
参考になりそうです。
そんなわけで、
技術的な裏付けがはっきりしているこちらと、
こちらの論文を参考にして、
謎なパラメータはとりあえず使わずに、
回転中心点を使った実装を試してみることにしました。
BDEFの単純な線形補間に比べて、
SDEFの球状補間ではクォータニオンを扱うことになります。
頂点シェーダーでの実装は以下のようになりそうです。
1)2つのマトリクスから回転成分をクォータニオンとして抽出。
2)2つのクォータニオンを slerp
を使って重みで球状補間。
3)中心点と補間後のクォータニオンを使って対象頂点を回転。
4)2つのマトリクスを重みで積和(線形補間)した結果で中心点を変換。
5)3)の結果に4)の結果を加算。
6)法線も補間後のクォータニオンを使って回転。
※ slerp
は球状補間の関数名です。
上記の処理をシェーダー内でやるわけですが、
ちょっと気になるのが slerp
。
こちらの three.js の実際のコードにあるように、
atan2
や sin
などの三角関数を使うことになります。
つまり、それなりに重めな計算処理になります。
幸いにも先の記事によると、
クォータニオンを単純に線形補間してもそれほど誤差がないそうです。
実際に比較しているこちらを見るとよく分かります。
桃色のが頂点を積和しただけの線形補間、
青がslerp
、赤がqlerp
です。
個人的に調べた感じでは、平均で5度、最大で8度くらいの差になるようです。
360度に対してせいぜい2%程度になるわけで、このくらいなら許容できそうですね。
なので、slerp
ならぬ qlerp
でやります。
クォータニオンを使っての回転はこちらをそのまま使わせてもらいました。
また、マトリクスからクォータニオンを抽出するのは、
three.js のこちらの setFromRotationMatrix
を参考にしました。
で、
実際にやってみた結果ですが、
BDEF と大差ないような感じです。
差が分かるようなデータとかあれば、
よりハッキリするかもしれませんが・・・。
やはり、謎なパラメータであるR0とR1を使わないといけないようです。
ところが、
先に上げた3つのコードを比較してみると、
実装がまちまち・・・。
どれが正しいのか分からなかったので、
ひと通り試してみることにしました。
いずれも基本的には変換後の中心点を、
R0とR1を使って補正するような感じっぽい?
結論から言うと、
うち2つはどうもうまく実装できませんでした。
なんかわけわからんことになってしまいます(^_^;)
コードの意味をちゃんと理解できてないのかもしれません。
結局、こちらを採用することにしました。
結果的に計算量が3つの中では一番少なくて済むことになりました。
ただ、果たして本家と同様な正しい実装なのかどうかは不明です。
なので、あくまで参考実装ということにします(^_^;)
ちなみに、
BDEF と SDEF の結果の違いは以下のような感じになりました。
髪の毛の曲がり具合がSDEFだとより自然な感じになっているように見えますね。
なお、
かつて実験的に実装したけど
旧作のViewerでは使っていなかった、
グループモーフとボーンモーフも導入してみました。
とりあえずは動くはずですが、
十分には検証できてなかったりするので、
うまく行かないことがあるかもしれません。ご了承ください。
また、
こちらを参考に物理演算のパラメータを調整し直してみました。
エラー低減なパラメータであるSTOP_ERPは若干強めに。
そして拘束力に関するパラメータであるSTOP_CFMについては、
既定のゼロだと制約が厳しいようなので、少し緩くしてみました。
剛体同士の衝突に「遊び」を認めるようにすることで、
旧作にくらべてソフトな感じになっているはずです。
ただし剛体同士がめり込み易くなるので、
おかしなことになってしまうこともあるかも(^_^;)
それから three.js については、
Viewerとして必要ないモジュールとかはばっさり切って、
ファイルサイズが小さくなるようにビルドし直してあります。
ということで、
現時点での最新版である three.js r104 を改造して、
SDEF を参考実装した「MMD Viewer 改」を試すにはこちらからどうぞ。
旧作と異なる点や改善点についても書いてあります。
ボーンモーフの対応が不十分だったのを修正しました。右手系へ変換するのをすっかり忘れてました(^_^;)
ボーンに対する頂点の重み処理が不十分だったのを修正。重みの合計は基本的に1となりますが、個々の重みとしては負の値をとることがあるようです。てっきり全て正の値になるものと思い込んでいたのが問題でした(^_^;)
BDEF と SDEF の結果の違いがより分かり易くなる画像がとれたので、
更新しました。