技術

まだ直接照明の影と格闘中

あれから、リアルなソフトシャドウの計算をどうやって速くするか考えてた。
候補としては大きく分けて

  1. シャドウマッピング系
  2. ステンシルシャドウ系
  3. GPUレイトレ系

の3つの技法を検討してた。

シャドウマッピング系の技法の場合、やはり心配なのはシャドウマップの解像度の問題。
そこで色んな最適化技法を調べてみたけど、ほとんどが視点の存在を前提としてるものばかりで、ベイカーとしては採用しづらい。(不可能ではないと思うけど。)

今回は使わないけど、パラボロイド座標変換を利用したシャドウマッピングはなかなか面白いと思った。
魚眼レンズのようなもの(実際は魚眼じゃなくてパラボロイドだけど)を通してシャドウマップを作る技法と言えば分かりやすいか。
利点は、半球分の視野を1回の描画でカバー出来ること。全球分だと2回。
あと、パラボロイド座標変換の実装が簡単。
シャドウマップが歪むことになるけど、他の似たような技法に比べて歪みが少ないから比較的どんな場合でも使いやすい。
問題は、頂点シェーダでパラボロイド座標変換をしたあとの頂点に対して、ラスタライザで普通の透視投影変換用の補間が行われるため、ズレが生じてしまうこと。
ポリゴンを細かくすれば誤差が目立たなくなるし、シャドウマップ参照時のフラグメントシェーダで補正したりも出来るらしい。
今回は使わないと思うけどどっかで使いたい。

ステンシルシャドウ系の技法が使えれば一番いいんじゃないかと思ったけど、最適化技法うんぬん以前に普通のステンシルシャドウ自体が視点の存在を前提としてるので、やはりベイカーとしては採用しづらい。

GPUレイトレ系については、レイトレというか光源の可視性をチェックするだけだけど、一つ案があったので、この前作ったものとは別のものを実装してみた。
OpenGL 3.2を使うようにしたおかげでTransform feedbackやジオメトリシェーダが使えるようになったので、その辺りを活用するパターンのもの。
これは後にも書くがかなり遅かったし、最終的な画もこの前と変わらないので画像を載せたりはしない。
多分ボツになるだろう。
一応処理の概要としては以下のような感じ。

レイの情報を頂点データとしてGPUに与える。
glDrawArrays(GL_POINTS, 0, 全レイ数)として描画コマンドを実行。
ジオメトリシェーダで、レイの向きが光源の法線と相対してないものを棄却する(頂点を出力しない)。
また、ポリゴンを複数個ずつ参照し、光源より近くでレイが遮蔽されるなら棄却する。
光源に到達することが確定したレイに関しては、フラグメントシェーダでそのレイが該当する位置に1(日向を意味する)を出力。
それ以外のものに関しては次回の処理のために頂点を出力する。
GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTENのクエリを発行し、出力された頂点数(レイ数)を得る。
Transform feedbackのために頂点バッファオブジェクトを入れ替え。
glDrawArrays(GL_POINTS, 0, 直前に出力された頂点数)とする。
そしたらクエリを発行し、出力された頂点数を得て…ってのを繰り返す。
これを出力された頂点数が0になるまで繰り返す。
さらに以上全体を光源ごと(面光源を多数の点光源で近似してる)に処理する。

で、これを実行してみたら、この前のより3倍くらい遅くなった。
この前の実装では、交差判定の回数が「総点光源数」x「総ポリゴン数」x「総レイ数(1024×1024≒100万固定)」だったが、今回のは「総点光源数」x「総ポリゴン数」x「有効レイ数(ループごとに減っていく)」なので、多少速くなるかなと思ってたけど結果は逆。

この前のは少し最適化してシェーダの分岐を0にしてたんだけど、今回のは棄却処理の関係で少し分岐が残ってるせいかもしれない。
あと、ジオメトリシェーダよりフラグメントシェーダの方が並列性が高くなるのかもしれない。
あくまでもうちの環境における話だけど。

空間分割とかして交差判定の回数を減らすのがセオリーかなと思うけど、実装が面倒なのよね…
どうしようか。

あとは、正確性を犠牲にして速度を稼ぐとか。
それに関係するけどもうひとつVPL技法に関連する方法を考えてるのでそれにするかもしれない。

コメントを残す

メールアドレスが公開されることはありません。



※画像をクリックして別の画像を表示

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください