Japanese memo of "inFAMOUS Second Son:Engine PostMortem"@GDC2014

概要


  • 下図は概要です. 今回のメモについては実際のスライドの発表順と順序を結構,入れ替えています.

グラフィックス

シェーディング

  • 下図は G-Buffer の構成で, 解像度は 1080p.
  • 最大で 8 枚のバッファ( 5-6 + デプス/ステンシル)に書き込んでいて, 1 ピクセルあたり 41 バイトの書き込み.
  • このバッファの 1080p でのデータサイズは 85 MB だけど, PS4 の全体のメモリサイズに比べると大したことない.

  • 物理ベースなマテリアルとライティングを使用.
    • デザイナーさんにとっては非直感的になるが, ライト環境の変化に強くなる.
    • 最初はスペキュラに Blinn-Phong を使っていたけど, プロジェクト後半で GGX に移行したので, ランタイムで下図の変換処理をかけている.
  • デザイナーさんに対して 物理ベースシェーディングの訓練をするのに時間をかけた.
  • テクスチャの値を適切な範囲に収めるために, PhotoShop でコンストレイントのレイヤーを追加した.
    • アーティストさんが完全な黒や白を塗った場合, 名前が付いているレイヤーが自動的に値を適切な範囲に調整するようにしている.
    • 完璧な仕組みではないけど, うまくいった.
  • 下のケースではディファードシェーディングは使っていない.
    • 事前積分してある肌, 異方性の布や髪の毛, ガラス
    • シャドウ : 法線方向のオフセット, 8x8 PCF, スクリーンでの解決(?)


  • コンピュートシェーダによるタイルベースなディファードライティング+
    • 16x16 ピクセルのタイルとオーバーラップするライトを求める(カリング).
      • ライトの種類による動的分岐を減らすために, ライトの種類でライトをソートしておく.
      • ここまでの話はライトのディフューズ直接光のためのカリングの話.
      • 一方で, スペキュラ用には別途, 1 タイルあたり 最大 9 個のライトをカリング可能.
    • 後でフォーワードのシェーダでも使うために, タイルごとにライトのリストを保存しておく.
    • 最大 2500 命令, GPU 負荷は 1 フレームあたり 2-10 msec.

  • シャドウの話
  • 量子化(? unorm とか?)を使ったカスケードシャドウマップ
    • 2048 * 2048 解像度のシャドウマップ を 3-4 枚. さらに, 4 個のスポットライト用のシャドウマップ.
  • PCF (Percentage Close Filtering)
    • 大部分は 8x8 の PCF フィルタで, 最も遠いカスケードは 4x4 の PCF フィルタ.
    • 見た目を良くするために, カスケードごとにシャープさを変更している.
    • 肌へのシャドウは 16x16 の PCF フィルタと
    • 髪の毛のシャドウは, 4x4 の PCF フィルタとシャドウライト方向へのスクリーンスペースのレイマーチでシャドウ判定.
  • "Normal Offset Shadows" も利用(シャドウのバイアスとして, 法線方向にオフセットする?).
  • シャドウの合成はスクリーンスペースで行う.(いわゆる, ディファードシャドウ?)

  • 描画回数を減らす件
  • ジオメトリをマージして, 描画キック回数を減らした方がいいのか ?
    • カリングされにくくなるし, シェーダのオーバーヘッドが増える, テクスチャのアトラス化.
  • シェーダの種類でソートしたら, 3 倍速くなった.
  • インスタンシング : 5-20% 速くなった, 理想的なケースだと 6 倍.
    • 描画データを全てバッファにアトラス化する.
    • シングルバッファにしてしまう.
  • しかし, デプスソートが必要な半透明描画ではうまくいかない.

ポストエフェクト
  • 下図はポストエフェクトの一連の流れで, 標準的な実装.
    • 0. スクリーンスペースのローカルリフレクション
    • 1. DOF + ボケ
    • 2. オブジェクトのモーションブラー
    • 3. レンズ汚れ付きのブルーム
    • 4. 最後のトーンマップ


  • 下図は一連のポストエフェクトの適用後の図.

  • ポストエフェクトでのピクセルシェーダの利用
    • コンピュートシェーダよりわずかにピクセルシェーダの方が速いことが多い.
      • ラスタライズしたタイルが複雑なフォーマットと合っているから ?
      • ROP のキャッシュが効いていて, パイプラインがより良いから ?
    • コンピュートシェーダ固有の機能( Local Data Storage など)が使えると良い
      • しかし, ピクセルシェーダに勝つにはまだ注意が必要 (ブラーのためのテクスチャタップなど)
    • 常にタイリングを使うこと (フィルタサイズは 8x8 かそれ以上)
      • フェッチ時に, 64B のキャッシュラインをまたがないこと

SH によるディフューズ間接光
  • 四面体ベースのグリッド状のライトプローブ
    • 最初, ライトマップを使うことを考えたが, UV だけで 1 ブロックあたり 最大で 12 MB 必要だったので使わないことにした.
    • そこで, 四面体ベースのグリッドを作ってその頂点の位置にライトプローブを配置することにした.
    • 四面体の構築については, Tetgen というミドルウェアを使って三角形メッシュから四面体を自動生成している.
    • 1 個のプローブのデータサイズについては RGB のそれぞれのチャンネルにつき, 0,1,2 次の SH の基底(合計9個)を利用しているので, 3channel * 9SH_basis = 27 rgb16f となっている.
      • スライドには 28rgba16f/probe と書いてありますが, 開発者の @adrianb3000 に Twitter で質問したところ間違いだと言っていました.
    • 世界全体で 25 MiB で, 1 ブロックあたりの疎なメッシュのデータサイズは 1-3 MiB.
    • GPU の処理コストは 1-3 msec. ( 6%-18%)
  • 短所
    • デザイナーさんがいくつかプローブを配置する必要があり, その維持コストがかかる.
    • ライトリークが起きる. 地形に埋まったプローブが黒くなるので, 対処が必要.
    • 四面体の縮退(?): 位置を微妙にずらしたり, エッジ法線をごまかす必要がある.
    • ベイクに時間がかかる. 1 ブロックあたり 15 分.

  • 四面体ベースのグリッド状のライトプローブのサンプリング(後述するようにコンピュートシェーダを使っている)
  • 事前にブロック単位(?)で 16x16x16 のグリッドを作って, そのグリッド内の四面体のインデックスを事前に保存しておくことで, 四面体のインデックスを高速化する.
    • 描画単位あるいは頂点単位で, 位置座標がどの四面体に属するか?(四面体のインデックス)を探索する.
    • ピクセル単位だと, 処理が重すぎる.
  • 無駄な探索を減らすために, データをキャッシュしておく.
    • 描画単位で, 以前の四面体のインデックスをキャッシュしておく.
    • 静的で動かない頂点単位で, 以前の四面体のインデックスをキャッシュしておく.

  • コンピュートシェーダで, 頂点単位で四面体のインデックスの探索をする場合
    • for ループで, その頂点がどの四面体に属するか? を探す.
    • 1 メッシュあたり 1 回ディスパッチする. シェーダの命令数は 233 命令.
    • 61,000 個の頂点で, 379 メッシュの場合, ディスパッチ回数は 379 回. 0.2 msec (約 1%)
  • コンピュートシェーダで, 描画単位で四面体のインデックスの探索をする場合
    • 1 フレームに 1 回ディスパッチする.
    • シェーダの命令数は 315 命令, 1334 個の SH サンプルを処理するのに, 0.017 msec (約 0.1%)
    • CPU から GPU に移行すると 40倍 速くなった.

  • 下図はディフューズ間接光がない場合で, 単一の低解像度のディフューズキューブマップ(Ambient Cube)だけを適用した場合.

  • 下図は SH ディフューズ間接光を適用した場合. 遮蔽がある箇所できちんと暗くなるので, 高いコントラストの絵になっている.

スペキュラ間接光
  • スペキュラ用のローカルな環境キューブマップ
    • 解像度は 256x256 ピクセル. 最大 256 個.
    • 実際にメモリを占めるデータサイズは 175 MBで、ロードに時間がかかる.
    • アトラス化するとメモリを占めるサイズが増えるが, 最適化で 2 次コピーが除去できる. (?)
    • 事前にラフネスに応じたフィルタリングを適用して, 各ミップレベルに保存.
    • ランタイムで, グロスの値に応じて適切なミップレベルのものを参照して適用.
    • エッジ付近までの距離に応じて, 値を歪ませている(?).
    • 沢山配置する必要があるので, オープンワールドのタイプのゲームとは相性が悪い.
  • スクリーンスペースのローカルリフレクション
    • 1/2 * 1/2 解像度で, ほとんどブルートフォースの実装.
    • アーティファクトとのバランスの調整をするために, いろいろいじくり回している.
    • リフレクションとして映る遮蔽物(reflection casters)と, リフレクションが映るもの(reflection receivers)を制限することでアーティファクトを防いでいる.

  • 下図はスペキュラ用にローカルな環境キューブマップを使っていない状態. 空の映り込みが多くなってしまう.

  • 下図はスペキュラ用にローカルな環境キューブマップを適用した状態. 周囲の物体が映り込むようになって, 接地感が出た.

  • しかし, 下図のようにローカルな環境キューブマップには「動的なオブジェクト」や「壊れる物」, 「高くて細いもの(柱とか)」は撮影しておけない.

  • そこで,下図のようにスクリーンスペースのローカルリフレクションを使うと, それらに接地感が出る.

コンピュートシェーダの利用








8GB のメモリの利用



CPU のマルチスレッド












リソースのストリーミング


アセットのワークフロー

Maya によるマップエディットの話








ゲーム内エディタの話



シェーダ編集


アートの話




ネットワーク UI