ゲームエンジンや、フレームワークを作成する上で、避けては通れないのが描画パフォーマンスの最適化です。
シーンにたくさんの光源を配置したとき、従来のレンダリング(ForwardRendering)のままだと、ピクセルシェーダ内ですべてのピクセル*すべての光源の計算が走ってしまい、フレームレートが急降下(パフォーマンスの低下)を招きます。
そういった場合、DeferredRendering(遅延レンダリング)を使用することで改善を行うことができます。
本記事では、実装編へ入る前に、DeferredRenderingの概念や仕組みなどについて、紹介します。
前提知識
- 従来のレンダリング方法(
ForwardRendering)について理解している
ForwardRenderingの限界
フォワードレンダリングでは、シーン内のオブジェクトを1つずつ描画し、そのピクセルを描画するタイミングでそのピクセルに影響を与えるすべての光源からの光をまとめて計算します。
これを数式で表すと、計算量は以下のようになります。
$$
\text{計算量} = O(\text{オブジェクト数} \times \text{ピクセル数} \times \text{光源数})
$$
ここで問題になるのが、カメラから見えていない(奥に隠れている)ピクセルや画面の大部分を覆うオブジェクトに対しても、無駄にすべての光源計算が走ってしまう点**です。
深度テスト(Zテスト)によって手前のピクセルが勝つ場合でも、複雑なライティング計算が走った後に破棄されてしまうケースがあり、GPUにとって無駄で非常に重い負荷となります。
DeferredRenderingとは
ここまで書いたように、ForwardRenderingではオブジェクトを描画しながらライティング計算を行うため、光源数が増えるほど負荷が大きくなります。
一方DeferredRenderingでは、描画とライティングを分離して処理します。
Plain Text
1. オブジェクトの情報をG-Bufferへ記録
↓
2. 画面全体に対してライティング計算
↓
3. 最終画像を出力
従来のようにオブジェクトごとにライティングを行うのではなく、最終的に画面へ表示されるピクセルに対してのみライティングを行うことが特徴です。
G-Bufferとは
G-Bufferとは、描画に必要なジオメトリ情報を格納するための複数のレンダーターゲットのことです。
座標や法線、マテリアル情報や震度情報などを、Geometry Passとして、書き込みます。
こうして情報を保存しておくことで、ライティングを行う段階で情報を使用することができます。
DeferredRenderingのメリット
-
大量の光源に強い
- オブジェクトごとにライティングを行わないため、
Forward Renderingのように指数関数的に計算量が上がらない
- オブジェクトごとにライティングを行わないため、
-
無駄なライティングを削減できる
-
Forward Renderingでは、奥から手前を順に描画するため、最終画面にないものにもライティングを行う -
Deferred Renderingでは最終的に表示されるピクセルにだけライティングを行うことができる
-
-
スクリーンスペースエフェクトと相性がいい
-
G-Bufferに保存された情報をそのまま利用できる
-
DeferredRenderingのデメリット
-
メモリ消費が大きい
- 3Dオブジェクトのテクスチャを保持する必要があるため、
Forward Renderingと比較してVRAMの消費量が大きくなる - 高解像度環境では負荷が顕著になる
- 3Dオブジェクトのテクスチャを保持する必要があるため、
-
半透明オブジェクトとの相性が悪い
-
G-Bufferには通常1ピクセルにつき1つの情報しか保存できないため、奥のオブジェクト情報が必要になる半透明オブジェクトは相性が悪い - 透明オブジェクトを使用する場合は、
Forward Renderingとのハイブリッド実装が必要
-
-
G-Bufferの読み書きコストが発生する
-
G-Bufferに読み書きする追加処理が発生するため、光源が少ないシーンではForward Renderingの方が高速な場合もある
-
-
シェーダデバッグが複雑になる- 複数の工程へ分かれるため、問題の切り分けが難しくなる
総括
-
DeferredRenderingでは、複数の光源計算を使用する場合に圧倒的なパフォーマンスを発揮する -
G-Bufferに書き込み、ライティングを遅れて行うことで、描画を行う手法である