[Olsson 2015Olsson, O. 2015. Introduction to Real-Time Shading with Many Lights. ACM SIGGRAPH Courses. https://web.archive.org/web/20221003210507/https://efficientshading.com/2015/01/01/real-time-many-light-management-and-shadows-with-clustered-shading/.]
問題定義#
これから紹介するアルゴリズムは数百から数千のライトをリアルタイムで処理するとき関係してくる。言うなれば、数は問題ではないとする所までが最終目標。
ライトは影響範囲を制限され、減衰効果によってその境界で0になる。
ここでは影を考えないで、このあとのコースの方でカバーする。
事前計算しないので、シーンをフレームごとに変化させることができる。
ライト割り当て#
シェーディングポイントに及ぼす影響をすべて調べるには、単純に考えてもフレームバッファのピクセル数xライト数くらいかかるので、ピクセルあたりのライト数を最小化して高パフォーマンス化したい。その解決策として、ライト割り当て(ライトカリング)を導入する。
シェーディング技法のコースと言いつつも、実際には注目する各点に関係するライトがどれだけあるかを算出する方法についての話が主になる。
これを高パフォーマンスで行うことは、関心事とするのに十分すぎるほど複雑である。
ここではライト割り当てと呼ぶが、ライトカリングの名でも知られている。
(Traditional) Forward Shading#
リアルタイムシェーディングで使われていた伝統的な手法で、コンシューマGPUの最初期にのみ使われていた。
モバイルではまだ支配的。
フレームバッファに書き込むまでにひとつのパスしかない。
ジオメトリはラスタライズされ、頂点データを補間して、フラグメントを生成する。
フラグメントは必要分すべてのライトを用いてシェーディングされ、計算した色はフレームバッファに流される。
シェーディングはフラグメントシェーダで処理する。(頂点シェーダでもできるけど)
ドローコールごとに関連するライトをすべて集めなければならず、バッチごとにライトを割り当てなければならない。
ライトの数を少なくするためにバッチサイズは小さくしたいけど、描画が高速化するようにGPUを休ませずにAPIのオーバヘッドを避けるためにバッチサイズを大きくしたい。
バッチサイズの影響が強いため、ジオメトリの形状はあまり気にしなくていい。
どっちがいいかは場合によるため、妥協するのが一番はやいが、難しい問題である。
問題#
単なるバッチサイズの話から掘り下げてみると、ジオメトリのチャンクサイズに基づいてライトを割り当てなければならないという基本的な問題になる。
いくつかの大きなオブジェクトと大量の小さなライトがある場合、ジオメトリの一部にしか影響を与えないライトが多く、無駄なシェーディング計算しなければならなくなる。
一方で、大量の小さなオブジェクトといくつかの小さなライトがある場合、個々のオブジェクトでライトの影響範囲を求める、似たような処理を繰り返さなければならなくなる。
これらの状況はシーン中に混在するため、パフォーマンスのバランスを良好に保つのは困難に近い。
一般に、三角形の大きさやライトの密度変化が関わってくると、この話はうまく解決できなくなってしまう。
まとめ#
良い点#
- シングルパス
- フレームバッファだけなので、かかるメモリコストが少ない
- 透明処理ができる
- MSAAが使える
- ライトが少ないうちはシンプル
- シェーディングモデルの切り替えが簡単
悪い点#
- オーバードロー
- バッチ処理がライティングと一体となっている
- シェーダ管理
Traditional Deferred Shading#
シェーディングからジオメトリを分離して、ライト割り当てとオーバードローの問題を解決するのが目標。
こんにち知られているGバッファを用いたDeferred Shadingは1990年に導入された。[Saito and Takahashi 1990Saito, T. and Takahashi, T. 1990. Comprehensible rendering of 3-D shapes. SIGGRAPH Comput. Graph. 24, 4, 197–206. 10.1145/97880.97901. https://www.cs.princeton.edu/courses/archive/fall00/cs597b/papers/saito90.pdf.]
“Deferred Shading”はその後に改めて作られた用語。[Tebbs et al. 1992Tebbs, B., Neumann, U., Eyles, J., Turk, G. and Ellsworth, D. 1992. Parallel Architectures and Algorithms for Real-Time Synthesis of High Quality Images using Deferred Shading. https://www.cs.unc.edu/techreports/92-034.pdf.]
ゲームでは2005年頃から、MRTの登場により使われ始めた。[Shishkovtsov 2005Shishkovtsov, O. 2005. Deferred Shading in S.T.A.L.K.E.R.. GPU Gems 2. https://developer.nvidia.com/gpugems/gpugems2/part-ii-shading-lighting-and-shadows/chapter-9-deferred-shading-stalker.]
シェーディングを遅らせる。
ジオメトリパスでは、ジオメトリがラスタライズされ、頂点データを補間してフラグメントが生成されるまでは同じで、フラグメントはシェーディングされず、ピクセル単位のアトリビュートがGバッファに格納される。
ジオメトリを全部処理したら、そのあとで、シェーディングパスを行う。
シェーディングパスは、シーンのジオメトリ表現に依存せず、可視サンプルのみをシェーディング計算する。
Gバッファには色、法線、深度、スペキュラなどがある。
シェーディングパス#
各ライトの境界領域をジオメトリとしてスクリーンに描画することでラスタライザがライトの割り当てを行う。
フラグメントシェーダはGバッファの内容を読み取ってシェーディングを行う。
計算した色はフレームバッファにブレンドする。
これなら、各ライトを独立して処理できる。
Stencil Buffer Optimization#
[Arva and Aila 2003Arva, J. and Aila, T. 2003. Optimized shadow mapping using the stencil buffer. Journal of Graphics Tools 8, 3, 23–32. 10.1080/10867651.2003.10487587.]
ステンシルシャドウと要領は同じ。
- ライトボリュームの背面を描画して、深度テストに失敗した所のステンシルをマスクする
- ライトボリュームの前面を描画して、ステンシルテストに合格した所のシェーディングを計算する
これにより、ライトボリュームの外にあるピクセルを除外できる。
シェーディングが必要なところだけを的確に狙い撃つため、効率が改善する。
モダンなハードウェアで大量のライトを扱う状況に対して効率的に実装できるかは不明。
その他のバリエーション(variants)#
- Deferred Lighting
- Gバッファには法線とshininessのみを格納する
- Gバッファをもとにライトの影響を計算して、拡散色と鏡面色をバッファに格納する
- 最後に、もう一度ジオメトリを描画して、マテリアルにライトの影響と掛け合わせる
- Light PrePass
- 基本はDeferred Lightingと同じだが、スペキュラが単色
- deferredとパフォーマンスは似たりよったり
- それよかシェーディングモデルの制限が増えてたりする
まとめ#
良い点#
- 自明なライト管理: カリングしてライト境界を描画するだけ
- 大量ライトが可能に
- 簡単な(ライトの)シェーダ管理: 1シェーダにライトのタイプは1つだけ
- 高シェーディング効率
- 照らされているサンプルだけ陰影を計算すればいい
- オーバードローなし
- ジオメトリパスが1つ
- シャドウマップが再利用できる
悪い点#
- 透明処理ができない
- フレームバッファが巨大になる
- MSAAを使うなら特に
- ライトを累積するために高精度フォーマットが必要
- 複数のシェーディングモデルを扱うのが難しい
- 特定のジオメトリにカスタムしたりとか
- かなりのメモリ帯域を使う
Modern Shading Techniques#
過去10年間分のNVIDIA GPUの性能を比較すると、計算効率が100倍になった対して、メモリ帯域幅は10倍にしかなっていない。
最近のGPUは計算パフォーマンスが大事。
GPGPUのモデルやそのコア性能がここ数年でとても向上している。
以前はリアルタイム処理には三角形のラスタライゼーションしか選択肢がなかった。
多くのリアルタイムを想定したアルゴリズムは再考される時期に来ているのかも。
帯域の問題#
伝統的なdeferred系では同じピクセルにライトがオーバーラップすればオーバードローが起こる。
オーバードローが起これば、それだけGバッファの同じ場所からの読み出しが発生することになる。
模式的には以下の様になる。
for each light
for each coverred pixel
read G-Buffer
compute Shading
read + write frame bufferここで問題になるのは、Gバッファの読み書きが最深のピクセル毎ループで行われること。
なので、ループの外に出してやればいいのでは、と考える。
for each pixel
read G-Buffer
for each affecting light
compute Shading
write frame bufferこうすれば、計算志向の構成になる。
読み書きも1回になって、帯域の問題も解決できそうに見える。
この変形では、ピクセルごとに、関連するライトのすべてに対してシーケンシャルにアクセスする必要が出てくる。
グローバルなライトのリストを使うのは非効率すぎる。
ピクセルごとにライトnリストを用意する方法は遅いし容量を食う。
モダンなシェーディングの解き方#
モダンな解法は、似たようなピクセルのグループでリストを作ること。
for each pixel
read G-Buffer
for each possibly affecting light
if affecting
compute Shading
write frame bufferサンプル間のコヒーレンシが高まる。
コヒーレントなアクセスを行う。
必要なストレージが小さくなる。
リストは保守的。タイル内のいずれかのサンプルに影響を与えるかもしれないライトをすべて格納する。
計算パフォーマンスをいくらか帯域に逃がすことは、モダンなGPUでは良い戦略である。
しかし、同時に、ブルートフォースと比べて最適解に著しく近づく。
モダンなDeferred Shading#
- Gバッファにシーンをレンダリングする
- ライトを割り当てる
- フルスクリーンパス(CUDAやコンピュートシェーダ)
- 各ピクセルで、Gバッファから読み出したデータとライトリストのライトすべてを使ってシェーディング計算する
モダンなForward Rendering#
- オプショナル: Pre-Zパス、または、ジオメトリパス
- ライト割り当て
- ジオメトリパス
- ライトリストのライトすべてを用いて、ほぼ通常通りのシェーディング計算を行う
まとめ#
良い点#
- 低帯域幅
- 柔軟性が高い: DeferredにもForwardにもできる。
- Forwardなら、透明処理が使える
- Forwardなら、MSAAが使える
悪い点#
- ライトのタイプが複数あると、シェーダが複雑になる
- シャドウマップのストレージが再利用できない
Tiled Shading#
帯域制限、計算志向の時代に作られた最初のやつ。
まとめて、Tiled Shadingと呼ぶ。Deferred版とForward版がある。
AMDはForward+と呼ぶけど、Tiled Forward Shadingのほうが意思疎通がしやすいのでこちらを使う。
Tiled Shading Gridの生成#
(このデモでは)スクリーンを32x32ピクセルのタイルに区切る。
各タイルは、タイル内のピクセルのいずれかに影響を与えるすべてのライトを格納したリストを持つ。
このリストはそのピクセル間で共有されるため、メンテナンスとフェッチのコストは小さくなる。
ライトがリストに入るかどうかは、スクリーン空間上でライトとタイルとの接触判定を行って決める。
Depth Range Optimization#
タイルごとに最大最小深度を求めておいて、その値を用いてオブジェクトの無い空間にあるライトをカリングする。
これをしないと、普通はパフォーマンスがだだ下がる。
Tiled Forward ShadingではPre-Zパスが必要な理由でもある。
Tiled Shading Light Grid#
すべてのライトは、ライトのインデックスを格納するリストと、タイルが参照しているインデックスのリストの範囲を格納するグリッドに処理される。
まとめ#
良い点#
- 必要な帯域幅が小さくなる
- 簡単なライト割り当て
- 些細なライトリストのルックアップ
- 高パフォーマンス
- 柔軟性が高い: forwardとdeferred
- forwardなら透明処理ができる
- forwardならMSAAができる
悪い点#
- ライト周りの複雑なシェーダ
- シャドウマップのストレージが再利用できない
- 視点に依存する
- 2Dのライト割り当て
- 深度の不連続性
パフォーマンスが強く視点に依存するため、シーンから実行時のパフォーマンスを予測しにくい。
Troublesome Tiles#
深度に不連続な部分があると、タイル内で取り得る深度範囲が大きくなる。非常に奥行きのある場合も同様。
深度範囲が大きいと、どう頑張っても影響外でしかないライトを無駄に集めてしまう。
Tiled Extensions#
この問題に対する緩和策がいくつかある。
この緩和策の問題点として、
- 傾斜やマルチレイヤーを扱えないなど、一般性に欠ける。
- 透明処理が扱えない
2.5D Culling#
[Harada 2012Harada, T. 2012. A 2.5D Culling for Forward+. Technical Briefs. ACM SIGGRAPH Asia. 10.1145/2407746.2407764. https://www.slideshare.net/slideshow/a-25d-culling-for-forward-siggraph-asia-2012/34909590.]
タイルとライトの両方で、モノが存在するところが1となるように深度方向のビットマスクを作り、それらのANDを取ることでタイルとライトとが重なるかどうかを判定する。
Binormal Clusters / HalfZ#
[Lauritzen 2012Lauritzen, A. 2012. Intersecting Lights with Pixels: Reasoning about Forward and Deferred Rendering. Beyond Programmable Shading course. ACM SIGGRAPH. https://web.archive.org/web/20191111125124/http://bps12.idav.ucdavis.edu:80/.]
[Thomas 2015Thomas, G. 2015. Advancements in Tiled-Based Compute Rendering. Game Developers Conference. https://media.gdcvault.com/gdc2015/presentations/Thomas_Gareth_Advancements_in_Tile-Based.pdf.]
深度範囲を2つに分けて、不連続な部分を削ぎおそす。
ライトリストも2つに分ける。
‘Modified HarfZ’では、さらに手前の最大値と奥の最小値を使ってピッタリに縮小する。
Clustered Shading#
キーアイデア#
深度方向にも分割する。
関係するサンプル点をグループ化したクラスタを用いる。
関係を定義するのは3次元のユークリッド距離を使う。要は、空間的に近いかどうか。他にも、法線や反射率なんかも使える。
froxel = frustum voxelと大体同じもの。
プロパティ#
3次元的な分割を行うことで、さらなる局所化やサンプルグループごとに一貫したボリュームができる。
つまり、シーンのライト密度と実行時シェーディングコストの間に相関が生まれる。
制御性とパフォーマンス予測性が大事。
クラスタキー#
2Dタイルの座標i,jと、サンプルのview空間での奥行きを示す対数関数kを使って、3Dグリッドを表現する。
この対数による分割では、クラスタはできるだけ正方形になるように調整されている。
つまり、ある種のLODのようなもので、viewパラメータを広めにとってもクラスタ数が爆発しないようになっている。
ライト割り当て#
Tiled Shadingと同じように、ライトとクラスタが重なるかどうかを見つける。
クラスタごとのリストに記録する。
バリエーション#
クラスタグリッドをsparseにするかdenseにするか
クラスタがexplicitかimplicitか
deferredかforwardか
階層的にするか否か
透明処理のサポート
Sparse vs. Dense Cluster Grid#
Sparse#
サンプルが含まれるセルのみを格納する
Pre-Zパスかdeferredが必須
冗長なライト割り当てが発生しない
ジオメトリ情報を他のことに活用できる
Dense#
すべてのクラスタにライトを割り当てる
非同期にできる。CPUでもできる
viewボリューム内のどこにでもアクセスできる
spaseと同等のシェーディングコスト
Explicit vs. Implicit Clusters#
Explicit#
サンプルからバウンディングボックスを作る。
ストレージが必要。
構築コストがかかる。
タイトな境界を作れる。
Forward Shadingでは追加のジオメトリパスが必要。
implicit#
グリッド座標から計算する。
ストレージが必要ない。
大きなカラの空間を取れる。
Deferred vs. Forward Shading#
切り替えが簡単なので、深刻な決断ではない。
Deferred#
sparseグリッドのサポートが比較的やりやすい。
Forward#
MSAAと透明処理が使える。
シェーディングモデルをたくさん使える。
両方を採用する#
不透明にはDeferred、透明にはForward
同じグリッドを使うことも、別にすることもできる。
Cluster Hierarchy#
構築は安い。[Sintorn et al. 2014Sintorn, E., Kämpe, V., Olsson, O. and Assarsson, U. 2014. Per-triangle shadow volumes using a view-sample cluster hierarchy. Proceedings of the 18th meeting of the ACM SIGGRAPH symposium on interactive 3D graphics and games 111–118. 10.1145/2556700.2556716. https://www.cse.chalmers.se/~uffe/clusteredptsv2014.pdf.]
可視サンプルの階層表現
新しい技法を可能にする。
- 効率的なシャドウボリューム
- シャドウマップ(3番目のトークを参照)
- splatting in general.
Transparent Geometry#
Tiled Forward Shading#
[Olsson et al. 2012Olsson, O., Billeter, M. and Assarsson, U. 2012. Tiled and clustered forward shading: supporting transparency and MSAA. ACM SIGGRAPH 2012 talks. 10.1145/2343045.2343095. https://www.cse.chalmers.se/~uffe/tiled_shading_siggraph_2012.pdf.]
深度の最小値を最前面の透明オブジェクトかnear planeまで拡大する。
この手法の問題として、
- 視界にかぶさると、depth range optimizationの意味がなくなる
- 視点依存なので、実行時にしか決定できない
Clustered Forward Shading#
[Olsson et al. 2012Olsson, O., Billeter, M. and Assarsson, U. 2012. Clustered deferred and forward shading. Proceedings of the fourth ACM SIGGRAPH / eurographics conference on high-performance graphics 87–96. 10.2312/EGGH/HPG12/087-096. https://www.cse.chalmers.se/~uffe/clustered_shading_preprint.pdf.]
Clustered Shadingなら、クラスタが三次元空間で分かれているため、この問題は苦ではない。
Denseなクラスタグリッドなら普通に動作する。
Sparseクラスタグリッドでは、pre-geometryパスで透明オブジェクトが使うクラスタにフラグメントシェーダのside effectとしてフラグを立てる。