以下のサイトを見て面白そうだったので、WebGLでも円形ワイプエフェクトをしてみました。
円形ワイプはスーパーマリオなどでよく見かけたエフェクトですね♪
ワイプエフェクトの仕方
ワイプエフェクトをするにはステンシルバッファを使います。
ステンシルバッファとはマスク処理のようなもので、以下のサイトを読めば詳しくわかると思います。
・wgld.org - ステンシルバッファ
・wgld.org - ステンシルバッファでアウトライン
今回の実装の流れとしては、ざっくりと以下のようになります。
1)ステンシルテストを有効にする
2)円を描画する(マスク用の描画)
3)表示したいテクスチャを描画する
4)ステンシルテストを無効にする
というわけでまずは円の描画します。
WebGLでは円の描画ができないので、10度ずつの三角形を36枚描画してみました。
(この円にアンチエイリアスのシェーダー適用したらもっと綺麗になるかも)
・WebGL-円描画・・・drawArraysで円描画のコード
・WebGL-円描画(インデックスバッファ)・・・drawElementsで円描画のコード
※ インデックスバッファの方は無駄に頂点を使わずに済む方式です
あとはこれをステンシルバッファに組み込めば完成です!
ついでにマウス操作できるようにしてみました。
・WebGL-ワイプエフェクト・・・マウス操作でワイプが動くコード
Live2D WebGLで実装してみる
Live2Dの場合は、内部のライブラリでステンシルテストが無効にされていました。
そのため上記方法では実装できませんでした。
ステンシルバッファを使うためにフレームバッファに一度描画してやる必要があります。
今回はフレームバッファを2枚使い、1枚目はLive2D描画、2枚目でステンシルバッファをしています。
ただ、上記サイトの引数ではフレームバッファ上でステンシルバッファを使う事が上手くできず...。
そこでwgld.orgのdoxasさんに相談したら「デプスステンシル」という素晴らしいものを教えてもらいましたっ!
コード上では以下の引数がデプスステンシルに変わるだけです。
// レンダーバッファのフォーマット設定
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
// フレームバッファへのステンシルバッファの関連付ける
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, stencilrenderbuffer);
これでやっと実装が完了しましたっ!
・Live2Dで円形ワイプエフェクトデモ
Live2Dでのソースコード
githubにソースをアップしたので、モデルとLive2DライブラリをDLすれば使えます。
https://github.com/naotaro0123/Live2D_Stencil
ハマったところメモ
はじめは円描画にdrawArrays()を使っていましたが、なぜか一部のLive2Dモデルではワイプエフェクトで描画する事はできなかった...。
(haru、wankoモデルはワイプエフェクトもできずLive2Dモデルも表示されない)
どうやらLive2D Cubism Modeler上でカリングがONになっていると描画できない事がわかりました。
Modeler上ですべての描画オブジェクトのカリングをOFFにして書き出せばワイプできました。
描画オブジェクトが1つでもカリングONになってると描画されずWebGLエラーも出なくて、gl.cullFace(gl.BACK)とかも効果なしで困りました。
たぶん、初期に作られたLive2DモデルはカリングONで作られていてこれで結構ハマりました...。
ただ、効率化のために円描画をdrawElements()に変えたら、全てのLive2Dモデルでワイプできたという。
理解不足でこの挙動は謎ですが、WebGLで描画する時は極力drawElements()使おうね!って事がわかりました。
その他の参考サイト
Direct3D ステンシルバッファって何?
OpenGL ステンシルバッファ
2015/12/02追記
フォーラムで関数が一部暗号化されていて使えなくて困ってる人がいたので、使えるようにしてもらいました。
Live2D Community - motion - set / get LOOP fade