前回の続きです。

頂点モーフによる表情アニメを実現すべく、
シェーダーとかいじってみたりしてました。
だいたい期待した通りな感じになってくれたのですが、
なぜか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に関するシェーダーのコードは以下のようになっています。

#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 処理とかをやる予定。

コメントする

メールアドレスが公開されることはありません。が付いている欄は必須項目です。