ActionScript でセルフシャドウレンダリング


Self Shadow on FlashPlayer10 | Si+ (wonderfl.net)

小ネタ.FlashPlayer10 の 似非3D機能で,セルフシャドウをやってみました.
セルフシャドウってのは,物体が自分自身に落とす影の事です.技術的には結構面倒くさくて,前回の屈折マップよりも手間が掛かってるんですが,アルゴリズムは10年以上前から知られており,現在のゲーム等においてはもはや常識レベルの技術なので,残念ながら小ネタレベルなのかなぁと思ってます.逆に言えば,一般的な技術ですので,実用ネタともいえるかも知れません.もっとも,FlashPlayer10 の 似非3D機能でやるには,CUP的にも手間的にも負荷が高すぎる気がしますが.


影の描き方はここら辺(google:シャドウマップ],[google:デプスシャドウ)を見てください.上で述べたようにごく一般的な技術ですので,技術的解説はいくらでも見つかります.ここでは,FlashPlayer10 の 似非3D機能に特化した技術(またゆくゆくは FlashPlayer11 の 3D描画でも必要となるであろう技術)のお話のみに絞ろうと思います.
ちゃんと説明するには模式図を使わないと無理なので,原理説明はグウグル先生に丸投げしてしまいましたが,一応,いろいろ見た中で解り易かった(というかこの解説で理解した)ページを上げておきます.
3Dグラフィックス・マニアックス24 影の生成(5)〜デプスシャドウ技法
3Dの人なら名前は聞いたことあるであろう西川善司氏の記事です(私的にはx68kの有名なMMLエンジンZ-Musicの中の人.日本AS3D界の重鎮note.xのrectさんもMMLマニアっぽいし,因果は良くわからないけど MML->3D ってパスは結構あるっぽい:以上余談).数式を一切使わずに解説されているので,概念を理解するには非常に良記事だと思います(ただしデプスバッファとか基礎知識は必要).
さて,このデプスシャドウをFlashPlayer10 の 似非3D機能でやってやろう,というのが今回の趣旨なのですが,当然いろいろなものが足りてません.最たるものがデプスバッファ(zバッファ)でしょう.今回はDepth buffer test - wonderfl build flash onlineで2年以上前(!)に実装したものを少し修正して使いました.Render3DクラスのrenderDepth()というのがその実装です.やり方としては,

  • x方向に黒→白グラデーション(256段階)を描いた256pix幅のテクスチャを用意.
  • 各頂点のテクスチャ座標のu値に,座標変換後の頂点のz値を0-1の値に変換した値を代入(今回はzNearからzFarの間の内分比を採用).
  • 通常のテクスチャマッピング描画(Graphics.drawTriangles()).

として得られたビットマップをデプスバッファとして使用します.Depth buffer testで画面をクリックすると,具体的にどのような画像が作成されるか解ると思います.本当にデプスバッファテストを行う場合は,2つのデプス画像を"lighten"合成した全体デプス画像を作成,その全体デプス画像と個々のデプス画像を"Difference"合成して,差が0だった場所をthreshold()で抽出,各画像のマスクとして採用することで合成します.とまぁ有り得ないくらい面倒なんですが,今回の影生成にはデプスバッファ情報だけが必要であり,デプスバッファ合成は必要ないため,ここら辺の処理は行いません.
さて,とりあえず基本となるデプスバッファ画像が得られるようになったので,ここからの流れを一気に書きます.

  • 光源をカメラに見立てて座標変換/レンダリングしたデプス画像を取得(シャドウマップ)
  • このとき座標変換で得られるxy情報を各頂点のテクスチャ座標として保存
  • 実際のカメラ座標でレンダリングする際にシャドウマップをテクスチャとして描画する事で,シャドウマップが物体表面に投影された画像を合成する.-(1)
  • 先ほどの光源をカメラに見立てた座標変換の際得られたz情報を,別のテクスチャ座標として保存
  • 実際のカメラ座標でレンダリングする際に,デプスバッファ作成で用いる黒白グラデーションをテクスチャとして描画することで,光源からのz方向距離を黒白グラデーションで表した画像を合成する.-(2)
  • (1)と(2)を"subtract"合成した画像に対してthreshold()を使う事で,光源からの光が届かない部分を抽出.-(3)
  • (3)画像を"multiply"で通常のレンダリング結果に合成((3)の抽出結果は画面クリックで見ることができます.)

かなーり面倒くさいというのが判ると思います.前回の屈折も面倒と感じたかもしれませんが,それよりさらに激しいです.その上効果が影だけなのでかなり地味.
今回のこの描画は基本に忠実な方法でしたが,あえてFlashならではという点を上げるすれば,画像合成演算を駆使するところと,影のぼかしが簡単に出来るところでしょうか.こんな複雑な技術であっても,今では当たり前に使われてるっていうのは感慨深いです.

そして次世代描画エンジン molehill へ?

FlashPlayer11の次世代描画エンジン molehill は,デプスバッファもステンシルバッファも対応しており,プログラマブルシェーダにより,かなり自由に情報を加工することが出来ます.今回のデプスシャドウデモでは,デプスバッファ作成とか合成演算とか負荷が高い処理はことごとくmolehillに任せられるため,相当の高速化が見込めます.ただデプスシャドウは,屈折と比べて汎用性が高く成熟した技術であるため,すぐに誰かがやるんじゃないかなと思っています(観測範囲ではまだ見た事はありませんが,もう出来てる人はいるんじゃないかと思います).
というわけで,今一モチベーションが保てそうに無いので正直いつやるか判りませんが,後学のためにも,そのうちやってみようと思っています(16bitデプスバッファとかVariance Shadow MapとかScreen Space Ambient Occlusionとか,いくつかやってみたいアイディアもあるし).