Japanese memo of "Ambient Obscurance Baking on the GPU"

概要



距離減衰する AO, カーネルサイズは 2m・300m の 2 種類.

  • AO(p) = (1 / PI) ∫ rho( d(p, ω) ) cos θ dθ
    • p は位置座標, d(p,ω) は ω方向の最近傍のブロッカーまでの距離, θはωと法線間の角度.
  • rho(x) = 1 - exp(a * x)
    • a = log(0.5 / d), d はメートル.
    • カーネルサイズ(レイを飛ばす範囲)は 2m と 300 m の 2 種類で計算.

(A) 建物と地形 : 頂点 AO

  • Least Squares Vertex Baking[Kavan 2011] を利用.
    • ポリゴンの面単位で, 最大のサンプル数を制限している.
    • サンプルから飛ばしたレイの 25% が裏ポリゴンにヒットしている場合, そのサンプルは捨てる.
  • 木や低木による遮蔽は半透明扱いで AO を計算.

(B) 装飾用のオブジェクト : 頂点 AO (ローカル AO)とグローバル AO の組み合わせ

  • 下のような装飾用のオブジェクトはインスタンス化している.
    • 大きいオブジェクト : 木, プレイエリア外でインスタンス化したオブジェクト
    • 小さいオブジェクト : 茂み, 低木
  • インスタンスごとに別々の頂点 AO データを持つとデータサイズが大きくなってしまう.
  • そこで, インスタンス化するようなオブジェクトの AO はローカル AO (オブジェクトごと)とグローバル AO(オブジェクトのインスタンスごと)の組み合わせを使う.
  • ローカル AO
    • そのオブジェクト自身による頂点 AO.
  • グローバル AO
    • オブジェクトのインスタンスをシーンの配置にすることによって生じる AO.
    • インスタンスごとに, ワールド位置座標を入力とする関数 ( a * world_pos_x + b * world_pos_y + c * world_pos_z + d )の 4 係数である a,b,c,d を求める.
    • 上の式は 3D linear functions( 4 sclar coefficents) という文章から推測したもの.
    • この係数は ( "参照用の AO の正しい値" - "ローカル AO" * "グローバル AO" )^2 の誤差の和が最小になるように計算する.
  • 下図の左側で木はグローバル AO のみの結果. 中央は グローバル AO + ローカル AO. 右側は参照用の正しい AO の値.

  • 木の AO の計算の工夫
    • 木の法線がいまいちだったり, 複雑にポリゴンが交差してたり, ビルボードを使っているので計算用に飛ばしたレイが裏ポリゴンに当たる問題がある.
  • 従って, ツール側では以下のように, AO のボリュームテクスチャを作る.
    • まずは 木を 2 階層の疎なボリュームテクスチャで表現する.
    • 各ボクセルで 2 種類の AO (カーネルサイズが2m・300mの場合)を計算して, その結果をそれぞれ 4 つの float で表現した SH(0,1次) として保持する.
    • AO の結果を必要な場合は, AO のボリュームテクスチャの結果をトライリニア補間して利用する. ( この AO のボリュームテクスチャはランタイムでは使用しない )
  • ランタイムでの頂点 AO の利用
    • 2m の AO を 3%, 300 m の AO を 97% としてブレンドして, 頂点 AO として保存する.
    • それは頂点シェーダで参照して, G-Buffer に 5 bit のデータとして書き込む. ディザリングも利用する.

(C) 動的なオブジェクト向けの AO : AABB を小さく分割して AO の情報書き込んでおく

  • ゲームで使っている可視性のシステム
    • AABB とそれらの接続性を表現しているグラフで, 粗く可視性を判定できる.
    • ランタイムのレンダリング時のカリング用などに使う. Umbra を利用.
  • 上の可視性のシステムで使っているデータに, 動的なオブジェクト用の AO の情報を書き込んでおく.
    • AABB を 8x8x8 の AABB に分割して, それらの小さい AABB が実際にレンダリングされる可能性があるかどうか? を 1 bit のフラグで有効・無効と判定する.
    • 小さい AABB の内部で AO の結果をサンプリングする. サンプリング点からポリゴンの裏側が沢山見える場合は何かに埋まっている可能性が高いので無効と見なす.
    • 有効と判定された小さい AABB 内の AO については その領域内で有効なサンプリング点を元に, 平均の AO (1 float)と XYZ 方向のそれぞれの微分値(3 float)を求める.
  • さらに, AABB 同士の接続性を考慮して, AO が急に変化しないように正規化しておく.
  • ランタイム
    • 動的オブジェクトはそれが, そのクラスタに属しているか? がわかるので, そこから AABB 内に埋め込まれている AO の情報を参照する.
    • 急に変化するのを防ぐために, 前フレームの結果とブレンドする.

オフラインで GPU を使って AO を計算

  • NVidia Optix で GPU を使って計算.
  • 150 万頂点, 180 万ポリゴンのシーンで NVidia K20 GPU を使った場合, 78 秒.