[OpenGL] リアルタイム・影生成 - シャドウボリューム
シャドウボリュームとは?
シャドウボリュームとは,一般的に光が当たらない範囲のことを言うそうです.(定義なんてあるのかなぁ)
で,そのシャドウボリュームを使ってリアルタイムに影を作成することを考えます.下図を見てください.薄紫の部分がシャドウボリュームです.このボリューム内にある物体に,光源からの光は当たりません.光の回折,反射は無視します.反射を考慮する場合は,光源を反射する物体に対して対象に作成することで回避できるかも知れませんが,回折は無理です.一般的にそこまで詳細にこだわった描画は,レイトレーシングなどの方法で描画されることが主です.

シャドウボリュームの作成
これがもっとも非効率的な作業となります.
前セクションの”簡単な影作成”では行列変換一発で床に影を作成することができましたが,今回はボリュームを作成する必要があるので,影を作成したいオブジェクトの全頂点に対して行列変換をし,それをもとにひとつずつシャドウボリュームを作らなければなりません.
下図に一つのポリゴンに対するシャドウボリュームの例を示します.この青いポリゴンが影を作るポリゴンとなります.このポリゴンが光源に対してと逆の方向にシャドウボリュームは作成されます.シャドウボリュームを形成するの各頂点は,平面射影行列などを用いて算出します.このとき,シャドウボリュームは,大きめに作成しておかないと影の届かない部分ができてしまいます.これらの頂点を用いてシャドウボリュームを描画しますが,このときすべてのポリゴンの向きをそろえて描画します.

シャドウボリュームはステンシルバッファを用いて作成します.影が描画されるべき空間は,下図のようになります.まず,シャドウボリューム以外のオブジェクトをすべて描画します.シャドウボリュームの作成は,視点に対して表向きのポリゴン,上図における手前のポリゴンを描画します.このとき,書き込まれたピクセルに対してすべてステンシル値を"1"に設定し,シャドウボリュームのデプス値を書き込まないようにします.
次に裏向きのポリゴンを描画します.すると,先にかいたオブジェクトのデプスバッファが効いているので,すでにオブジェクトが描画されているところには,裏向きのポリゴンは描画されないことになります.裏向きのポリゴンと表向きのポリゴンが書き込まれたところは影のできない空間となり,裏向きのポリゴンしか書き込まれないところは影のできる空間となります.


これを用いて,表向きのポリゴンが書き込まれているところに,裏向きのポリゴンが書き込まれた場合にはステンシル値をデクリメントし,はじめて裏向きのポリゴンが書き込まれたところには,そのままにしておきます.すると,影が映るピクセルには,すべてステンシル値,"1"が書き込まれることになります.
実際の処理の流れ
サンプルでは,波打った床と壁に四面体の影が映る様を再現しています.そのアルゴリズムの流れは下のようになります.
以上が処理の流れです.5については,当然光源に対して,表向きでないポリゴンは影を作りませんから,処理を軽くするためにチェックします.
まとめ
この処理方法の汚点は,頂点の計算とシャドウボリュームの作成にあります.さらにポリゴンの描画方法を表向きと裏向きに分けて描画する部分もきれいとはいいきれませんね.なんとかうまく処理すればきれいに影をつけられそうなのですが,今のところ思いつきません.
うーん,なんとかしたい.
http://code.google.com/p/sonson-code/source/browse/#svn/trunk/gl/shadow_volume