Japanese memo of "The Rendering Technology of Ryse" @ GDC2014

概要




物理ベースレンダリング

  • スペキュラの BRDF はクックトランスによるマイクロファセットモデル.
  • NDF は GGX で, スムースネスとαはデザイナーさんが直感的に調整できるように式でマッピングしてある.

  • フレネル項はシュリック近似. 可視性の項は Schlick-Smith 近似.

ディファードシェーディング

  • Crysis2 ではライトプレパス(Deferred Lighting)だった.
    • G-Buffer は最小限にして, 法線とラフネスだけを保存していた.
    • ディファードパスでイラディアンスとラディアンスを計算していた.
    • 2 回目のジオメトリパスで, マテリアルの属性を適用していた.
  • しかし, 物理ベースシェーディングではフレネル項がスペキュラリフレクタンス(F0)を必要とするため, ライトプレパスとは相性が悪い.
  • なので, Ryse ではディファードシェーディングに移行した.
    • これでジオメトリのサブミットが 1 回で済むようになった.
    • 結局, 前世代のコンソールよりもさらに G-Buffer を圧縮することでなんとかなった.

  • 下が GBuffer の構成で RGBA8 が 3 枚と, デプス・ステンシル.
  • スペキュラカラーは YCbCr で保存しているので, ブレンド演算中に αに書けない場合の「非金属のデカール」は G-Buffer にブレンドできる. (非金属の半透明デカールブレンドができるということ ?)
  • 法線については Crytek の Best Fit Normal という手法によって, RGB8 でも大丈夫なようにしてある.
  • スペキュラカラー(金属用)と透明度(非金属)は同じバッファの領域を使って, 排他的に利用している.
  • 事前計算 AO 用のチャネルも用意してあるけど, あまり使わなかった.

  • 最初は Forward+ と MSAA を使うことを考えていたが, 実用上の問題があった.
    • デカールや濡れた面のように, 表面の材質を変えるタイプのものにどう対処するか ?
    • ライトカリングのために, ジオメトリパスが 2 回必要. (Z-Prepass)
    • 多くの研究はポイントライトだけ考慮しているけど, ゲームではプロジェクトライト, シャドウを落とすライト, エリアライトのようにライトの種類が複雑.
    • ライトの種類が増えると, 分岐時の GPR の使用数が増えるので, シェーダの実行効率が低下する.
    • シェーディングはクワッド(2x2)単位で行われるので, 小さいトライアングルでは無駄なシェーディングが発生する.
  • 最終的にはハイブリッドな方法を使っている.
    • 大部分のオブジェクトはディファードシェーディングのパス.
    • 髪や耳といった特殊マテリアルだけは Forward+.

タイルベースのディファードライティング

  • ディファードシェーディング時に, 複数のライトがオーバーラップすると G-Buffer の読み込みやライティング結果の書き出しが重複してしまう.
  • そこで BattleField3 で行っていたタイルベースなディファードシェーディングをしている.
    • 画面をタイルに分割して, タイルごとにライティングの影響を受けるライトのリストを作ることをコンピュートシェーダで行う.
    • タイルの Min/Max のデプスを使って, ライトのカリングをする.
    • カリング後は, タイルごとのライトのリストをループしてシェーディングを適用する.
    • これを行うと, G-Buffer を読む回数が 1 ピクセルあたり 1 回で済むし, シェーディング結果を書き出すのも 1 回で済むのでかなり帯域の節約になる.
  • Ryse では単一のコンピュートシェーダで, ライトのカリングとそれによるライティングを行っている.

  • 課題
    • プリミティブのフラスタムカリングは正確ではなく, false positive(カリングされるべきではないのにカリングされる)が起きる. (ここは謎の箇所. @kenpex が Twitter で少し議論してた.)
    • ライトリソースがすべて, コンピュートシェーダからアクセスできる必要があること
      • 大きなアトラステクスチャに敷き詰めている, シャドウマップ
      • テクスチャアレイとして保存しているディフューズやスペキュラ用のキューブマップテクスチャ
      • テクスチャアレイに保存している射影テクスチャ
    • シェーダの GPR レジスタの数をコントロールすること
      • 異なる種類のライトで動的分岐を使うケース
      • 分岐が深いと, 使用する GPR の数が増えて, スレッドの占有率が低下すること
      • GPR の数の制限に収まるように, コードを手動で調整する必要があること
  • タイルベースでライトカリングすると, 平均的に 2-5 msec は速くなる. もっと悪い条件だと, より速くなる.

スペキュラのエイリアシングの除去

  • 物理ベースシェーディングはスペキュラのエイリアシングの影響を受けやすい.
    • 何故なら, 正規化した BRDF による高い輝度値と, 法線マップの高周波な情報が組み合わせるので.
  • ミップマップで法線マップをダウンサンプリングすると, 情報が減る.
    • ミップマップを平均によって作ると, 分散が減る. 凸凹した法線マップが滑らかになってしまう.
  • Ryse では法線とラフネスを対応付けている.
    • 法線マップはマクロスケールの表面の凹凸を示し, ラフネスはマイクロスケールの凸凹.
    • 法線マップのアルファチャンネルにラフネスを格納しておく.
    • ラフネスマップに法線マップのミップマップの分散値を事前に書き込んでおく.
      • Toksvig の式によって, 分散を推定して新しいラフネス値を求めて使う.

  • しかし, デカールや雨で濡れた面のように G-Buffer のラフネスをランタイムで加工する場合に問題が起きる.
  • 従って, スクリーンスペースで法線マップの分散フィルタを適用して, ラフネスを書き換えている.
    • ただシルエットのエッジ付近に適用しないようにデプスの差分値を見ている.
    • 細くて反射率が高いジオメトリにも役に立つ.
    • しかし, grazing light angle のときに輪郭でのアーティファクトが発生する.

  • 下図が適用前.

  • 下図が「スクリーンスペースで法線マップの分散フィルタを適用して, ラフネスを書き換えた場合」.

ライティング・モデルについて

  • Ryse で直接光のために使われているライトはポイントライト, プロジェクターライトなどで全てシャドウを落とすようになっている.
  • リアライトは使っていない.
    • 何故ならゲームの都合上, 人工的なライト(電球や蛍光灯)の代わりに「たいまつ」を使っていたので.
    • しかし, エリアライトは物理ベースレンダリングでの不自然に小さいハイライトを防ぐために必要である.
    • そこで, BRDF の中で最小のハイライトサイズを決めていて, ハイライトが必ずそれ以上の大きさになるようにしている. (この方法でうまくいった.)

  • ライトの減衰の話
    • 従来的な半径ベースの減衰モデルは不自然.
    • 従って, 半径 bulb_size の球が光を放射するような物理ベースなモデルに移行した.
    • bulb_size にはいろんな値を入れることができて, 例えばフェイクで太陽っぽいエリアライトを作ることもできる.
    • ライトの強さは, ライトから 1 m 離れたときに特定の値になるように正規化している.
    • ライトの影響がなくなる半径のパラメータも, パフォーマンスやライトリークを防ぐ都合上, まだ必要になっている.

間接光と環境プローブ

  • スペキュラに影響を与えずに, ディフューズだけに影響するような「一定の環境光」や「半球ライトによる環境光」は使っていない.
  • 何故なら, ディフューズとスペキュラの反射率の辻褄が合わなくなるし, マテリアルがのっぺりと見えるから.
  • 大体の間接光は環境プローブでキャプチャした結果を利用し, そしてローカルリフレクションと組み合わせている.
  • 一部, 一様な感じをなくすために後述する Ambient Light (環境ライト) を使っている.

  • 環境プローブ
    • 1 ステージで 100 個ぐらい, 手動で配置してある.
    • 解像度は 256x256 で BC6H に圧縮してある.
    • キューブマップについては「改造した ATI CubeMapGen」でオフラインでフィルタ処理をかけておく.
    • プローブはレイヤーを持つようになっていて, ローカルなプローブはよりグローバルなプローブを部分的に上書きする.

  • 環境プローブは OBB での視差補正が適用してある.
  • プローブ間の急激な遷移を防ぐために, ブレンドの重みが緩やかに減衰するようにしてある.
  • ボックス用のブレンドの重みの計算については, 一旦, ボックスを球にマッピングしてから下のように計算する.

環境光

  • しかし, 上の環境プローブで範囲が大きい場合, ライティングの変化が乏しくなって, のっぺりとした見た目になりやすい.
  • なので, Ambient Light(環境光)というデザイナーさんが手置きで配置するライトを用意してあり, これはディフューズとスペキュラの両方に影響する.
  • 強さが 1 より大きい場合は, バウンスライト的な役割.
  • 強さが 1 より小さい場合は, 減算ライトによる AO 的な役割.

  • 下図は Ambient Light がない場合.

  • 下図は Ambient Light がありの場合.

環境 BRDF

  • 通常のフレネルの式は完全に滑らかな表面に対して有効である.
    • マイクロファセットは小さい完全に滑らかな表面の集合なので, Analytical な BRDF では機能している.
    • しかし, 事前フィルタリングした環境マップでは直接的には使えない.
  • スペキュラローブに対して, BRDF積分する必要がある.
    • キューブマップに保存されているラディアンスを, 積分の外に出す.
    • 2 つに分離した積分のうち, 環境 BRDF の部分を残す.
  • Ryse では Black Ops2 と同様の手法を利用. ( "Call of Duty: Black Ops 2 のより物理ベースなシェーディング(SIGGRAPH 2013)"の説明 - OLD hanecci’s blog : 旧 はねっちブログ )
    • 反射率 0-100% に相当する「積分済みの環境BRDF」を事前に求めておいて, 2D のルックアップテーブルに保存しておく.
      • パラメータは ラフネスと N・V.
  • 表面の反射率に応じて, このルックアップテーブルを 2 つ参照して結果を線形補間して利用する.



ラフネスを考慮した(グロッシーな)ローカルリフレクション

  • 環境キューブマップの視差を補正したとしても, 環境キューブマップの影響範囲はとても限られている.
  • なぜなら, 細かいリフレクションや動的なオブジェクトのリフレクションは環境キューブマップにキャプチャできないから.
  • なので, スクリーンスペースの情報を利用できるケースでは, ローカルリフレクションを利用する.
  • この実装は Crysis2 で最初に使われたが, 今回はマテリアルのラフネスの変化に対応できるように改良してある.

  • 本当はリフレクションのベクトルに沿って, スクリーンスペースでコーントレースする処理が正しい.
  • そして, ラフネスに応じたコーンの幅と, レイの距離によってどのレイフレクションの像のミップレベルを参照するかを決めるべき.
  • しかし, Ryse で使った方法はとてもシンプルで軽い処理のもので, リフレクションの像を得るためにただ単にスクリーンスペースでレイマーチするだけ.
  • リフレクションの像をダウンサンプリング+ガウシアンフィルタでフィルタ処理してミップマップを作る.
  • このとき, αに対しても同じフィルタをかけることで, ぼやけたリフレクションは見えにくくなるようにしている.
  • マテリアルのラフネスに応じて, どのミップレベルを選んで利用するかを決めて, 環境キューブマップの結果に対してブレンドする.
  • これは厳密なコーントレーシングでもないし, 距離に応じたブラーも考慮していない.
  • だけど, レイトレース後にフィルタをかけているので, 粗いマテリアルのアーティファクトは少ない.

顔のレンダリング (Skip)








髪のレンダリング (Skip)






目 (Skip)


アップスケール

  • Ryse では 3D シーンは 900p でレンダリングして, 1080p にアップスケールしている. 一方で, UI は 1080p で描画している.
  • アップスケーリングは自前のシェーダで行っていて, XBox のハードウェアのアプスケーラーはまだ評価していない.

静的シャドウマップ

  • ロード時にだけ, 静的なオブジェクト用の巨大な静的なシャドウマップを作っている.
  • 4,5 番めのシャドウのカスケードの代わりに使っている.
  • 遠景の静的なオブジェクトを描画して, 静的なシャドウマップを毎フレーム更新する必要がなくなる.
  • 解像度は 8192x8192 16 bit で, ゲーム中の 1km の範囲をカバーしている.


参考文献