2011年9月17日土曜日

WebGLでスキニングは難しいです


進捗です。スキニングを試してみました(Demo)。
スキニングというのはボーンと呼ばれる骨に沿って皮膚が変形するというヤツです。画像で言うと赤いヤツがボーンですね。こいつには親子関係があって親のボーンが変形したら子どものボーンも変形するようになっています。
MikuMikuDanceには頂点ごとに「合成するボーン番号0」「合成するボーン番号1」「重み」がふられていて「ボーン0 * 重み + ボーン1 * (1 - 重み)」という計算をすべての頂点に対して施します。頂点ごとの処理ですので頂点シェーダの得意分野です。重みは固定値ですのでattributeとして渡せばいいのですが問題はボーンです。1フレームごとにアニメーションさせたければ1フレームごとに行列を更新しなければなりません。となるとボーンはattributeよりもuniformが適切です。ボーンと頂点は見ての通り「1:多」の関係ですので頂点はボーンへの参照を保持しておけばボーンを変更するだけでボーンに依存するすべての頂点を変形することができます。

実装の方法としてはいくつか考えられます。
1つはシェーダに行列の配列(uniform)を用意して頂点毎に「配列へのインデックス」と「重み」をもたせます(attribute)。試してみましたが無理でした(´;ω;`)ブワッ
もう1つは変換済みボーンをテクスチャに書き込みテクスチャから行列を読み出すという方法です。本家MikuMikuDanceもこの方法を使っているようです。・・・だけどこれも無理でした。どうやら現時点では頂点シェーダ内でテクスチャのサンプリングはできないようです(ANGLEが実装するかもしれないという噂ですが)。

となるとソフトウェアでスキニングするしかないわけです・・・。つまり頂点ごとにボーンの重み計算を行うわけです。このモデルは9kオーバーの頂点で構成されているので9kオーバーの行列演算が発生します。もちろん負荷もかかります(行列演算を1つ増やすだけでFPSがガクッと下がりました)。
しかし現時点ではコレ以外に方法も思いつきません。ハードウェアスキニングを実現する方法はないのでしょうか・・・?

2 件のコメント:

  1. > 1つはシェーダに行列の配列(uniform)を用意して頂点毎に「配列へのインデックス」と「重み」をもたせます(attribute)。

    試していませんが、おそらく uniform 数が足りないですよね。

    描画するときはマテリアル毎に draw してると思われます。なのでその draw で必要なボーンだけ頂点に対してインデックスを振りなおしてみてください。これで一度に影響するボーン数が相当抑えられると思われます。
    あとは頂点ブレンディングには転置された行列の三行だけでよいので、 4x3 行列にするか vec4 の配列を各行で dot で uniform 数を若干抑えられると思われます。
    さらに MMD モデルの場合はスケーリングなく各ベクトルが直行すると思われるので、クォータニオンと平行移動に分けて、頂点シェーダで行列を作ればさらに uniform 削減できるかもしれません(重いですが)

    がんばってください!

    返信削除
  2. コメントありがとうございます。
    テクスチャからボーン座標を読み取ることができたので今はこの方法を使用しています。
    ですがこの方法でうまく動かないという報告もいただいています。
    uniform数の制限にひっかかっているというアドバイスもいただきましたので参考にさせていただきたいと思います。

    ありがとうございました!

    返信削除