Japanese memo of "Crafting a Next-Gen Material Pipeline for The Order:1886" @ GDC2014
概要
- 趣味で作成中の http://d.hatena.ne.jp/hanecci/20140322/p8 の日本語のメモです.
- 個人用のメモなので, 内容はざっくりしたものになっていますがご了承下さい.
- 内容的に SIGGRAPH 2013 の PBR のコースと講演内容(http://blog.selfshadow.com/publications/s2013-shading-course/ )が被っているものがあります.
エンジンの概要
- ツールのフレームワークは C++ と Qtで, Maya への組み込みも可能.
- C++ によるカスタムのビルドパイプラインで, マルチスレッドのビルドシステムで分散キャッシュに対応している. ビルドマシンは 25 台.
- Maya 2014 の Viewport 2.0 の機能を使って, Maya 内にレンダラーを組み込めるようにしてある.
- Maya にどの UI の要素を描画させるか ? も指定可能.
- マテリアル/パーティクル/レンジフレアなどが Maya 内でリアルタイムにエディット可能.
- レベルエディタ的な役割も持っている.
メモリ
- メモリ使用の内訳.
物理ベースシェーディング
- スペキュラの BRDF はクックトランス
- NDF は GGX, Geometric term は Smith geometric term, フレネルはシュリック近似.
- ディフューズはランバートの BRDF. スペキュラとのバランスを取るため, スペキュラの逆数を乗算している.
- シェーダ計算の最適化についての詳しくは SIGGRAPH 2013 の資料を参照.
- 0-1 のラフネスは, リニアっぽくない挙動をするのでデザイナーさんにとって扱いにくい. 結果として, 0-0.1 の範囲のレンジしか使われない.
- そこで, デザイナーさんには sqrt(ラフネス)の値をテクスチャに書き込ませて, シェーダ内で ラフネス^2 して使う方法にした.
- その方がデザイナーさんのとってラフネスが扱いやすくなった. またシェーダでの値のブレンドもしやすくなった.
- 以下は GGX のシェーダコードの実装例.
シェーダ
- 手書きの Uber シェーダで, #if を沢山使っている.
- 主要なマテリアルの機能については, マクロ定義してある.
- マテリアルをビルドするときには, シェーダのソースコードとマテリアルのアセットを入力として, マテリアル固有の新しいコード(マテリアル用にハードコードされたパラメータ, テクスチャやサンプラ定義, アニメーションプロパティの定数バッファ, テクスチャやアニメーションのパラメータを扱うために自動生成したもの)を生成する.
- 一方で, Permutation (マクロ定義とエントリポイントの組み合わせ)が何種類もある.
- シェーダのフローの図.
- ステージのマテリアルのシェーダをまとめたものを Shader Set として, それもビルドしている.
- 長所
- マテリアル単位でシェーダが最適化されていること
- ゲーム自体でランタイムでシェーダコンパイルすることがない.
- 短所
動的ライティング
- タイルベースのフォーワードレンダリング(Forward+)を利用.
- デプスプリパスを行っている.
- タイルベースなライトカリングをしていて, タイル単位でライトのリストを持つ.
- ライトのカリング時には、タイルの depth の min-max を使っている.
- 以下はこの手法に行き着くまでの流れ.
- 最初, ライトプレパスを試した.
- それから 太めの G-Buffer を使ったディファードシェーディングを使い, コンピュートシェーダを利用したタイルベースなディファードを実装した.
- しかし, シェーディングモデルを変える際に G-Buffer の構造を変えるのに苦労したので, Forward+ に移行した.
- また MSAA が好きなので使いたかった.
- 今回はダイナミックなエリアライトはゲーム中で使っていない.
- 半透明のメッシュにディフューズとスペキュラの両方が適用される.
- そのために, 半透明用にデプスプレパスと, 半透明用のデプスバッファを使ったデプステストをする.
- 半透明用のデプスバッファを作ることで, 下のようにタイルのデプスの最小/最大の値を求めることができ, その結果, タイル単位で半透明メッシュに適用するライトのリストをライトカリングで求めることができる.
- タイルの最小デプス = 半透明メッシュのデプスの最小値
- タイルの最大デプス = 不透明メッシュのデプスの最大値
- パーティクル用には頂点単位のライティングを利用.
ライトマップによる静的ライティング
- 静的なオブジェクト用には事前計算したライトマップを利用.
- 動的なオブジェクトには事前計算したライトプローブを適用.
- 0,1,2 次の SH 基底を使っているので, 9 個(1+3+5)の係数.
- スペキュラ用の環境キューブマップには事前フィルタリングを適用.
- 環境キューブマップはエンジンで事前に撮影しておき, コンピュートシェーダで事前フィルタリングする.
- 下図は Maya 内の組み込んだレンダラーで GI のプレビューをしたときのテスト環境の画像.
- ライティング環境の標準化や露出値, HDR のワークフローが適切なものか ? の確認用.
- 下図では H-Basis のライトマップを使っているので, きちんと法線マップが考慮されていることがわかる.
Directional AO マップ
- 環境キューブマップや SH プローブのサンプル数は疎なので, 違う場所でそのまま使うとライトリークが起きる.
- そこで, キャラクターと地形用に, Directional AO マップ(H-Basis が基底)を事前にベイクしてある.
- 地形は SH プローブからのスペキュラライティングのマスクにしか, Directional AO を使わない.
- 動的キャラクターは SH プローブからのディフューズライティングと, スペキュラライティングのマスク用に, Directional AO を利用.
- 下図は銃のリフレクション・オクルージョンを考慮しない場合のもの. 銃の内部のエッジ部分のスペキュラが光っていて不自然.
- 下図は銃のリフレクション・オクルージョンを考慮した場合. 銃の内部のエッジ部分のスペキュラがマスクされて, 見た目が良くなった.
- 下図は銃のリフレクション・オクルージョンを可視化したもの.
- リフレクション・オクルージョンなので, 視点を変化させると見た目が変化する.
- 壁の方のオクルージョンのベイクは失敗しているので, 無視して.
キャラクター用のカプセルによる動的 AO
- 動的なキャラクターやオブジェクトが AO をキャスト(落とす)ように, スキニングのジョイントにカプセルを配置して解析的にカプセルの AO を計算している.
- 元々, Splinter Cell: Conviction, The Last of Us と同様の手法.
- オクルージョンの値は Inigo Quilez さんの記事を参考にしている.
- この手法を使うと, SSAO のような折り目に暗がりを入れる方法とは違って, ソフトで安定していて綺麗に減衰する AO になること.
- 通常の SSAO で同じことをやるにはカーネルを広げる必要がある.
- 最初はライトと同じようにカプセルのボリュームを扱っていた. つまり, タイル単位でカプセルをカリングして, フォーワードパス(Forward+)でカプセルのオクルージョンを計算していた. しかし, 処理が重すぎた.
- 従って, 解像度を半分にして, コンピュートシェーダでタイル単位のカリングと AO の計算をディファードパスで計算するようにした.
- こうすることで, 画面中で特に重いシーンでカプセルが 100 個以上になっても, PS4 の GPU で 1-2msec に処理が収まるようになった.
- カプセル AO が無効の場合
- カプセル AO が有効の場合
描画パスの分解例 0
- 下図はテスト用のアトリウムのシーン.
- 下図は地形用の焼き付けたライトマップと, イラディアンスプローブの結果.
- 環境キューブマップからのスペキュラ間接光.
- 太陽からのディフューズ直接光
- ディフューズアルベドだけ
- ベイクしたオクルージョンマップを使った リフレクションのオクルージョン.
- カプセルによる 動的 AO
- 最終結果
描画パスの分解例 1
- 製品レベルの絵の最終結果.
- 下図は地形用の焼き付けたライトマップと, イラディアンスプローブの結果.
- 環境キューブマップからのスペキュラ間接光.
- 動的なライトのディフューズとスペキュラ
- ディフューズアルベドだけ
- ベイクしたオクルージョンマップを使った リフレクションのオクルージョン.
- 16x16 ピクセル単位のタイルと交差しているライトの数.
- 緑(良い), 青(OK), 赤(悪い), 白(リリースしない)
- カプセルによる動的 AO
- 最後結果