Japanese memo of "Hi-Z Screen-Space Cone-Traced Reflections" @ GPU Pro5

概要

  • GPU Pro5 の記事 "Hi-Z Screen-Space Cone-Traced Reflections" のメモです. (正確で詳細な情報は本を参照していただけると助かります. )
  • この記事では大雑把に言うと, GPU が描画した画面内の情報を使ったリアルタイムなリフレクションである Realtime Local Reflection(以下, RLR) を発展させたアルゴリズムについて記載されています.
  • この記事を書いた人は Yasin Uludag さん(https://twitter.com/YasinUludag)で, EA DICE で Mirror's Edge 2 の開発に携わっています.

従来の RLR との違いは何 ?

  • Hi-Z Buffer (階層型デプスバッファ)をレイのコリジョンを高速化するためのデータ構造 (acceleration structure) として構築し, それに対してレイマーチすることで高速化しています.
  • ぼけたリフレクションを近似的に求めるために, スペキュラの反射モデル(この記事では Phong モデル)・ラフネス・レイの移動距離を考慮しながら, コーントレースを行ってぼけたカラーバッファを参照しながら加算していきます.

パスの種類

  • 0. Hi-Z を構築するパス (Hi-Z pass)
  • 1. レイトレースするパス (ray-tracing pass)
  • 2. カラーバッファのミップマップを作るパス (pre-convolution pass)
  • 3. Visibility バッファを構築するパス (pre-integration pass)
  • 4. コーントレースして, リフレクションのボケ具合を求めるパス(cone-tracing pass)
  • 以下では各パスの内容について簡単に説明します.

0. Hi-Z を構築するパス (Hi-Z pass)

  • フル解像度の Z バッファから, Z バッファの MipMap を作ります.
  • ダウンサンプリングの際には 4 ピクセルをフェッチして, その最小値を出力するようにします.

1. レイトレースするパス (ray-tracing pass)

2. カラーバッファのミップマップを作るパス (pre-convolution pass)

  • リフレクションの像として参照するためのカラーバッファのミップマップを作成します.

3. Visibility バッファを構築するパス (pre-integration pass)

  • この Visibility バッファ(8bit)はコーントレースの終了条件を求めるために使用します.
  • Visibility バッファとは Hi-Z バッファの情報を補助する目的で作るものです.
  • Hi-Z のミップレベルを下げる場合には, 4 つのピクセルの Min を取ってその結果の 1 ピクセルを書き出します.
  • その際に Min のデプスと 他のピクセルのデプスの差分が大きいほど, ミップレベルが下るごとに差が蓄積してしまいます.
  • そこで, その MinZ のミップマップが実際にはどれぐらいの可視性を占めているか? を確率的に表現したものをバッファとして求めておきます.
  • この記事ではそれを "Hiearchical pre-integrated visibility バッファ"と読んでいました.
    • 具体的に Mip0 では, この Visibility バッファの値は全て 1.0(100%) です.
    • Mip1 を作る際に例えば, 0.5, 0.5, 0.5, 0.5 の 4 ピクセルの Min を取った時 Hi-Z の値は 0.5 になります. また この Hi-Z のデータは情報が欠落していないので, Visibility バッファは 1.0 のままになります.
    • 今度は Mip1 を作る際に例えば, 0.5, 0.5, 0.0, 0.0 の 4 ピクセルの Min を取った時 Hi-Z の値は 0.0 になりますが, 元々含まれていた 0.5 に関する情報が欠落しています.そこで, この際には Visibility バッファの値を 0.5 にしておき, Hi-Z 構築時に欠落する情報を補完します.
    • 計算方法としては ( 0.5 - 0.0 ) * 2 / 2 = 0.5; です.
  • つまり, Visibility バッファは HiZ バッファが示す値に対して, それが実際に空間的にどれぐらいの割合を占めているか ? を表したものになります.

4. コーントレースして, ボケたリフレクションの像を求めるパス(cone-tracing pass)

  • ( ここのパスの説明は図がないと, かなりわかりにくいと思います. )
  • 1. の Hi-Zに対するレイトレースによって, 表面上の点から伸びたリフレクションベクトルのヒット先のリフレクション用のスクリーン座標を求めることが出来ました.
  • このパスでは, このリフレクション用のスクリーン座標からコーントレース用のコーンを構築して, コーントレースを行います.
  • まずは, スペキュラの BRDF (この記事では Phong モデル)を元に, スペキュラパワーαからコーントレース用のコーンの頂点の角度θを求めます.
    • その際に, θ = cos( ξ^(1 / (α+1) ) ), ξ = 0.244 の近似式を使います.
  • コーントレースの円錐を真上から見ると, 2 等辺三角形になります.
  • コーントレースの際には, この 2 等辺三角形に対して連続的に内接する円を順に計算で求めていきます.(内接円の半径は小さい -> 大きいへと変化します).
  • 実際のコーントレース時には以下の手順を繰り返します.(記事だと最大 7 回)
    • (0) 現在の内接円の半径を求めます.
    • (1) 現在の内接円の中心をサンプリング点として, Visibility バッファとカラーバッファを参照します. (使用するミップレベルは内接円の半径から求める)
    • (2) 参照したカラーバッファの色に Visiblity を乗算して, リフレクションカラーとして加算します.
    • (3) これまでのレイの Visiblity を加算した合計値が 1.0 を超えた時は, break で抜けてコーントレースを中断します.

そのほか

リニアステップのレイマーチ後に, レイのヒット精度を上げる方法
  • 2 分探索を使う (Relief Mapping と同様)
  • Secant search を行って, 交差点を補間する. (Parallax Occlusion Mapping と同様)
  • レイの長さをジッタリングする (Crytek の RLR )

緩やかにリフレクションの像をフェードする方法

  • clamp( dot( view_reflect, view_dir), 0.0, 1.0 )を重みとして使う
  • リフレクションのヒット先が, 画面の中央から離れるに従ってフェードする.
  • リフレクションの始点とヒット点の距離に応じてフェードする.
最適化テクニック
  • レイマーチで 4 * N 回のループ処理するときに, 4 回分ずつ N 回のループに変更してテクスチャフェッチの処理をまとめます. (Unreal Engine 4 の RLR でも同様なことをしていました. )
  • 1/2 * 1/2 解像度で計算している場合, Temporal SuperSampling(別名:Temporal Filtering)を 4 フレーム分行うことで擬似的に解像度を上げます.