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

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

 プログラム,
 公開日:2012年11月22日 / 更新日:2013年10月24日

前回からだいぶ日が経ってしまいましたが、
ミクさんに物理演算を適用すべくチマチマやっておりました。

ただ残念ながら期待したようにはなってくれてません。
よく分からなかったり、うまく行ってくれなかったり、
自分の力不足な所が大きいと思うのですが、
JavaScriptでは荷が重いのかもしれません。
しばらく動作させるとハングアップしたりします・・・(^_^;)

これ以上はうまく行きそうにない感じなので、
中途半端ではありますが、この辺りで〆にしようと思います。
それでも、それなりに得たものがないこともないので、
それをまとめておこうと思います。

前回でも触れましたが、
three.jsで物理演算をやるには、
BulletのJavaScript版であるammo.jsを使いますが、
three.jsからは直接ammo.jsを使いません。
physi.jsを仲介してその機能を利用する形になります。
ammo.jsはWebWorkerを利用して別スレッドで動作するようになっています。
physi.jsおよびphysijs_worker.jsによって、
スレッド間のイベントによる
メッセージ送受信で機能する仕組みになっています。
物理演算は別スレッドでやって、
どうにか性能を稼ごうという仕様なのだと思います。

three.jsでの使い勝手を損なわないように、
physi.jsはScene,Material,Meshを拡張する形で実装されています。
Meshは物理演算で言うところの剛体に対応しており、
Box,Sphere,Capsuleなどの形状をサポートしています。
注意点としては、Meshを生成しないと剛体を設定できないことです。
つまりphysi.jsでは、見える物体と剛体は形状が一致しているという
前提に立っています。

しかし実際には、まずMeshが先にあって、
そこへ抽象的な剛体を設定するという順になると思います。
これはちょっと困りました。
ミクさんはまずモデルの形状があって、
そこへボーンや剛体などが設定されているからです。

そこでシーンを分けることで対応することにしました。
つまり通常のシーンにはモデルを登録し、
もう一つの物理演算なシーンには剛体Meshを登録し、
演算結果を2つのシーン間で相互に反映させるというやり方です。

続いて、
MMDなファイル内にある物理演算な情報が、
物理エンジン側の何に対応しているかを調べました。
MMDで言う所のrigidはその名の通り剛体を示しており、
jointはconstraint(拘束条件)に対応していることが分かりました。
こちらのBulletに関する日本語のページを参考にしました。
なお、MMDは拘束条件として6DofSpringというのを採用していますが、
physi.jsではサポートしてなかったので6Dofで代用しています。
(ammo.jsはBulletの機能を全て網羅しているはずなので、physi.jsを改造すれば6DofSpringも対応可能だとは思います)
問題なのは「衝突フィルタリング」です。
現状のphysi.jsではサポートしてないので要改造となります。
以下のようにしました。

衝突フィルタリングでは group と mask の2つのパラメータを使いますが、
MMD側の数値を変換する必要があります。
そのまま渡してもうまく行ってくれないので、かなり悩まされました(^_^;)
mask はそのままでよいのですが、
MMD側のgroupはビット番号なので、以下のようにシフト演算が必要です。
physics.group = 1 << mmd.group; さて、そんなこんなで どうにかパラメータをセットアップするところまでは行けたのですが、 その後が大変でした。 ときに、 剛体には大きく分けて3種類あります。

・動的(動きのある)剛体

正の質量。毎フレームごとにワールド変換行列が更新される。

・静的剛体

質量ゼロ。衝突はするが、動かない。

・Kinematic剛体

質量ゼロ。ユーザーが動かすことができる。動的オブジェクトを押したりすることはできるが、オブジェクトからは影響を受けない。つまり一方通行。

MMD側も剛体の種類は3種類ですが、
「静的」、「動的」、「動的&ボーン位置合わせ」
ということになっています。
しかし3番目のがよくわかっていません。
質量ゼロなkinematicとは矛盾するので、これには該当しないと思われます。
結局どうすべきか分からなかったので単に「動的」扱いとしています。

物理演算を適用する手順は以下の様な感じになると思っています。

  1. 頂点モーフ処理。
  2. ボーンによるアニメーション処理。
  3. IK処理。
  4. 対応するボーンから取得した位置と回転を静的剛体Meshに反映して物理演算側に通知。
  5. 物理演算の結果、動的剛体Meshの位置と回転を対応するボーンに反映させる。

注意すべきは、剛体は全てグローバルな座標系で扱われることです。
全ての剛体Meshはシーン直下にぶら下がっています。
というかそうしないと物理演算の対象とならないのです。
親子関係とかないので剛体Meshの位置と回転はグローバル座標系となります。
剛体が位置する座標と剛体の重心点は一致するようになっていることにも注意が必要です。
さて、剛体にはそれぞれ対応するボーンが設定されていますが、
ボーンは親子関係を持ち、それぞれにローカル座標系が存在します。
さらにボーンはバインドポーズを基準とした変位量でアニメーションします。
バインドポーズとはモデルの初期の形状のことを指しています。
ミクさんで言えば、両腋を45度くらい開いて立っている状態に該当します。
バインドポーズ時のボーンの回転量は全てゼロとして設定されていますが、
対応する剛体は個々にグローバル座標系における位置と回転値を持っています。
そんなわけで、色々と変換が面倒そうなのでめまいを起こしそうです(^_^;)

物理演算で必要な手順をもう少し詳しくまとめてみます。
モデルを登録する側をシーンA、剛体を登録する側をシーンBとします。

  1. シーンAに属するボーンから取得した位置と回転値を変換して、
    シーンBに属する静的剛体Meshに適用する。

  2. 変更したことをマークして物理演算の更新処理を呼び出す。
  3. 物理演算が更新されたイベントを受けて、
    シーンBに属する動的剛体Meshの位置と回転値を変換して、
    シーンAに属するボーンに適用する。

という流れになります。

この際の「変換」がなんかややこしくて、
正直、自分のやり方はなんか怪しい感じがしています(^_^;)
また、本来なら物理演算の更新処理を呼び出したら、
その結果を待ってから次のステップへ進むべきなのですが、
これだとWorkerを利用して別スレッドにしている意味がなくなってしまうので、
シーン間での変更のやり取りは行いますが、
同期はとってなかったりします(^_^;)
そんなわけでシーンA側とシーンB側でタイミングがあっていません。

さて、
そんなこんなで激しく中途半端な感じではありますが、
ミクさんに物理演算を適用した結果はこれです。
なお、ワイヤーフレームで表示されているのが剛体です。
なんかそれっぽいことをやっているという
雰囲気だけでも感じてもらえれば幸いです(^_^;)

※2013.2.6追記
現状では物理演算の結果をミクさんにうまく反映させることが出来ていません。
また、PhysijsはWorkerを使わない版に改造しています。

諸般の事情でちょっとローディングに時間がかかりますが、しばらく待ってみてください。なお、ChromeなどのWebGL対応のブラウザで見てください。PCパワーもそれなりに必要となるかもしれません。あまりにも時間がかかり過ぎるようならば、リロードして再度試してみてください。場合によってはハングアップすることがあります。

mytest13

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

 プログラム,
 公開日:2012年10月19日 / 更新日:2014年8月27日

前回の続きです。

今回は、toon shading や edge 処理とかをやります。
いよいよシェーダーをいじります。

WebGLというかOpenGLでは、
GLSLというシェーダー言語でプログラミングします。
だいぶ昔、DirectX9の頃に
HLSLをちょっと使ったことはあるのですが、
GLSLは初めてだったりします。

ときに、
シェーダーには大きく分けて、
vertex shader と fragment shader の2つがあります。
後者は pixel shader とか呼ばれたりすることもあります。
それぞれ頂点、ピクセルを対象として演算を行います。

ところで、
前回までのテストでは、
three.js で用意されている MeshPhongMaterial、
いわゆる Phong シェーディングを使っていました。
そこで、
これによって生成されるGLSLコードを改造して、
ShaderMaterialを継承するという形で
toon shading や edge 処理を実装することにしました。
クラス名は MMDMaterial としました。

さて、
toon shading は cel shading とか「アニメ塗り」とか言われる技法です。
光源処理による連続的な陰影を、
テクスチャマップを応用して段階的なものにしてしまうことです。
技術的にはこちらとか参考になります。
MMDでは toon マップ用のテクスチャがプリセットで10個用意されています。

toon は拡散光(Diffuse)に応じた処理をします。
MeshPhongMaterialなGLSLコードでは拡散光の処理は以下の様な感じに、
fragment shader で処理されます。
法線と光線方向との内積から、陰影が計算されます。
※注意:実際のコードとは異なります

これに toon shading を導入すると以下のようになります。
toonMap なテクスチャはあらかじめセットされているものとします。
法線と光線方向との内積から、
テクスチャのV方向を参照し乗算することで段階的な陰影にするわけです。
U方向は参照しませんが、このような手法を使うと、
toon と 輪郭強調(輪郭抽出風味)を同時に出来たりしてお得かも(^_^)

上のような処理を、設定されているlightの数だけ行なって、
(ただし ambient light は除きます)
合計した結果がtoonな拡散光成分となります。

次に edge ですが、これは輪郭線を表現する処理です。
輪郭線を描くにはいくつか手法があるようです。
例えば、いったんオフスクリーンでレンダリングし、
こういった感じのフィルターで輪郭線を抽出して
後処理で合成するといった感じです。
後処理な手法は別途バッファが必要になったりするので、
もっと簡便な手法を採用します。
以下の様に描画を2回に分けて行います。

  1. 普通に描画。
  2. vertex shader では頂点を法線方向に少し引き伸ばし、
    fragment shader では裏面を特定色で描画する。

これだけでそれっぽい輪郭線が表現できます。うまく考えたものですね。
今まで袖の内側やスカートの内側が描画されていなかったのは、
両面ポリゴン化し忘れているわけではなく、実は
edge 処理による裏面描画によって自動的にうまく行くようになってたりします。

MMDでは引き伸ばす量を「エッジ太さ」として
0~2くらいに設定しているようです。
おそらく単位はピクセルで、
輪郭線の太さを固定する手法を用いているものと思われます。
しかし今回は単純にローカル座標系で処理するので、
視点からの距離に応じて太さがスケールされます。
先の数値のままだと伸ばし過ぎることになりそうですので、
重みを掛けて処理します。
やってみた感じでは 0.02 ~ 0.03 程度にするのが良さそうです。
ちなみに通常描画で頂点を引き伸ばすと、
デブっちょな感じのミクさんになったりして面白いです(^_^;)
なお、
視点からの距離に関わらず輪郭線の太さを固定する方法は
まだよく分からないので、後々どうにかする予定です(^_^;)

ということで、
実装するには描画を2回に分けて行う必要があります。
マルチパスな描画ということになります。
mesh全体をシェーダーを切り替えて2回描画すればいいのですが、
これは処理コスト的に問題となります。
まずシェーダーを切り替えるということは、
関連付けられているmaterialも切り替えることになってしまいますので、
meshを再設定するようなことになってしまいます。
またmesh単位で描画を2回行うのもコストが気になります。
というのも SkinnedMesh の場合は、
内部的には複数のmaterialから構成されることになるからです。
前回でも書きましたが、
頂点や面情報で構成されたmeshがレンダリングされる際、
各要素は属するmaterial単位に分割されて処理されます。
materialに関連する情報を切り替えるのはコストがかかるので、
同じmaterialを使うのであれば、切り替えずに描画したいわけです。

ただ残念ながら、
現状の three.js ではこのようなマルチパス描画をサポートしていません。
オフスクリーンなバッファを使う後処理なマルチパスなら、
Composerという機能が用意されているのですが・・・。
仕方ないので three.js 本体を改造することにしました。
具体的には WebGLRenderer 内の renderBuffer() を以下のようにします。
※注意:実際のコードとは異なります。

改造のポイントは、renderMesh を複数回行えるようにして、かつ
前後でmaterialに登録された関数をコールバックできるようにしたことです。
フラグなパラメータ(uniform)を設定するようにすれば、
シェーダー内で if によって処理を切り分けることができるようになります。
つまり、シェーダーそのものを切り替えずに済むようになります。
ちなみに、HLSLでは言語レベルでpassを設定できるので、
もっとスマートに記述できると思います。

ついでに、
シェーダーのプログラムをセットする setProgram() にも手を入れました。
setProgram() ではシェーダーのパラメータ(uniform)を WebGL へ渡す際に、
material がどんなクラスのインスタンスなのかをチェックして、
それに応じたパラメータをセットアップするような作りになっています。
そのためこのタイミングにおいては、
three.js が認識してないパラメータをセットアップすることができません。
そこで以下のように、
material に登録された関数をコールバックして、
カスタマイズしたパラメータをセットアップできるように改造しました。

ということで toon shading と edge 処理が実現できました。
ただ edge はコントラストが強くなるため、ジャギーが気になります。そこで
three.js で用意されているFXAAなアンチエイリアスを後処理で施しました。

あと今回、
material 単位で影の濃さを調整できるようにもしたので、
セルフシャドウを有効にしました。

さて、
なんか文章が長くなりましたが、お待たせしました(^_^;)
bone skinning + IK + morph + toon + edge + self shadow なミクさんの結果はこれです。

諸般の事情でちょっとローディングに時間がかかりますが、しばらく待ってみてください。なお、ChromeなどのWebGL対応のブラウザで見てください。PCパワーもそれなりに必要となるかもしれません。あまりにも時間がかかり過ぎるようならば、リロードして再度試してみてください。

mytest10

あ、そうそう、あと一言。
今回、GLSLなコードを組み込んだわけですが、
JavaScript には heredoc がないんで、
ソース行を文字列化した配列をjoinするしか方法がないのが
なんか激しくメンドイです。
python みたいな heredoc があればラクなんだけど・・・。

さて、
残りは物理演算。なんか手強そうです(^_^;)
いまだ完全にはうまく行ってないIKもどうにかしたいですね。

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

 プログラム,
 公開日:2012年10月11日 / 更新日:2013年10月24日

前回の続きです。

頂点モーフによる表情アニメを実現すべく、
シェーダーとかいじってみたりしてました。
だいたい期待した通りな感じになってくれたのですが、
なぜかShadowMapがおかしくなる・・・。

そんな折、
ふとthree.jsのGitHubを覗いてみると、
リビジョンが r51 に上がっている。
しかも skinning + morph に対応したっぽいことが書いてある。
それをどうにかしようと色々いじっていた苦労がぁ・・・(^_^;)

マメにthree.jsのGitHubをチェックする必要がありそうですね。
というか、GitHubで更新あったら通知してもらえると嬉しいかも。
どうやればいいんだろ?

それはともかく、
確かにExampleにこんなのが増えてます。
こちらでは変化の加減を調整できたりしますね。

ということで、シェーダーとかいじらなくても、
MMDなファイルから取り出したmorph情報を
キーフレームにしたがって駆動するだけで良いことになります。
morph頂点の情報はthree.jsでは morphTargets という名前になっています。
中身は頂点データの配列です。
x,y,z なので頂点数×3個の値が並ぶことになります。
さらにこれが表情の数だけ存在するので、
データ量はわりと大きくなることが想像できます。

ところで、
MMDでは変化する頂点のデータだけを保持しています。
具体的にはmorphする頂点の番号と位置オフセット量です。
three.jsで扱うには全頂点の数だけ列挙しなければなりませんが、
ミクさんのモデルは頂点数が9000くらいあるので、
ローディング時間を考えるとファイルサイズが気になります。

そこで、
MMDのmorphデータを素直にjson化して、
それを独自ローダーで morphTargets に展開する手法にしました。

ということで、
bone skinning + IK + morph なミクさんの結果はこれです。

諸般の事情でちょっとローディングに時間がかかりますが、しばらく待ってみてください。なお、ChromeなどのWebGL対応のブラウザで見てください。PCパワーもそれなりに必要となるかもしれません。あまりにも時間がかかり過ぎるようならば、リロードして再度試してみてください。

mytest9

ちなみに、
頂点や面情報で構成されたmeshがレンダリングされる際、
各要素は属するmaterial単位に分割されて処理されます。
materialに関連する情報を切り替えるのはコストがかかるので、
最適化するためにこのような処理が行われているのだと思います。

これに関連して、morphについて注意すべきことがあります。
それは、頂点morphはmaterial毎に同時に最大8つまでしか行えないことです。
しかも、法線morphも行う場合は頂点、法線それぞれ4つまでとなります。
この制限を超えるような場合は、
重み値のより小さいものを対象外とすることで処理されるようです。

ところで、
morphに関するシェーダーのコードは以下のようになっています。

MMDではオフセット値として扱っているので、
頂点値ではなく、オフセット値として渡せば
計算を減らせると思って、
当初は以下のようにやってました。

morphはこれでうまく行ってくれたのですが、
なぜか ShadowMap がおかしなことになってしまうのです。
原因は不明ですが、余計なことはせずに、
three.jsでの流儀にしたがって、
morphオフセット値は素直に頂点に加算した方が良さそうです(^_^;)

次回は、
toon shading や edge 処理とかをやる予定。

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

 プログラム,
 公開日:2012年9月29日 / 更新日:2013年10月24日

前回の続きです。

次は頂点モーフによる表情アニメでもやろうとかと思ったのですが、
BoneによるSkinningと頂点モーフが
現状のthree.jsでは共存できないことが分かりました。
シェーダー関連をいじってどうにかしないといけない感じです。
なんか手間ヒマ掛かりそうなので、IKを先にやってみることにしました。
基本的にはBoneのマトリクス操作だけで済むはずですので、
three.jsに対する追加や修正はそんなに大きくならないと思われます。

ときに、
IKとは Inverse Kinematics、逆運動学とか言うそうです。
前回やったBoneによるSkinningは、言わばFK。
Forward Kinematics、順運動学とか言うっぽいようです。

IKという言葉とか、それがどういうモノなのか、
というのはそれなりに知ってはいましたが、
実際にやってみるのは初めてだったりします。

さて、まずは
MMDなファイル内のIK情報を変換してJSONへ追加する作業を行いました。
three.jsオリジナルのJSONLoaderでは、
MMDなIK情報は当然サポートしてないのでLoaderも独自に作りました。
といっても、中身はJSONLoaderとほぼ同じだったりしますが。

つづいて、
IKはどうやってプログラムすればいいのかググって情報収集しました。
そしたら既にWebGLでMMDをやられている方を発見しました!
しかもイチからスクラッチで作ってしまったようです。凄いっす!!
ソースも公開されているので参考になりますね。
アルゴリズム的にはこちらがとても参考になります。
サンプルプログラムが公開されているのもありがたい。
手法としてはCCD-IKとかParticle-IKとかがあるようです。
MMDのIK情報には演算の繰り返し数というのがあるので、
CCD-IKを採用すれば良さそうです。
それからソースコード的にはこちらこちらの第8回が参考になります。

ということでIKやってみました。結果はこれです。

諸般の事情でちょっとローディングに時間がかかりますが、しばらく待ってみてください。なお、ChromeなどのWebGL対応のブラウザで見てください。PCパワーもそれなりに必要となるかもしれません。あまりにも時間がかかり過ぎるようならば、リロードして再度試してみてください。

mytest8

なお、スポットライトはミクさんを追いかけるようにしてあります。

とりあえず、だいぶそれっぽく踊ってくれるようになりました(^_^)
でも、まだ十分にうまく行ってない感があります。
特に足もとが床にめり込んでしまうのが気になりますね。
IKターゲットを追いかければ、
本来はそういうことにはならないはずだと思うのですが・・・。
床にめり込まないようにするのは、
別途処置が必要なのかもしれません。とりあえず保留としておきます。

あと、
IK解決はそれなりに計算量が多いようです。
ブラウザでプロファイルをとってみると処理時間は多めなようです。
ベクトルや行列演算などを繰り返すので、しょうがない感じではあるのですが
差がある一定以内になったら計算を打ち切る等の
対策をやった方がいいかもしれませんね。
あるいは、Workerスレッドを使って並列にやらせる手もありそうですが、
どこかで同期を取る必要があるので手間かかるかもしれないですね。

次回は表情モーフに挑戦する予定です。
ついにシェーダー周りをいじくることになりそうです(^_^;)

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

 プログラム,
 公開日:2012年9月20日 / 更新日:2013年10月24日

前回で書いたように、
MMDなデータをBlender経由でthree.jsで扱えるjsonへ変換するのは
どうもうまく行かない感じです。

そこで自前で変換ツールを作ることにしました。
Blenderのimporterやexporterは参考になるので、
pythonで作ってみることにしました。
ちなみにpython使うのは初めてだったりします(^_^;)

BoneによるSkeletalなアニメーションは、
こんな感じにthree.jsで動いているので
どうやってるのかを調べることから始めました。

データファイルはthree.jsで扱いやすいようなjsonになっており、
テキストエディタなどで中身を見ることができます。
内容は以下の様な感じ。

1)マテリアルの配列
2)頂点の配列(x,y,z,x,y,z,…)
3)法線の配列(x,y,z,x,y,z,…)
4)UVの配列(u,v,u,v,…)
5)面データの配列
6)Bone情報の配列
7)頂点に対するBoneインデクスの配列
8)頂点に対するBone重みの配列
9)アニメーション情報(Bone毎のキーフレームの配列)

面データは、まずフラグ値があり、
それによって続く値が決まるようになっています。
詳しくは”JSONLoader.js”を参照してみてください。
7)と8)は常に2個セットで扱われるようになっています。
つまり,一つの頂点に対するBoneは2個までとなっています。
例えば、ある頂点に影響するBoneが#2,#7だったとすると、
インデクス=[2,7] 重み=[0.25,0.75]とかになります。
重みの合計は基本的に1.0になります。
頂点に影響するBoneが一つだけの場合は、
インデクス=[3,0] 重み=[1,0]とかになり、
2つ目の値はゼロになるようになっています。
これだと重みゼロで積和することになるので
無駄な計算となるはずですが、
シェーダーで扱いやすいように、この構成なのだと思います。
アニメのキーは位置、回転(クオータニオン)、拡縮の3要素から成っており、
変化のある要素のみが記述されるようになっています。

ということで、どういうjsonを作れば良いか大体わかったので、
MMDなデータファイルから変換する作業へ進みます。

MMDなモデルファイルであるPMDの仕様は公式には公開されていないようですが
ググってみると解析された結果が見つかります。
ところでモデルファイルにはもう一つPMXというのがあります。
PmxEditorに同梱されているファイルに詳しい仕様が記述されています。
さいわいPMDはPMXへ変換することが出来ますので、
変換ツールではPMXファイルを扱うことにしました。
モーションファイルであるVMDの仕様も非公開のようですが、
解析情報は検索したら見つかりました。

さて、
そんなこんなで紆余曲折を経て、
ある一定の成果を得ることが出来ました。
変換時の留意点をまとめてみます。

  • PMXにおけるBoneの位置はルートからの積算になっているので、
    three.jsで扱うには親からの相対値へ変換する必要がある。
  • 左手座標系から右手系への変換が必要。
    位置や法線はZ値の符号を反転させるだけ。
    回転(クオータニオン)の場合はXとYを反転させるとうまくいく感じ。
    あるいはZとWを反転でもいいっぽい。
    面の頂点並びはA->B->CからC->B->Aに反転。
  • PMXにおける Bone Deformation は BDEF1(ボーン1つ)、BDEF2(ボーン2つ)、BDEF4(ボーン4つ)、SDEF(ボーン2つでspherical)の4種類がサポートされているが、three.jsで扱えるように全てBDEF2相当に変換する。ただ、PMDにはBDEF1かBDEF2しかないはず。
  • VMDのBoneキーフレームのposには、
    相対化したPMXでのBoneの位置を加算する必要がある。
  • three.jsにおけるアニメのキーは最低2つ必要で、
    なおかつ最初と最後にはpos(位置),rot(回転),scl(拡縮)が揃って無いとダメ。
  • Boneキーの補間はMMDでは4つの制御点による3次ベジェですが、現状のthree.jsではリニア補間しかない。ただ、4つのキーを使ったCutmull-Romな補間をサポートしている。

あと、
「頭」と「首」のBoneはそれぞれ個別に回転できるわけですが、
実際に再生させると「傾き過ぎる」ような感じになるので、
「頭」の回転量は強制的にゼロにして変換するようにしました。
なぜこういう現象になるのかよくわからないのですが、
とりあえず暫定処置としておきます。

ということで、
表情モーフ無し、IK無し、物理演算無し、toonシェード無し、エッジ処理無し、
Boneによるskeletalなアニメーションだけな再現なので
特に足のIKが無くて変なことになってますが、
とりあえずthree.jsで再現したミックミクな感じのモーションはこれです。
諸般の事情でちょっとローディングに時間がかかりますが、
しばらく待ってみてください。なお、ChromeなどのWebGL対応のブラウザで見てください。PCパワーもそれなりに必要となるかもしれません。

あと、何か変な線が見えてますが、
これはスポットライトの範囲を可視化したものです。

一応、影も出せるようになったのですが、
現状のthree.jsではマテリアル単位で影の濃さを調整できないので、
床影のみにしています。
ライト毎にしか影の濃さを調整できないので、
床影が認識できるような濃さにした上でミクさんにセルフ影を導入すると、
なんか「やり過ぎな影」になっちゃうんです(^_^;)

次は表情モーフあたりをやってみたいかな。

mytest7


※9月28日追記。
「傾き過ぎる」件ですが、変換ツールがバグっていたようです(^_^;)
隣接するキーの内容に同じのがあったら、
端折るようにして最適化したつもりがうまくいってなかったようです。
VMDのキーを素直に出力するようにしました。