リフレクション【Planar Reflections, SSR】(知見倉庫)
リフレクション表現に関する知見スレです。
コロプラ社のグラフィックス技術デモ開発「PRINCIPLES」ではSSRを採用している。
「鳴潮」はSSRを採用している。
中品質以上サポート。
前のフレームの深度バッファとカラーバッファを活用してSSRを生成し、現在のフレームの反射マッピングに再投影することでデノイズをおこない、SSR品質を向上している。
ゼンレスゾーンゼロ ZZZ
SSRのShaderGraph実装サンプル
【SSR, 描画品質】Ray接触判定精度の向上
SSR処理のうち、Raymarchingによる接触判定で反射対象座標を取得する場合、対象オブジェクトの厚みを考慮する必要がある。
基本的にSSRでは、Rayの現在StepでのDepthとオブジェクト描画時書き込まれたDepthを比較して、反射対象オブジェクトに接触したかどうか判定をおこなう。
厚みを考慮しない場合、手前すぎるオブジェクトに対しても接触判定とみなされ、反射描画結果に破綻が生じてしまう。
ただ、厚みが一律だったり、精度が低い場合、以下の問題が発生する
シーンにさまざまな深度が含まれている場合 (巨大な山の底に人が立っているところを想像してください)、誤検知が発生します。
また、mebiusboxさんの記事にもある通り、閉じた物体(ドーナツ型など)の接触判定も破綻が生じる
【SSR, 描画品質】画面端の描画破綻の改善
SSRでは事前に画面に描画したバッファをサンプリングして反射カラーを決定しているため、画面端の反射に関しては見切れ等の破綻が生じてしまう。
下記サンプルや上記サイトでは、NDC空間での画面端に対してフェードを加えることで、破綻を抑えている。
【SSR, 描画品質】Reflection Probeとのブレンドによる描画破綻の改善
破綻が生じる箇所はReflection Probeを使用することで、破綻を軽減する手法がある。
【SSR, 描画品質、最適化】DDAアルゴリズム
SSRの遮蔽判定の欠点として、カメラの方向ベクトルと反射面の法線ベクトルが直行に近い状態になればなるほど、手前側は反射物をSkipしてしまい、奥側は同一ピクセルが反射対象とみなされ、オーバーサンプリングされてしまう。
これを回避するために、DDAラインアルゴリズムを活用し、Rayのサンプリング数を減らしつつより正確な反射色を取得することができる。
後で読む
SSRの手法がまとめられた論文
各手法の解説、GPU時間における統計的有意差、コードも紹介されている
SSRのRaymarchingによる反射対象カラー算出方法は、以下の課題と手法が存在する
Traversal Scheme最適化
Rayの進行はどう進めるのが適切か?
- 線形深度 vs 非線形深度
- 前提として、RayのStep間隔と深度間隔は合わせる必要がある
- 線形深度で進行するのが一般的
各種手法
Linear Schemes
- 純粋なRaymarching
- 純粋にStep数が精度に直結する
- 高負荷
- Non-Conservative DDA (NC-DDA)
- Bresenhamの線分描画アルゴリズムを、RayのStepごとの移動座標算出処理に用いる
- ピクセルの境界を丸めることで線分を細くし、処理を高速化(その代わりピクセルの境界が不正確になる可能性がある)
- Non-Conservative DDA (NC-DDA, Strided)
- Bresenhamの線分描画アルゴリズム
- ピクセルの境界を丸めることで線分を細くし、処理を高速化(その代わりピクセルの境界が不正確になる可能性がある)
- Step幅を大きくもたせて全体Step数を減らし、交差した場合その前のStepの位置からの二分木探索で正確な交差位置を算出する
- 全体Step数を抑えつつ、接触時の位置を正確にサンプリングできるため、Step処理の効率が良い
- 1Step内に交差オブジェクトが複数存在する場合、どちらか片方は交差判定と見なされない
- Conservative DDA (C-DDA)
- Bresenhamの線分描画アルゴリズム
- DDAアルゴリズムで対象となるピクセル(線分が重なるピクセル)全てを判定対象とみなす
- ただし、StepごとのRay進行先の決定処理に動的分岐が発生するため、比較的高負荷
これらのStepはカメラから近いほどアンダーサンプリングが、カメラから遠いほどオーバーサンプリングが発生する
- アンダーサンプリング問題...Rayの交差判定が見落とされる可能性がある
- オーバーサンプリング問題...同じDepth Bufferのテクセルを複数回サンプリングする -> 無駄なサンプリングコンストが発生する
また、探索距離が長くなると
- 精度そのまま...Step数が増加し、GPU負荷が増大
- Step数そのまま...Step幅が増大し、交差判定精度が下がる
という問題が発生する
Hierarchical Schemes
- Min Hi-z
- 1/1サイズのDepth Buffer、1/2サイズのDepth Buffer、1/4サイズのDepth Buffer...と、解像度の異なるDepth Bufferを階層的(=Hierarchical)に生成(=Mipmap化)し、任意のレベルのDepthBufferを参照する手法。
- ダウンスケール時、周囲4点の最小深度値(=手前と判定される深度値)を書き込む。このMipmap Depth Bufferを低解像度からサンプリング、交差判定をおこなうことで、低解像度のサンプリングにコストを抑え、次Stepへ進めることができる。
- ただしカメラからのRayベクトルである場合に限る。これはダウンスケール時、周囲4点の最小深度値を書き込むようにしているため、深度値が浅い方から順に交差判定をおこなう場合のみ有効であることに起因する。
- Min-Max Hi-z
- Min Hi-zの課題点である、カメラからのRayベクトルでない、カメラとは逆方向のRayベクトルでのRaymarching処理をおこなう際にも正常に判定できるように、ダウンスケール時周囲4点の最小深度値と最大深度値を書き込み、交差判定をおこなう手法。
Traversal回数の最適化
- 最大反復回数をきめ、Step数を制限する
- 低解像度でTraversalを実行する
- Interleaved sampling
- Traversal Schemeを2回実行後Intersection Schemeを1回実行するようにし、フレームごとにRayの進行具合を変化させることでStep数を減らす手法(完全に説明しきれていないため、詳しくは文献閲覧推奨)。
- そもそもRaymarchingしないで反射表現する
- Reflection Probeを使用する
- テクスチャサンプリングのバッチ化
- Rayに沿った複数のDepth Bufferを一度にサンプリングし、今後サンプリングが必要な場所をより正確に求める。
- この手法はHierarchical Schemesとは相性が悪い(様々なMipmapレベルのBufferサンプリングが必要になるため)
Intersection Scheme最適化
Rayの現時点のz値とDepth Bufferの値比較による接触判定のうち、より正確で低負荷な判定方法とは?
-
純粋な値比較(=オブジェクトの厚さが無限)
- かなり薄いオブジェクトに対しても必ず判定が正となってしまったりと、精度が低い
-
一定の厚さを定義し、接触判定
- 厚さ無限で定義するよりも、接触判定精度は上がる+負荷が比較的低い
- 厳密なオブジェクトの厚みを定義していないため、接触判定精度はオブジェクトの実際の厚みと差があるほど下がってしまう
-
解像度が上がる、または視野角が減少すると、スクリーン空間での奥行きさがなくなることに着目し、可変厚みを定義
t_Z=\frac{d \tan \left(\frac{\alpha_{F O V}}{2}\right)}{W H} -
...視野角,{\alpha_{F O V}} ...カメラ での距離,d ...スクリーン横幅,W ...スクリーン高さH - 正確な厚みを定義しているわけではない
-
FrontCulling時のDepthとBackCulling時のDepthから厚さ情報を取得し、接触判定
- 正確なオブジェクトの厚みを取得できるため、接触判定精度は高い
- Depth Bufferのサンプリングコストが2倍になる+オブジェクト描画時FrontとBack両方のDepth情報を書き込むコストが発生する
-
Parametric ray distance intervalsを使用した接触判定
- 2つのParametric ray distance intervalsは、Depth Intervalsが交差する時に交わる特性を活かし、一部のTraversal Schemeで深度比較の代わりにParametric Distanceを使用することで計算コストを軽減している
アーティファクト軽減最適化
SSRはスクリーンスペースに描画されているカラーのみ反射対象と見なされるため、Occlusionされた情報(可視面の背後にある情報)は無視されてしまう。
これを回避するために、Multi-Layer Techniquesが存在する。
Multi-Layer Techniques
- Multi-Layer SSR
- 各ピクセルに複数のレイヤーのシーン情報を格納し、複数のレイヤーに渡ってTraversalを実行する。Deep G-Bufferを定義し、Occlusion対象の描画も可能にする。
- 各レイヤーのG-Bufferを格納する必要があるため、メモリ負担が増大する
- Multi-Layer情報のうち、頂点が被ったフラグメント情報とポインタを格納したFragment Data Bufferを定義し、Raymarching処理時Sortしたフラグメント情報を参照することで、Occlusion対象オブジェクト情報を参照する手法がある
- Fragment Data Bufferにより、Occlusion対象の頂点情報も全て格納することが可能
- ただし、リストのサイズがフレームごと可変であることや、Fragment Data Bufferサイズが膨大
- Rasterizer Ordered Views(ラスタライザ時使用されるオブジェクト処理順序情報)を活用すると、LinkedListにSort情報を格納せず済むため、バッファを多少削減できる
- Early returnに関して
- 最大Ray深度がLayerの最小深度バッファ値よりも小さい場合、ループは早期に終了できる。ただし、各テクセルでチェックする必要があるLayerの数がスレッド間で異なる可能性があるため、Traversal Schemeに動的分岐が生じる。
これもあとで読む
フレネル反射率について
線形トレース法と一部最適化解説
Hi-z SSR法と一部最適化解説