前回の続きです。
頂点モーフによる表情アニメを実現すべく、
シェーダーとかいじってみたりしてました。
だいたい期待した通りな感じになってくれたのですが、
なぜか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 なミクさんの結果はこれです。
ちなみに、
頂点や面情報で構成されたmeshがレンダリングされる際、
各要素は属するmaterial単位に分割されて処理されます。
materialに関連する情報を切り替えるのはコストがかかるので、
最適化するためにこのような処理が行われているのだと思います。
これに関連して、morphについて注意すべきことがあります。
それは、頂点morphはmaterial毎に同時に最大8つまでしか行えないことです。
しかも、法線morphも行う場合は頂点、法線それぞれ4つまでとなります。
この制限を超えるような場合は、
重み値のより小さいものを対象外とすることで処理されるようです。
ところで、
morphに関するシェーダーのコードは以下のようになっています。
#ifdef USE_MORPHTARGETS
vec3 morphed = vec3( 0.0 );
morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];
morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];
morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];
morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];
#ifndef USE_MORPHNORMALS
morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];
morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];
morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];
morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];
#endif
morphed += position;
#endif
MMDではオフセット値として扱っているので、
頂点値ではなく、オフセット値として渡せば
計算を減らせると思って、
当初は以下のようにやってました。
#ifdef USE_MORPHTARGETS
vec3 morphed = position;
morphed += morphTarget0 * morphTargetInfluences[ 0 ];
morphed += morphTarget1 * morphTargetInfluences[ 1 ];
morphed += morphTarget2 * morphTargetInfluences[ 2 ];
morphed += morphTarget3 * morphTargetInfluences[ 3 ];
#ifndef USE_MORPHNORMALS
morphed += morphTarget4 * morphTargetInfluences[ 4 ];
morphed += morphTarget5 * morphTargetInfluences[ 5 ];
morphed += morphTarget6 * morphTargetInfluences[ 6 ];
morphed += morphTarget7 * morphTargetInfluences[ 7 ];
#endif
#endif
morphはこれでうまく行ってくれたのですが、
なぜか ShadowMap がおかしなことになってしまうのです。
原因は不明ですが、余計なことはせずに、
three.jsでの流儀にしたがって、
morphオフセット値は素直に頂点に加算した方が良さそうです(^_^;)
次回は、
toon shading や edge 処理とかをやる予定。