Japanese memo of "inFAMOUS Second Son:Engine PostMortem"@GDC2014
概要
- 趣味で作成中の http://d.hatena.ne.jp/hanecci/20140404/p3 の日本語のメモです.
- 個人用のメモなので, 内容はざっくりしたものになっていますがご了承下さい.
- 下図は概要です. 今回のメモについては実際のスライドの発表順と順序を結構,入れ替えています.
グラフィックス
シェーディング
- 下図は G-Buffer の構成で, 解像度は 1080p.
- 最大で 8 枚のバッファ( 5-6 + デプス/ステンシル)に書き込んでいて, 1 ピクセルあたり 41 バイトの書き込み.
- このバッファの 1080p でのデータサイズは 85 MB だけど, PS4 の全体のメモリサイズに比べると大したことない.
- 物理ベースなマテリアルとライティングを使用.
- デザイナーさんにとっては非直感的になるが, ライト環境の変化に強くなる.
- 最初はスペキュラに Blinn-Phong を使っていたけど, プロジェクト後半で GGX に移行したので, ランタイムで下図の変換処理をかけている.
- デザイナーさんに対して 物理ベースシェーディングの訓練をするのに時間をかけた.
- テクスチャの値を適切な範囲に収めるために, PhotoShop でコンストレイントのレイヤーを追加した.
- アーティストさんが完全な黒や白を塗った場合, 名前が付いているレイヤーが自動的に値を適切な範囲に調整するようにしている.
- 完璧な仕組みではないけど, うまくいった.
- 下のケースではディファードシェーディングは使っていない.
- 事前積分してある肌, 異方性の布や髪の毛, ガラス
- シャドウ : 法線方向のオフセット, 8x8 PCF, スクリーンでの解決(?)
- コンピュートシェーダによるタイルベースなディファードライティング+
- シャドウの話
- 量子化(? 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 倍.
- 描画データを全てバッファにアトラス化する.
- シングルバッファにしてしまう.
- しかし, デプスソートが必要な半透明描画ではうまくいかない.
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 ディフューズ間接光を適用した場合. 遮蔽がある箇所できちんと暗くなるので, 高いコントラストの絵になっている.
スペキュラ間接光
- スペキュラ用のローカルな環境キューブマップ
- スクリーンスペースのローカルリフレクション
- 下図はスペキュラ用にローカルな環境キューブマップを使っていない状態. 空の映り込みが多くなってしまう.
- 下図はスペキュラ用にローカルな環境キューブマップを適用した状態. 周囲の物体が映り込むようになって, 接地感が出た.
- しかし, 下図のようにローカルな環境キューブマップには「動的なオブジェクト」や「壊れる物」, 「高くて細いもの(柱とか)」は撮影しておけない.
- そこで,下図のようにスクリーンスペースのローカルリフレクションを使うと, それらに接地感が出る.