Japanese memo of "Taking Killzone Shadow Fall Image Quality into the Next Generation" @ GDC2014

概要

背景

  • 開発期間は 2011/4 〜 2013/11/15 の約 2.5 年
  • レベルの大きさは Killzone Shadow Fall3 に比べると 10-100 倍になった.
  • 1 つのレベルセクション内のジオメトリインスタンスの数も 2 倍になった. 大体 10,000-25,000 個.
  • ゲーム内に配置したジオメトリインスタンスの総数は 689,334 個.
  • 1 つのセクションの静的ライトの数は 200 個. ライトマップ用には 200 個. 動的ライトは 100 個ぐらい.
  • 頂点やテクスチャのディティールは 4 倍.
  • 1 キャラクター 40,000 頂点. マテリアルごとに 6-12 枚のテクスチャを利用.

ライティング

  • 物理ベースライティング, ライトは全てエリアライト.
  • リフレクションはライトモデルとエリアライトに対してマッチしている.
  • 詳しくは GPU Pro5 を参照.

ライトプローブによる間接光

概要
  • Killzone 3 では地形にはライトマップを使用し, オブジェクトにはオブジェクト単位でライトプローブを適用していた.
  • Killzone Shadow Fall ではライトプローブを地形とオブジェクトの両方に適用することにした. (しかし, この技術を導入する時期が遅れたたので実際のゲームではライトマップを使っている地形が多々ある. )
ライトプローブの自動配置の方法
  • シーンのメッシュをボクセル化する. (ゲームプレイエリアは 1-2m, 背景は 10m の間隔)
  • ポリゴンの表面に沿ったボクセルにライトプローブを配置する.
  • そして, ライトプローブから四面体を作る.
  • しかし, このままだと鋭角な四面体が作られてしまう箇所があるので, ポリゴン沿いでない空間にも少しプローブを配置する.
ランタイムでのライトプローブの参照
  • セクションごとに 200-3000 個のライトプローブがある.
  • ライトプローブはピクセル単位で適用したい.
  • 従って, 四面体を疎なグリッド(1 セル 16m^3)のセルに割り当てて, そのセルごとに BSP を作った.
  • これによって「画面中の位置座標 -> 疎なグリッド -> その BSP -> 該当する四面体」というように参照するライトプローブの探索を高速化した.

ローカルライトのシャドウの最適化

  • ローカルライト(エリアライト, スポットライト)用のシャドウマップ用に, 事前にシャドウ用のポリゴンを作ってこれを 1 回のドローコールで描画した.
  • シャドウ用のポリゴンを作る際には各三角形のシャドウマップの寄与をチェックして, 不必要な 60-80% の三角形は取り除いた.

ディレクショナルライトのシャドウの最適化

  • ベイクしたシャドウマップ + シャドウ用のポリゴンで解決.
  • ベイクしたシャドウマップの作成時には, シーン全体を覆うシャドウマップ(解像度 16,000 * 16,000)を作成してそれをダウンサンプリングする.
  • そして, それを Signed Distance Field (シャドウの縁までの距離)として保存する.
  • 上のベイクしたシャドウマップに情報として含まれていないポリゴンについては, 先程のローカルライトと同様にそれ用のシャドウ用のポリゴンを作る.

リフレクション

  • リフレクションのグロシネスは 表面の粗さ(リフレクションのコーンの角度を決定)とリフレクションレイの移動距離に応じて決める.
  • このリフレクションのグロシネスを使って, ローカルリフレクションやローカル環境キューブマップで使用するミップマップレベルを選択する.
  • リフレクションは各ピクセルで下の (A)->(B)->(C) の優先度でチェックして, 有効だったリフレクションを利用する.
  • (A) ローカルリフレクション(Realtime Local Reflection)
    • リフレクションの計算は 1/2 * 1/2 解像度. ただし, 4 フレームごとに 2x2 のピクセルの異なる位置を参照して計算してスーパーサンプリングする. (Temporal Filtering )
    • 色の類似性のチェックで history buffer を参照するかどうか? は Dust514 の SIGGRAPH 2010 の発表()と同様の方法. ( http://advances.realtimerendering.com/s2012/CCP/Malan-Dust_514_GI_reflections(Siggraph2012).pptx )
    • レイマーチはスクリーンスペースで行う.
      • ラフネスが滑らかなときはアーティファクトが目立たないようにするため, レイマーチ時のステップサイズを小さくする.
      • デプスバッファとのレイマーチにはセグメントヒットを用いる.
      • リフレクションとして参照するカラーバッファは前回のフレームのもので, Reverse reprojection をしてずれを減らす.
      • ローカルリフレクションの計算結果として, リフレクションのカラー(hit color), ヒットしたかどうか(hit mask), リフレクションのグロシネスを出力する.
      • レイヒットは FPS 用の武器にはヒットしないようにマスクできるようにしている.
    • レイマーチで求めたリフレクションの結果をぼかしながらミップマップを作る.
    • 前述したリフレクションのグロシネスを使って, 適切なミップレベルを選択する.
  • (B) 静的なローカルの環境キューブマップ
    • エリアを定義するボックスはデザイナーが手置き.
    • CubeMapGen を使って, ラフネスに応じて環境キューブマップをぼかして各ミップレベルに格納する.
    • エリアのボックスを使って視差補正.
    • ランタイムではタイル単位で影響がある環境キューブマップを参照して使う. 使用するミップマップは前述したリフレクションのグロシネスを利用する.
  • (C) 静的な背景の環境キューブマップ
    • エリアがないタイプの背景用の環境キューブマップ.

アンチエイリアシング

  • 最初は MSAA を使っていたが, GPU の負荷が少し増えてしまうという問題があった.
  • 次に Temporal reprojection + FXAA (MSAA なし)を試した.
    • 約 16 フレーム分の結果をヒストリーバッファに蓄積して, 近傍の色の類似度に応じてブレンドして使った.
    • よりサンプル数を増やすために, サブピクセル単位でジッタリングする方法も試したが結果が安定しなかったので結局使わなかった.
  • 結局, Temporal SuperSampling AA を利用した.

  • 図にすると, こんな流れ?


  • シングルプレイヤーでは 30fps 1920x1080p で, マルチプレイヤーの場合は 60fps Temporal 1920x1080p にしている.
  • Temporal 1080p ではフレームの描画時に縦方向のピクセルを交互にレンダリングすることによって, 1 フレームでは 960x1080p の描画だけを行い, その代わり以前の複数フレーム分の描画結果を参照してブレンドして擬似的に 1920x1080p 相当に見せている.
  • まず, 今のフレーム(N)で描画したピクセルはそのまま利用する.
  • 次に今のフレーム(N)で描画していないピクセルについてはリバースリプロジェクションを行って N - 2 のフレーム(960x1080p)から参照できるピクセルがないか? を調べる. 具体的には近傍の色の類似度やモーションベクトルの一貫性をチェックする.
  • 上の方法でもピクセルの色が決まらない場合, N - 1 のフレーム(960x1080p)に対してリバースリプロジェクションを行う.
  • そして, まだ上の方法でもピクセルの色が決まらない場合, 今のフレーム(N)のピクセルを補間してピクセルの色を求める.
  • さらにフル解像度のヒストリーバッファを使ったリプロジェクションも行うが, ゴーストなどのアーティファクトが出やすいので, ピクセルのモーションベクトルがほぼ静止しているときにだけこの結果を用いる.