前方レンダリングと遅延レンダリング
多光源で描画が急に重くなる理由を、フォワードとディファードの計算量の差から解き明かし、G バッファ・透過・MSAA の相性まで踏まえて Forward+ やタイル/クラスタ方式を根拠を持って選べるようになります。
- 1.フォワードは各物体を描くたびに影響する全光源をその場で計算し、素朴な実装では計算量が「物体×光源」に比例して多光源で破綻する。
- 2.ディファードは幾何情報を G バッファへ書き出してから画面空間で1回だけ照明を解き、計算量を「物体+光源」へ分離するが、透過が扱えず MSAA と相性が悪く帯域を食う。
- 3.Forward+ とタイル/クラスタ方式は画面を格子に分割して各領域に効く光源だけを絞り込み、フォワードの柔軟さ(透過・MSAA)を保ったまま多光源を捌く。
多光源で効いてくる「照明をどこで解くか」の設計
リアルタイム描画で光源が数十・数百と増えたとき、描画コストがどう伸びるかは 照明計算をパイプラインのどこで、何回行うか で決まります。フォワード(前方)レンダリングとディファード(遅延)レンダリングは、この「照明を解く場所」の違いから生まれた二大方式です。描画パイプラインそのものの流れは共通で、違うのは陰影計算のタイミングだけと言ってよいでしょう。
素朴なフォワードレンダリングは、各物体(正確には各フラグメント)を描くその場で、影響する光源をすべて足し合わせて最終色を求めます。ジオメトリと照明が同じシェーダの中で一体になっているのが特徴です。一方ディファードレンダリングは、いったん照明を後回しにし、表面の幾何情報だけを画面バッファ(G バッファ)へ書き出してから、画面全体に対して照明を別パスで解きます。この一手間が、多光源の計算量を劇的に変えます。
フォワードの弱点は「物体×光源」の計算量
フォワードの問題は計算量にあります。ある画素が最終的に画面へ残るかは深度テストが終わるまで確定しないため、素朴な実装では 見えない物体のフラグメントに対しても照明を計算してしまいます(オーバードロー)。さらに悪いことに、各フラグメントの着色ではそこに効く光源をループで回すため、コストは大まかに「描画フラグメント数 × 光源数」に比例します。
フォワードの照明コスト(素朴な実装, 概念):
各物体について:
各フラグメントについて: … 被覆ピクセル数ぶん(重なりで水増し)
各光源について: … 光源数ぶんループ
拡散+鏡面を加算
総照明計算 ≒ (延べフラグメント数) × (光源数)
→ 光源が増えるほど線形に重くなり、
重なり(オーバードロー)が多いほどさらに水増しされる
光源が数個ならこれで十分ですが、多数の点光源を置くシーンでは急速に破綻します。実務では光源ごとにシェーダのバリアントを増やしたり、1 物体が受ける光源数に上限を設けたりして凌いできましたが、いずれも本質的な解決ではありません。
計算量では不利でも、フォワードには捨てがたい長所があります。各フラグメントで材質ごとに自由なシェーディングを書ける(材質バリエーションに強い)、半透明を素直に扱える、そして幾何エッジ単位で標本を増やす MSAA がそのまま効く、という3点です。後述するディファードはこれらを苦手とするため、フォワード系は今も現役です。
ディファードは「幾何」と「照明」を分離する
ディファードの発想は、照明を解く前に「どの画素にどんな表面があるか」を確定させてしまう ことです。まずジオメトリパスで全物体をラスタライズし、各画素について照明に必要な属性——ワールド座標(または深度から復元)、法線、アルベド(基本色)、粗さ・金属度など——を複数のレンダーターゲットへ書き込みます。この属性の束が G バッファ(Geometry Buffer) です。
ディファードの2パス構成:
[1] ジオメトリパス(照明なし)
全物体をラスタライズし、深度テストで各画素の
「一番手前の表面」だけを G バッファへ確定
G バッファの例(複数レンダーターゲット / MRT):
RT0: アルベド(RGB) + 材質フラグ
RT1: 法線(XYZ, 圧縮)
RT2: 粗さ / 金属度 / AO
深度: 深度バッファ(位置復元に使う)
[2] ライティングパス(画面空間, 幾何なし)
画面の各画素で G バッファを読み、
効く光源だけループして照明を1回解く
→ 計算対象は「見えている表面」に限定済み
決定的な違いは、ライティングパスが 画面上の可視画素に対して1回だけ走る 点です。深度テストは幾何パスで済んでおり、隠れた表面はここに残りません。したがって照明コストは「見えている画素数 × 効く光源数」となり、オーバードローによる照明の水増しが消えます。計算量の構造が 「物体×光源」から「物体+光源」へ分離 されるのが、ディファード最大の利点です。
| 観点 | フォワード(素朴) | ディファード |
|---|---|---|
| 照明を解く場所 | 各物体の着色時(幾何と一体) | 画面空間の別パス(幾何と分離) |
| 照明の計算量 | 延べフラグメント × 光源数 | 可視画素 × 効く光源数 |
| オーバードローの影響 | 隠れた画素も照明計算しがち | 可視画素のみ(深度確定済み) |
| 透過(半透明) | 素直に扱える | 扱えない(別パスが必要) |
| MSAA との相性 | 良い(エッジに効く) | 悪い(サブサンプルごとに照明) |
| メモリ帯域 | 小さめ | G バッファ書き出しで大きい |
ディファードの代償 ── 透過・MSAA・帯域
強力な一方で、ディファードには構造上の弱点が三つあります。いずれも「G バッファに1画素1表面しか持てない」ことに根ざします。
第一に 透過(半透明)が扱えません。G バッファは深度テストで各画素に最前面の1表面だけを残す仕組みなので、後ろの表面と混ぜ合わせる半透明を表現できません。実務では、不透明物体はディファードで描き、半透明物体だけを別途フォワードで上乗せする ハイブリッド構成 が定番です。
第二に MSAA と相性が悪い 点です。MSAA は幾何エッジのカバレッジをサブサンプル単位で評価し、シェーディングは原則1回に抑えることで効率化します。ところがディファードでは照明が画面空間の後段に回るため、エッジを正しくアンチエイリアスするには 各サブサンプルぶんの G バッファを保持し、サブサンプルごとに照明を解き直す 必要が生じ、MSAA の「シェーディング1回」という利点が崩れます。この事情から、ディファード全盛期にはポストプロセス系のアンチエイリアス(FXAA/SMAA)や、時間方向に標本を稼ぐ TAA が主流になりました。標本化とアンチエイリアスの原理は本トピックの別記事に譲ります。
第三に メモリ帯域の圧迫 です。G バッファは画面解像度ぶんの複数レンダーターゲットを毎フレーム書き出し、ライティングパスで読み戻します。高解像度・高ビット深度ほど書き込み・読み出しの帯域が支配的になり、G バッファの厚み(チャンネル数)を削る圧縮が最適化の要になります。
初期のディファードは、点光源ごとにその到達範囲を表す球(ライトボリューム)を描き、覆った画素だけを照明する方式を取りました。効かない画素を弾ける利点はありますが、光源が空間的に重なる領域では 1画素が何度も照明パスに掛かり、重なりに比例して帯域とラスタライズ負荷が伸びます。大量の光源を密に置くと、この重なりコストが再び効いてきます。次章のタイル/クラスタ方式は、まさにこの重なりを格子単位でまとめて解消します。
Forward+ とタイル/クラスタ方式 ── 光源を空間で絞る
フォワードの柔軟さ(透過・MSAA・材質自由度)を保ちつつ、多光源のコストを抑えたい——この要求に応えるのが Forward+(タイルド・フォワード) と、それを発展させた タイルド/クラスタド・ディファード です。核となるアイデアは共通で、画面を格子に切り、各格子に実際に影響する光源だけをあらかじめリスト化しておく ことです。
タイル/クラスタ方式の基本手順:
[1] 画面を格子に分割
タイル方式 : 画面を 2D タイル(例 16×16 画素)へ
クラスタ方式 : さらに深度方向にも分割し 3D セルへ
[2] ライトカリング(光源の割り当て)
各タイル/クラスタの視錐台と各光源の到達範囲を交差判定し、
「そのセルに効く光源の一覧」を作る(コンピュートシェーダで並列に)
[3] 着色
各画素は自分が属するセルの光源リストだけをループ
→ 全光源ではなく「近傍の効く光源」だけ計算
要点は、光源のループ範囲を全体からセル単位へ縮めることです。1画素が計算する光源は画面全体の総数ではなく、その場所に届く数個〜十数個だけに減ります。これを フォワードの着色段で行えば Forward+(=透過も MSAA も維持)、ディファードのライティングパスで行えばタイルド/クラスタド・ディファード になります。
タイル方式は 2D なので実装が軽い反面、同じタイルでも手前と奥で効く光源が違うのに区別できず、深度方向に広い範囲へ光源を余分に割り当てがちです。クラスタ方式は深度方向にもセルを分ける ことでこの無駄を減らし、視錐台の遠近で光源リストを的確に絞れます。現代のエンジンで多光源を扱う際の標準的な土台がこのクラスタ方式です。
ライトカリングは「各タイル/クラスタ × 各光源」の交差判定という、独立で大量の小計算の集まりです。これはまさに GPU のデータ並列に向いた形で、コンピュートシェーダで一斉に処理できます。CPU で光源リストを作ると転送と直列処理がボトルネックになるため、視錐台・深度範囲の判定を GPU 側に置き、着色パスと同じフレーム内で完結させるのが定石です。GPU の並列実行の位置づけは /hardware-components/ を参照。
「ディファードの利点と欠点を述べよ」には、利点=照明計算量を物体×光源から物体+光源へ分離しオーバードローの照明を消す、欠点=半透明を扱えず MSAA と相性が悪く G バッファの帯域を食う、と対で答えるのが要点です。「では現代はどうするか」と問われたら、不透明はディファード(またはクラスタド・ディファード)+半透明はフォワードのハイブリッド、あるいは Forward+ で透過と MSAA を保ちつつ光源を空間カリングで絞る、と展開できると理解が伝わります。
まとめ
- 二方式の違いは 照明をどこで解くか。フォワードは各物体の着色時に一体で解き、ディファードは幾何情報を G バッファへ出してから画面空間で別パスとして解く。
- 素朴なフォワードは照明コストが 「延べフラグメント×光源数」 に伸び、オーバードローで水増しされて多光源で破綻する。
- ディファードは G バッファで可視表面を確定させ照明を1回に絞ることで計算量を 「物体+光源」へ分離するが、透過が扱えず・MSAA と相性が悪く・帯域を食う という代償を負う。
- Forward+ とタイル/クラスタ方式は画面を格子に分割し各セルに効く光源だけを割り当てることで、フォワードの柔軟さ(透過・MSAA)を保ったまま多光源を捌く。クラスタ方式は深度方向にも分割して割り当ての無駄を減らす。
- 実務では 不透明=ディファード+半透明=フォワードのハイブリッドが定番。GPU 並列実行の背景は /hardware-components/ を参照。
グラフィックス Article
前方レンダリングと遅延レンダリングを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
グラフィックス
比較で見る軸
難易度: advanced / カテゴリ: グラフィックス / タグ数: 6
導入後に効く点
ディファードは幾何情報を G バッファへ書き出してから画面空間で1回だけ照明を解き、計算量を「物体+光源」へ分離するが、透過が扱えず MSAA と相性が悪く帯域を食う。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- グラフィックス
- タグ数
- 6
判断チェックリスト
- 自社の用途が「グラフィックス / レンダリング」に近いか確認する。
- 強みである「フォワードは各物体を描くたびに影響する全光源をその場で計算し、素朴な実装では計算量が「物体×光源」に比例して多光源で破綻する。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。