‘MMD’ タグのついている投稿

three.jsで遊んでみる(29)

 プログラム,
 公開日:2014年6月9日 / 更新日:2014年8月27日

前回の続きです。

こちらの Change Log を参照して、
r58 から r67 にベースを上げるにあたって、
今回の変更で考慮すべき点、気になった点を書いてみます。

・Object3Dとかにuuidを導入。
 オブジェクトを識別するためだと思いますが、メモリ使用量が増えますね。
 個人的にはuuidは無くてもあまり困らない気がしますが。

・オイラー角がVector3から新設のEulerへ移行。
 位置・方向と回転は明確に区別した方が良いということなのでしょう。

・Face4を廃止。
 4頂点な面は無くても問題なさそうですしね。

・Frustumにおいて、boundingSphereをサポート。
 たぶんこれは、以前に書いたこの問題の改善だと思われます。

・Object3Dにおいて、rotationとquaternionの相互同期を取るようになった。
 回転が内部的にはクォータニオンに統一されたことに伴う処置のようです。
 これは影響が大きいので後述します。

・Vector3において、get*FromMatrix系メソッドがsetFromMatrix*へ改名。
 なぜ改名?・・・後方互換性が失われました(^_^;)

・CubeGeometryをBoxGeometryへ改名。
 なぜ?(^_^;)

・WebGLRendererからphysicallyBasedShadingを削除。
 シェーダーに影響あり、後述します。

・MeshPhongMaterialからperPixelを削除。
 シェーダーに影響あり、後述します。

・Skeletonを導入。あわせてSkeletonHelperも追加。
 SkinnedMeshのボーンによるスケルトン構造が
 独立したクラスに整理されました。
 スケルトン構造を視覚化するヘルパー関数も追加されました。
 デバッグとかで視覚的に表現できそうですね。

・logarithmic depth buffer を導入。
 LogなZ値を扱えるようになりました。
 カメラの near と far の差が大きいと Z-fighting が起きやすくなりますが、
 それを回避するための機能ですね。
 MMD再生ではとりあえず使う必要はなさそうです。

さて、
シェーダーへの影響について検討してみます。
まずは、perPixelの削除について。
Phongシェーディング(より正確にはBlinn-Phongかな)において、
Lighting処理を頂点レベルで簡易的にやるか、
ピクセルレベル(perPixel)でより正確にやるかということです。
デフォルトでperPixel採用にしたので、このプロパティを削除したようです。
次に、physicallyBasedShadingについて。
これは specular(鏡面様ハイライト) の処理において、
物理的により正しい計算をするかどうかということのようです。
これもデフォルトで採用になったのでプロパティ削除となったようです。
いずれの場合もシェーダーのコストは増えますが、
GPUの性能とか上がってるから良しとしたのかも。
ただMMD的にはリアルスティックにする必要はあまり無いと思うし、
Sphereマップとかで工夫されてたりするみたいなので、
r58でデフォルトだった簡易版で行くことにしました。
従来と見た目が変わるのはどうかなと思ったのと、
シェーダーのコストが上がるのが気になったのも理由です。

そして影響の大きそうな、
Object3Dにおけるrotationとquaternionの相互同期の件。
r58においてはrotationとquaternionは排他使用でした。
useQuaternionプロパティを操作することで、
オイラー角かクォータニオンかを選択する仕組みでした。
しかしr67では相互に同期するようになりました。
つまりrotationを変更するとquaternionも変更されます。
その逆も同様です。
内部的には回転はquaternionに統一したけど、
後方互換性のためにrotationは残しているっぽい?
ただ少々気になるのが変換コスト。
どちらか一方を変更する度に変換計算が行われます。
また、内部的にはクォータニオンになったので、
rotation -> quaternion はまだしも、
quaternion -> rotation は無くても良いような気がします。
そこで、quaternion -> rotation の同期は止めれるように対処しました。
syncEulerプロパティを追加して対応できるようにしました。

そんなこんなで、
どうにかベースを r67 にすることができました。
ようやく本題のDDSに入れます。

ということで、やってみました。
・・・が、しか~し!
なんか gl なエラーがやっぱり出る。

・・・ぐはっΣ( ̄ロ ̄lll)

ベースを上げても関係なかったようです(T_T)
どうやら別な問題らしい。
 ・
 ・
 ・
せっかく r67 に上げたのですが、r58 に比べて
微妙にメモリやシェーダーのコストが増える雰囲気なので、
r58 のままでやり直すことにしました。嗚呼。

再度調査し直した所、
DDSを使ったexampleに答えがありました。
それは textureの minFilter と magFilter でした。
ImageUtils.loadTexture を使った場合は設定されるのですが、
ImageUtils.loadCompressedTexture の場合はそうなっていません。
なんでやねん(^_^;)
デフォルトで設定されない理由はよくわからないのですが、
これを設定しないと gl なエラーが出てしまうようです。
exampleでやってるのを真似して、
以下のようにやったらエラーが出なくなりました。

texture.minFilter = texture.magFilter = THREE.LinearFilter

ただ、これでもまだ問題がありました。
なぜかテクスチャが上下逆に貼られてしまうのです。
ところでこの上下逆問題は以前に指摘されており、
あらかじめテクスチャのV座標を逆転させることで対処しました。
にもかかわらず逆になってしまうのは何故なのか?
調べてみた所、texture.filpY が原因だとわかりました。
初めてこのプロパティの存在に気づいたのですが、
デフォルトでこれは true になっています。
つまり上下逆に貼るようになっていたのを、
さらに逆にすることで正常になるように対処してたことになります。
ところが DDS では flipY が効かないようなのです。
flipY = false に統一して調整しようとも思いましたが、
他で色々影響が出てしまうので止めました。
仕方ないので上下逆転させてから DDS へ変換することで対処しました(^_^;)

さて最後に、
実際にFPSが向上したのかを検証してみました。
結論から言うとほとんど変化なし・・・。
数値的には極々微妙にDDSの方が速かったのですが、ほぼ誤差の範囲。
残念ながら私の環境では差を認識できるような結果にはなりませんでした。
ともあれ、DDSでやってみた結果はこららです。
ちなみに、非DDS版はこららです。
なお、テストでは以下のデータを借用させていただきました。感謝。
モデルはこちら、モデルモーションはこちら、カメラモーションはこちら

WebGL対応のブラウザで見てください。WindowsのChromeで確認しています。PCパワーもそれなりに必要となるかもしれません。反応がない場合は、リロードして再度試してみてください。

やたらとサーバーが重いことがあります・・・。
そんな時は、激しく気長に待ってみてください(^_^;)

shadow などのチェックを全て外し、outline scale をゼロにすると動作的に一番軽くなります。ブラウザの拡張機能を一時的に無効にすると、さらに軽くできるかも。

20140514.001

three.jsで遊んでみる(28)

 プログラム,
 公開日:2014年6月8日 / 更新日:2014年8月26日

以前に、
テクスチャにDDSを使うとFPSが向上するかも、
というコメントをいただいたので
今回やってみることにしました。

DDS は DirectDraw Surface の略。
名が示す通り、もとはDirectX なファイル形式ですが、
一般的な認識としては、S3TC(DXTC) な圧縮が使えるファイル
ということで良いかと思われます。

圧縮アルゴリズムについてはこちらで詳しく解説されています。

『JPEGやPNGに比べるとDXTCの圧縮率は低いです。その代わり、
DXTCは画像を圧縮したままGPUのVRAMに置いて使うことができる、
という大きな利点があります』

DDSを使う大きな理由はコレですね。
圧縮したまま扱えるので、
ビデオメモリの使用量を減らせる&転送帯域も抑えることができる。
だから「FPSが向上するかも」になるわけですね。

実装するためにはまず、
PNG や TGA 等から変換してやる必要があります。
こちらを参考にやってみました。
変換ツールは DirectX Texture Tool を使うのが無難なようです。
DirectX SDK の一部として提供されているので、
SDKをインストールする必要があります。
インストール時に項目を選択できるので、、
余計なものは導入せずにやれると思います(^_^;)

DDSでは、
Surface Format として様々なタイプをサポートしていますが、
DXT1, DXT3, DXT5 のいずれかを選択すれば良さそうです。

・DXT1 アルファなし or 1bitアルファ
・DXT3 変化が急なアルファ値
・DXT5 変化が緩やかなアルファ値

アルファは透過度のことです。
変換の際にどう判定すべきか自信がなかったので(^_^;)
単純に、不透過なら DXT1 、
透過成分があれば DXT5 として変換しました。

実装するには、
ImageUtils.loadCompressedTexture を
使って読み込むのが手っ取り早そうです。
なお、
three.js では、DXT2、DXT4 はサポートされてないようです。
DXT2 や DXT4 はあまり使われない?

ところで、
PMXでのテクスチャは BMP,PNG,TGA とかが使われています。
PmxEditorとかで編集して拡張子を書き換えるのが手っ取り早いのですが、
あまりスマートではないと思うので(^_^;)
DDS をロードさせるために、拡張子を読み替える仕組みを入れました。
TGA だったら DDS にリネームしてロードするという感じです。
任意に置き換えできるようにしました。
たとえば以下の様な感じに指定します。

textureAlias = { bmp:'dds', png:'dds', tga:'dds' };

ということで、やってみました。
・・・がしかし、
なんか gl なエラーがたくさん出る
・・・うむぅ。
原因不明だけど、three.js を最新にしたら
うまく行くかもしれない感じがして来たので(^_^;)
この際、ベースのリビジョンを思い切って変えてみることにしました。
現状のベースは r58 で最新は r67。
気がつけば結構進んでたりもしますしね。
ただ、
以前に r51 から r58 にした時の経験から言うと、
three.js はリビジョンが上がると
後方互換性が失われていることがままあるので、
変更点をチェックして、
それに対応させるのが結構手間暇かかったりするのが
難点だったりします。

そんなわけで、次回に続きます(^_^;)

three.jsで遊んでみる(27)

 プログラム,
 公開日:2014年5月14日 / 更新日:2014年9月22日

残念ながらいまだ、
うまく再現できないPMXがあったりします(^_^;)
今回、改善や対応が少し進んだりしたので、
書いてみたいと思います。

1つ目は、物理演算の改善。

物理演算の剛体には、
大きく分けて静的と動的の2つがあり、
拘束条件によって2つの剛体を結び付けるようになっています。

MMDでは、拘束条件はジョイントと呼ばれ、
剛体には対応するボーンが設定されています。
MMDの剛体は以下の3タイプとなっています。

・type0:ボーン追従(静的)
・type1:物理演算(動的)
・type2:物理演算(動的)+ボーン位置合わせ

特徴的なのが type2 で、
当初はよく分からないので type1 として扱っていましたが、
最終的にはこちらで書いたように理解しました。
要するに、拘束条件のアンカーにロックさせるために、
強制的にボーンの位置を適用させる動的剛体ということになります。
位置はボーンのを、回転は動的剛体のを適用するわけです。
私の実装においては、
これは相手の剛体が静的である場合に成立します。
つまり、type2の相手はtype0である必要があります。
さらに対応するボーンの親子関係が、
type0 -> type2 でないとうまく行きません。

ところが、PMXファイルの中には、
剛体のジョイント関係が次のようになっていることがあります。
type0 -> type2 -> type2 -> type2 …
例えば、頭から連なる髪とかで見受けられます。
このままでは、固まったようになってしまい、
うまく動作してくれませんでした。
そこで、親側が静的剛体で無い場合は、
子側のボーン位置合わせは無効にするようにしました。
つまり以下のように強制的に変更するようにしました。
type0 -> type2 -> type1 -> type1 …
これで、期待したような感じになってくれました。
MMD的には厳密でないと思いますが、まぁ良しとしましょう(^_^;)

2つ目は、「付与」変形の対応。

あるボーンの変形量に他のボーンのを率を掛けて加算するのが「付与」です。
変形量は移動または回転です。
対象ボーンをA、参照するボーンをBとして、
Aのローカルな変形量 += Bのローカルな変形量 × 付与率
と素直にやってみたのですが、どうにもうまく行きません。
調べてわかったのは、
モーションによる変形の差分を適用する必要があるようです。
つまり、
ある描画フレームとその次のフレームでの変形量の差分を求めて、
それに対して付与を計算しないとダメなようです。
要するに、以下の様なことになるようです。
Aのローカルな変形量 += Bのローカルな変形量の差分 × 付与率

2014年9月21日追記。
付与の実装方法が間違っていました(^_^;)
詳しくはこちら

また、
付与については変形順序に注意です。
PMXにはボーンの変形順序が規定されているのですが、
今までは無視しても動いていました(^_^;)
three.js内部で行われる部分については、
制御するのが困難だったというのも理由だったりします。
ただ付与だけは対応しておく必要がありそうです。

さて、
テストでは以下のデータを借用させていただきました。
モデルはこちら、モデルモーションはこちら、カメラモーションはこちら
ということで動作を参照するにはこららからどうぞ。

なんか最近やたらとサーバーが重いことがあります・・・。
激しく気長に待ってみてください(^_^;)

WebGL対応のブラウザで見てください。WindowsのChromeで確認しています。PCパワーもそれなりに必要となるかもしれません。反応がない場合は、リロードして再度試してみてください。

20140514.001

three.jsで遊んでみる(26)

 プログラム,
 公開日:2014年4月26日 / 更新日:2014年5月2日

G-Tuneの公式キャラクター「Tuneちゃん」の
MMDモデルデータが配布されたそうです。

うまく表示できないらしいので調べてみたところ、
マテリアルのアルファ値(透明度)に関する処理が不十分だったようです。
アルファ値がゼロ、つまり透明なのに表示されてしまう不具合がありました。
なお、現状ではマテリアルのモーフィングには対応していないので、
それを利用した表情アニメは再現できていません。

それから、
「髪裏」「髪裏2」というマテリアルの輪郭線表示をONにすると、
なぜか輪郭線描画が髪の表側に微妙にはみ出るような感じになってしまいます。
残念ながら原因不明・・・う~む。
なのでPMXを編集してそれらをOFFにすることで対処しました(^_^;)

「Tuneちゃん」は頂点、面、物理演算の剛体などの数が
標準的なミクさんに比べて2~3倍あります。
特にテクスチャは数が多い上に解像度も高めな感じ。
WebGLには少々荷が重そうなので、
大きいやつは縦横50%にし、
さらに「PNGoo」というソフトでサイズ削減しました。

ということで動作を参照するにはこちらからどうぞ。
モーションデータは借用しています。詳しくこちらを参照してください。
なお、
shadow などのチェックを全て外し、
outline scale をゼロにすると動作的に一番軽くなります。

なんか最近やたらとサーバーが重いことがあります・・・。
激しく気長に待ってみてください(^_^;)

WebGL対応のブラウザで見てください。WindowsのChromeで確認しています。PCパワーもそれなりに必要となるかもしれません。反応がない場合は、リロードして再度試してみてください。

20140426.001

three.jsで遊んでみる(25)

 プログラム,
 公開日:2014年4月13日 / 更新日:2014年4月20日

前回のデモを何気に見ていたら、
カメラが大きく切り替わる時に
余計なフレームがほんの一瞬だけ交じっていることに気がつきました。
少々気になったので調べてみた次第です。

MMDのモーションデータであるVMDでは、
タイミングを示すキーはフレーム番号で管理されており、
1フレーム=1/30秒、つまり30fpsを想定しています。

一方、
three.jsでの私の実装ではフレームではなく秒で管理しています。
VMD的にはカメラを1フレームで瞬時に切り替えるようになっていても、
1/60秒で駆動すると2フレームを生成することになるため、
中間フレームの挿入によって瞬時に切り替わった感じがしないわけですね。
処理上は間違っているわけではないのですが、
視覚的には少々気になる場合もありそうです。

単純に30fps相当で駆動しようかとも思ったのですが、
60fpsで回っていても30フレームしかないことになるので、
流石にそれはないですよね(^_^;)

そこで2つのキーの間隔が1/30秒以下の場合に限って、
中間フレームを生成しないようにすることで
この現象を回避するようにしてみました。
カメラとライトのモーションだけに適用しました。

ということで動作を参照するには以下からどうぞ。
改善前はこちら
改善後はこちら
よく見ないと違いが分かりにくいかもしれません。
60fps出ないとさらに分かりにくいかも(^_^;)
デモの170秒あたりが比較的確認しやすいかな。

なんか最近やたらとサーバーが重いことがあります・・・。
激しく気長に待ってみてください(^_^;)

WebGL対応のブラウザで見てください。WindowsのChromeで確認しています。PCパワーもそれなりに必要となるかもしれません。反応がない場合は、リロードして再度試してみてください。