MMD on WebGL やっと Lat 式を表示できた
MikuMikuStudio を作ってる chototsumoushin さんに、「culling がおかしいんじゃね?」的な助言をいただいたので、頑張ってみた。
↓デモ。10月20日現在、Lat 式になってる。たぶんあとで標準モデルに戻す。「コッチミンナ」目をオンにしてあるので、どのアングルを向いてもこっちを見てくれる。
Mac の Chrome で確認したら酷いことになってた… あとで直す。
Mac でもちゃんと見られるようになった。pow(0.0, 0.0)
が GLSL で未定義なので、0.0だったとこを0.001にした。
ここから本題。
アルファブレンディングについて
この前シェーダーがほぼ完成したと書いた時は↓こんな感じ。
前やってたことは、まず全部の材質を drawElements して、次に cullFace(FRONT)
して(各頂点を法線方向にちょっとずらして)エッジを描画するという感じだった。ただし、これだけだと材質の裏面が黒くならないので、gl_FrontFacing で裏面を判断してそのときも黒くしてたんだけど、こんなヘンテコなことするのはおかしいよなーと思ってた。
今回は、まず cullFace(BACK)
して材質を drawElement して、次に cullFace(FRONT)
してエッジを同じように描画するようにした。これだけで裏面も黒くなる。こんな簡単なことに気づかなかったなんて…
これで↓こうなる。Lat 式は顔の作りが特殊で、法線が後ろ向いてるらしくてどうたらこうたら(よく知らない)のため、顎が黒くなってたっぽい。
次に、MikuMikuStudio (MMDLoaderJME) のコードで setBlendMode(BlendMode.Alpha)
というのがあったので、blending というのをやってみる。これについてはこのへんを参照。
- テクスチャでアルファ(透過)を使用する - 強火で進め
- ブレンド処理 - OpenGL de プログラミング - livedoor Wiki(ウィキ)
- ブレンディング::計算式の設定 - OpenGL de プログラミング - livedoor Wiki(ウィキ)
要はこれでアルファブレンディングというのをしないと、gl_FragColor
にせっかくセットしたアルファの効果が発揮されなくて、常に最前面の面のみが見えてる状態になってるらしい。
WebGL には BlendMode などというのは無くて、
gl.enable(gl.BLEND); gl.blendFunc(...);
という感じでやる。上の Wiki の blendFunc の例を見ながら
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
とすると…
おお!顔が出てきた。
…しかし、よく見ると顔に影が無い。もっと(照明を落として)よく見ると、
影になる部分が逆に明るくなってる。
実は Lat 式の顔には影ができないようになってて。影に見える部分は黒っぽい透明の材質が貼られているだけ。
黒っぽい透明なのになんで白くなるのか…と数時間悩んだところ、WebGL の gl.blendFuncSeparate
というのを発見。
なるほどそういうことか!ってことでやってみたら、
大成功。
つまり、色成分とアルファ成分を別々にブレンドするということだった。
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); // これは↓こういうこと // ピクセルの色(rgba) = 下層の面の色(rgba) * (1 - 上層のアルファ) + 上層の面の色(rgba) * 上層のアルファ
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.SRC_ALPHA, gl.DST_ALPHA); // これは↓こういうこと // ピクセルの色(rgb) = 下層の面の色(rgb) * (1 - 上層のアルファ) + 上層の面の色(rgb) * 上層のアルファ // ピクセルのアルファ = 下層の面のアルファ + 上層の面のアルファ
そりゃこうじゃないとおかしいね。