穴日記

どうだ明るくなったろう

シャドウマップのメモ

シャドウマップの自分用メモ。

シャドウマップによるアルゴリズムは無数にあるが、基本的には光源側からシーンをレンダリング、光源からの深度値をシャドウマップと呼ばれるデータ構造に保存し、その値を視点側からシーンをレンダリングするときに参照しながらシャドウ領域を特定していく。
(ここでの深度とは光源や視点からの距離を何らかの座標系・空間において線形、あるいは非線形に表したものだとする)


視点側からシーンをレンダリングするとき、ある点について、シャドウ領域か否かを判定したい。
シャドウ領域ということは、光源とその点の間に別の物体(遮蔽物)が存在するということであり、非シャドウ領域(普通にシェーディングされる領域)ということは、光源とその点の間に別の物体が何もないということになる。
さて、レンダリングしたい点について、光源側から見たときの深度というものが計算できる。一方、その点について、シャドウマップ上でどの位置に対応するのかというのもわかるので、シャドウマップ上における深度も求まる。もし、レンダリングしたい点が非シャドウ領域なら光源からその点が直接みえているため、シャドウマップ上に保存される深度値と、改めて計算しなおした光源側からの深度値は一致、ないしは非常に近いものになるはずである。しかし、レンダリングしたい点がシャドウ領域なら光源とその点の間に別の物体が存在し、その物体の方が光源にちかいため、シャドウマップ上に保存される深度値は別物体のものになる。この深度値は改めて計算しなおした光源側からの深度値より小さくなる。こうして、レンダリング対象の点について、シャドウ領域か否かが判定できる。


シャドウマップを作成するときは、光源を普通の視点と見立ててレンダリングを行う。たとえば、光源がスポットライトなら、それは普通のカメラと同じなので透視投影変換を行うし、光源が平行光源なら、それは平行投影変換を行う。
より具体的には、シーンの各頂点に対してモデル座標系からワールド座標系へと普通に変換してから、光源のビュープロジェクション行列をかけてやる。光源のビュープロジェクション行列は光源の位置や種類、向きなどによって求まる。
光源のビュー行列を作用させることで、光源の位置を原点とした座標系にシーンが設定される。プロジェクション行列(透視投影や平行投影)によって、平面上に投影される。この平面がシャドウマップに対応する。具体的に保存する値としては、非線形なZ値だったり、単純に光源からの距離などを保存する。前者は非線形、後者は線形な値が深度値として記録されることになる。


レンダリングするときは、まず対象の点について、光源からの距離を求める。シャドウマップに保存されている値をどう計算したかによって、この計算方法は変わり、シャドウマップに保存された値が線形な距離であれば光源からの距離は単純に求めることが出来る。
次に対象の点が、シャドウマップ上でどの位置に対応するかを求める。これも、上と同様の光源のビュープロジェクション行列をかけてやれば容易に求まる。ただし、これで求まる平面というのはデバイス座標系であるので、テクスチャ座標系にあわせるために若干追加の処理が必要な場合もある。たとえば、前者はXYが-1.0から1.0の範囲なのに対して、後者は0.0から1.0の範囲だったりするのでシフト、スケーリングが必要になる。シャドウマップ上でどの位置に対応するかがわかればテクスチャを参照して深度値を取得、あらかじめ求めてあった光源からの距離と比較し、シャドウ領域か否かを判定する。