スポンサーサイト

--年--月--日 --:--

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

PSM(Perspective Shadow Maps)とLiSPSM(Light Space Perspective Shadow Maps)のアルゴリズム

2008年11月04日 00:21

PSM、LSPSM のアルゴリズムを自分なりに解釈しまとめてみたのでアップしてみます。
参考になれば幸いです。

尚、絵は後で追加します。多分。

PSM のアルゴリズム



ワールド座標上のディレクショナルライトの方向をカメラローカルの座標に変換する
この表示用のローカル座標系を保持しているカメラを表示カメラとする。




1で変換した表示カメラローカル座標でのディレクショナルライトを同次座標上で歪ませる。
ライトは、無限遠平面上に位置させます(通常は 1 の同次座標値を 0 と置くことで表現できる)
このとき、ライトを実際に「置く」のがポイント。
ディレクショナルライトを影響距離が無制限の点光源を無限遠平面に置いたと見なしている。
また、行列変換はあくまで遮断を行うための行列です。カメラ位置を「下げる」平行移動は行いません。
(透視投影行列の [4][x] 成分はカメラ位置が原点なので近平面を原点にするための移動にあたります。これを無視するということです。また、[3][4] 成分は透視投影による歪み(fovy による歪み)に当たります。 この[3][4]成分が重要で、これにより透視投影が実現されています。詳しくは依然書いた透視投影の記事を参考にしてください)




同次座標上で歪んだ2のベクトルは歪んだことにより、 (視線に対して垂直の場合以外は)無限遠平面から外れるので、3 次元座標上の点になる。
もし、視線に対して垂直の場合(要するに光源位置は相変わらず無限平面にある場合)はライトの位置とカメラの位置での歪みは発生しないことになるので通常のシャドウマップと同じ意味になる。
よって、その場合は通常のシャドウマップを適応する。




3で求めた光源位置から透視投影変換後の空間(表示空間と仮に呼びます)の中心(0,0,0.5)を見るようなカメラを作成する。
これを通常のカメラと区別するためライトカメラと呼ぶ。
表示空間は -1 <= x,y <= 1、 0 <= z <= 1 になるので、表示空間の中心は、(0, 0, 0.5) ということになる。
このライトカメラから表示空間を投影することで、表示空間が歪む。




4で求めたライトカメラからの映像を平面に投影する。
このとき、表示カメラの近平面が画面いっぱいに上手く収まるように
視野角や近平面、遠平面を調整する。
  
具体的には、、、
  
視野角は表示空間の中心から一番遠い頂点までの距離を利用し逆算します。
  rad : 表示空間の中心から一番遠い(表示空間を構成する)頂点までの距離
  dist : ライトカメラから表示空間の中心(注視点)までの距離
とすると、
  fovy = 2 * atan( rad / dist )
になります。
  
近平面までの距離は、中心から z 軸のマイナス方向に一番遠い点(カメラに一番近い点)を取ります。
よって、
  pt_near : ライトカメラローカル座標上で一番 z 軸が小さい頂点の z 値
  center : ライトカメラローカル座標上での表示空間の中心の z 値
  dist : ライトカメラから近平面の中心(注視点)までの距離
とすると、
  plane_near = dist + (pt_near - center)
になる。
  
同様に遠平面までの距離は
  rad : ライトカメラローカル座標上で一番 z 軸が大きい頂点の z 値
  center : ライトカメラローカル座標上での近平面の中心の z 値
  dist : ライトカメラから近平面の中心(注視点)までの距離
とすると、
  plane_far = dist + (pt_far - center)
になる。

以上、これらの値を使って透視投影行列を作成する。




4と5で出来た行列は表示カメラにより投影された後の処理なので、 通常の描画処理の後に4→5と適応する。
これで PSM 用のシャドウマップが出来る。



補足


3の時点で、同次座標系の値が - の場合、無限遠平面にあったカメラ位置が
カメラの近平面より後ろ側に透視投影されることを意味します。
これだとライトカメラからみた近平面が裏になるので通常の透視投影ではカリングされ表示されません。
よって、この場合は、5で使用するライトカメラの投影行列の近平面を - にした値で対処します。
要するに、さらに裏返しにするわけです。
こうすることで、カリングにより除外されることがなくなります。





LiSPSM のアルゴリズム

PSM の欠点を補うために新しく考案されたアルゴリズムです。
PSM のアルゴリズムで気付かれたかもしれませんが、シャドウマップ関連の技法というのは
ある3次元の点を2次元上の決まった位置に拘束する関係式さえあれば、どんな関係式でもシャドウマップを生成することが出来、
また、それを参照することで影を落とせます。

かなり強力な技法ですね。
シャドウマップ技法がメインストリームになったのも頷けます。

LiSPSM では PSM の技法に一捻り加えます。



表示カメラの視線(-Z方向)ベクトル(表示視線ベクトル)とライトカメラの視線ベクトル(ライトベクトル)の外積を求めます。
両視線が形成する平面に対して垂直なベクトルを求めるわけです。
この時点で、表示視線ベクトルとライトベクトルの成す角度が 0 or 180 (すなわち、外積が縮退してしまう)ならライト位置による歪みがないので通常のシャドウマップ処理にします。

  
  

1で求めたベクトルとライトベクトルの外積を求めます。
両視線ベクトルが形成する平面上且つライトベクトルに垂直なベクトルを求めているわけです。

  
  

ライトベクトル、1のベクトル、2のベクトルで新しい正規直交座標系が出来ました。
この座標系を involve カメラ座標系とします。
(名称は適当に決めています)
このとき、
  ライトベクトル - x
  1 のベクトル - y
  2 のベクトル - z
にあたります。
影を落とす全てのオブジェクト(Caster)と表示カメラの視錐台をこの座標系で変換します。




involve カメラ座標系で全てを内包する Axis Align Bounding Box を作成します。
この AABB の z の小さい面が近平面の距離になります。

  


表示視線カメラの近平面までのベクトルを2のベクトルに射影し、値を計算します。
  
単純に
  Near = dot( 2のベクトル, 表示視線ベクトル) * 表示カメラの近平面までの距離
          … dot は内積を求める関数
でOKです。
  
ここまでの計算は表示カメラローカル上で行っているので、結果的に
  Near = 2のベクトルの z 値 * 表示カメラの近平面までの距離
になります。




4で求めた近平面(AABB の z が一番小さい面)の中心から、5で求めた距離を引いてやった位置が
involve カメラの位置になります。
ちなみに、5で求めた値は大まかな位置で、もっと大きい値にしてもいいですし、正の範囲ならもっと小さい値にしても問題ありません。
しかし、あまりにも小さい値にしすぎると、involve カメラの遠平面に近い点が、2次元に投影した時、極端に中心に寄ってしまいます。
少し考えればわかると思いますが、小さい値にする=視野角が広がることを意味するので、パースが極端についてしまうわけです。
逆に極端に大きくしてしまうと、今度はパースがほとんどつかなくなってしまうので、表示カメラの視錐台の範囲外の部分が多く描画され
テクスチャ領域を無駄にしてしまいます。
5の値を目安に、いい感じの値を入れてみてください。

  
  

カメラ位置が求まり、座標系も求まりました。
あとは透視投影変換用の行列を作ることが出来れば、2 次元に落とすことが出来ます。
  
透視投影行列で使われる近平面までの距離は、6で求めた値を使用します。
遠平面までの距離は AABB の各頂点の z が一番大きい値になります。
後は、近平面の範囲ですが、、、
まず、5で求めた各頂点と視点を結ぶベクトルを求め、これを involve カメラ座標系の z = 1 の値に写像します。
  
  vec = involve カメラ座標系での Caster ポジション - 5のポジション
  vec /= vec.z
  
ということですね。
これで、vec.z = 1 のときのベクトルが求まります。
  
このベクトルを近平面まで伸ばした時の最大 x と最大 y が近平面での高さになります。
(実際は、中心から x までの距離なので、×2すると矩形の幅と高さになる)
よって、、、
  
  vec *= near
  near_w = 2 * max( near_w, vec.x );
  near_y = 2 * max( near_y, vec.y );
  
ということです。

  
  

ここまでで、(ライトに垂直な)involve カメラによる2次元座標への投影が終わりました。
しかし、このままでは通常のライト座標系の z 軸ではないので、z テストが成功しません。
よって、y 軸を中心に 90 度回転してやり、ライトと z 軸の方向をそろえます。
ここで、involve カメラ座標系では
  x,y -> -1 <= x,y <= 1
   z -> 0 <= z <= 1
の範囲になっています。
 
これを、y 軸周りに 90 度回転させているので、、
   y,z -> -1 <= y,z <= 1
   x -> 0 <= x <= 1
となってしまっています。
 
これでは、ライトカメラの近平面で x 軸方向に半分までしか描画されませんし、
z テストも不正な値で処理されてしまいます。
 
これを回避するため、まずはスケール変換から行います。
 
x は 0 <= x <= 1 となっています。
これを -1 <= x <= 1 と拡張したいので2倍にします。
  x -> 0 <= x <= 2
  
逆に z は -1 <= z <= 1 となっているので、これを 0 <= z <= 1 と収まるように半分にします。
  z -> -0.5 <= z <= 0.5

y は通常のままで ok なので変化ナシです。
 
次に、移動処理。
 
拡張の結果、x は 0 <= x <= 2 となっています。
これを -1 <= x <= 1 としたいので、-1 します。
  x -> -1 <= x <= 1
  
次に z は -0.5 <= z <= 0.5 となっていますので、これを 0 <= z <= 1 としたいので、+ 0.5 します。
  z -> 0 <= z <= 1

  
  


これで、全ての計算が終わりました。
ここまで求めた行列は、キャスターが全て納まり、尚且つ表示カメラの視錐台の範囲で歪むように作られています。
  
この行列を用いて、キャスターの位置を歪ませてやれば、視錐台に影響するキャスターを逃すことがなく、且つ都合のいい座標変換を行うことが出来ます。
 
最終的な行列の計算順序は
  カメラ行列 -> LiSPSM 行列 → カメラ透視投影行列
ということになります。
 
今までの処理はカメラのローカル座標系上での変換処理だったことを思い出してください。




LiSPSM も PSM も考え方の出発点はカメラ空間がライト方向からどう見えるか?ということと、同時座標系の根本的な考え方を利用して作られています。
これをさらに発展させた考え方に TSM がありますが、また別の機会に紹介します。
スポンサーサイト


コメント

    コメントの投稿

    (コメント編集・削除に必要)
    (管理者にだけ表示を許可する)

    トラックバック

    この記事のトラックバックURL
    http://angra.blog31.fc2.com/tb.php/128-94447f93
    この記事へのトラックバック


    最近の記事


    上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。