
1993年にリリースされた最初のDOOMは、ゲーム開発とメカニズムに根本的な変更を加え、世界的なヒットとなり、 John CarmackやJohn Romeroなどの新しいアイドルを生み出しました。
23年後の今日、 id Softwareは Zenimaxに所有されており、すべての創業者はすでに会社を辞めていますが、これはidチームが優れたゲームをリリースしてすべての才能を発揮するのを止めませんでした。
新しいDOOMは、フランチャイズを完全に補完します。 新しいid Tech 6エンジンを使用します。 ジョン・カーマックが去った後、彼はリードレンダリングプログラマーとして元Crytekの開発者Tiago Sousaに取って代わられました。
従来、id Softwareは、作成から数年後にエンジンのソースコードを公開しました。これは、 興味深いリメイクや分析記事につながることがよくありました。 id Tech 6がこの伝統を継続するかどうかはわかりませんが、エンジンで使用されている好奇心の強いグラフィックテクニックを評価するために、必ずしもソースコードが必要なわけではありません。
フレームのレンダリング方法
以下の画像からシーンを分析します。このゲームでは、プレイヤーはゲームの開始時にプラエトリアの鎧を受け取った直後に 、いくつかの取りつかれたによって守られている血の巣を攻撃します。

最近のほとんどのWindowsゲームとは異なり、DOOMはDirect3DではなくOpenGLとVulkanを使用します。
Vulkanはエキサイティングな新技術であり、Baldur Karlssonはごく最近RenderDocにVulkanのサポートを追加したため、DOOMエンジンに侵入する誘惑に抵抗することは困難でした。 以下のすべての観察は、すべての設定がウルトラに設定されたGTX 980でVulkanから実行されるゲームで行われます。
メガテクスチャの更新
レンダリングの最初の段階は、 メガテクスチャの更新です 。 id Tech 5で導入されたこのテクノロジーはRAGEで使用され、現在では新しいDOOMでも使用されています。
要するに、このテクノロジーの意味は、いくつかの巨大なテクスチャ(DOOMでは16k x 8kのサイズを持っています)がビデオプロセッサのメモリに配置されるということです。 それぞれが128x128タイルのコレクションです。

16k x 8kテクスチャに保存された128 x 128ページ
これらのタイルはすべて、適切なレベルのミップテクスチャーを備えた関連テクスチャの理想的なセットである必要があります。これは、後でピクセルシェーダーが使用するシーンをレンダリングするために使用されます。
ピクセルシェーダーが「仮想テクスチャ」から読み取るときは、これらの128x128物理タイルの一部を単に読み取ります。
もちろん、プレーヤーがどこを見ているかに応じて、これらのテクスチャのセットは変わります。他の仮想テクスチャにリンクする新しいモデルが画面に表示され、新しいタイルをロードして古いタイルをアンロードする必要があります...
そのため、フレームの先頭で、DOOMは
vkCmdCopyBufferToImage
命令を使用していくつかのタイルを更新し、実際のテクスチャデータをGPUメモリに書き込みます。
メガテクスチャの詳細については、 こちらとこちらをご覧ください 。
シャドウマップの地図
影を落とす光源ごとに、一意の深度マップが作成され、8k x 8kサイズの巨大なテクスチャアトラスの1つのタイルに保存されます。 ただし、すべての深度マップがフレームごとに計算されるわけではありません。DOOMは前のフレームの結果を積極的に再利用し、更新が必要な深度マップのみを再カウントします。

深度バッファ8k x 8k(前のフレーム)

深度バッファ8k x 8k(現在のフレーム)
光源が静的で、静的オブジェクトにのみ影を落とす場合は、単純に深度マップを「そのまま」保存し、不要な計算を行わないことが賢明です。 ただし、敵がこのライトに移動する場合、深度マップを再度作成する必要があります。
デプスマップの寸法は、ソースからカメラまでの距離によって大きく異なります。 さらに、再計算された深度マップは同じアトラスタイルにある必要はありません。
DOOMは、特定の最適化を使用します。たとえば、深さマップの静的部分をキャッシュし、動的グリッドのみの投影の計算と結果のコンパイルを行います。
深度処理の予備
すべての不透明なグリッドはすでにレンダリングされており、それらの深度情報は深度マップに転送されています。 まず、これはプレイヤーの武器、次に静的なジオメトリ、最後に動的なジオメトリです。

深度マップ:20%意欲

深度マップ:40%意欲

深度マップ:60%意欲

深度マップ:80%意欲

深度マップ:100%意欲
しかし実際には、処理深度の予備パス中に、深度に関する情報だけが保存されるわけではありません。
オブジェクト( 取りつかれている 、ケーブル、およびプレイヤーの武器)を深度マップに動的にレンダリングする場合、ピクセルごとの速度も計算され、別のバッファーに書き込まれて速度マップが作成されます。 計算は、前のフレームと現在のフレームの間の各頂点の位置差を頂点シェーダーで計算することにより実行されます。

スピードマップ
速度を保存するには、2つのチャンネルだけが必要です。赤のチャンネルは水平速度を保存し、緑のチャンネルは垂直速度を保存します。
所持品はプレーヤーに向かって素早く移動し(したがって、彼は緑です)、武器はほとんど動かなくなります(黒)。
そして、この黄色の領域は何ですか(赤と緑は1に等しい)? 実際、これはデフォルトのソースバッファの色です。つまり、動的メッシュはありません。これは「静的メッシュの領域」です。
DOOMが静的メッシュの速度計算を実行しないのはなぜですか? 静的ピクセルの速度は、その深さと最後のフレームと比較したカメラの新しい状態から簡単に見つけられるためです。 グリッドごとに計算する必要はありません。
速度マップは、後でモーションブラーを追加するときに役立ちます。
クリッピングリクエスト
GPUでレンダリングするために、できるだけ少ないジオメトリオブジェクトを送信するよう努めており、これを達成するための最良の方法は、プレーヤーに表示されないすべてのグリッドを切断することです。 DOOMの不可視部分のクリッピングの大部分はUmbraミドルウェアによって実行されますが、エンジンは依然として可視領域をさらにトリミングするためにGPUへのクリッピング要求を実行します 。
GPUへのリクエストのクリッピングのポイントは何ですか? 最初のステップは、世界のいくつかのグリッドをこれらすべてのグリッドをカバーする仮想領域にグループ化し、その後、現在の深度バッファーに従ってこの領域をレンダリングするようGPUに要求することです。 ラスタピクセルのいずれも深度チェックに合格しない場合、この領域は完全に切り取られ、レンダリング中にこの領域に含まれるワールドのすべてのオブジェクトを安全に無視できることを意味します。
ただし、問題は、これらのクリッピングリクエストの結果がすぐに利用できず、GPUパイプラインがアイドル状態になり、これらのリクエストによってブロックされないようにすることです。 通常、結果の読み取りは後続のフレームに延期されるため、画面にオブジェクトが表示されないようにするには、もう少し保守的なアルゴリズムが必要です。

エリアの確認。 赤は切り取られ、緑は見えます。
不透明オブジェクトのクラスターダイレクトレンダリング
これで、すべての不透明なジオメトリとデカールがレンダリングされます。 照明情報は、浮動小数点HDRバッファーに保存されます。

25%の照明

50%の照明

75%の照明

100%照明
深度チェック機能は、不必要な計算を回避するために
EQUAL
に設定されています。これまでの深度の予備処理のおかげで、各ピクセルがどの深度値を持つべきかが正確にわかります。 デカールは、グリッドをレンダリングするときにも直接適用され、テクスチャアトラスに保存されます。
写真はすでにきれいに見えますが、ガラスや粒子などの透明な素材がまだなく、媒体の反射はまったくありません。
このパッセージについて少し説明します。EmilPersona (Emil Person)とOla Olsson (Ola Olsson)の作品に基づいたクラスターダイレクトレンダラーを使用しています。
直接レンダリングの弱点は、常に多数の光源を処理できないことでした。このタスクは、遅延シェーディングを使用する方がはるかに簡単です。
クラスターレンダラーはどのように機能しますか? まず、ビューポートはタイルに分割されます。DOOMで16 x 8の領域が作成されます。 一部のレンダラーはこれに専念し、タイルごとの光源のリストを計算します。これにより、照明計算の量を減らすことができますが、境界線の場合には特定の問題があります。
クラスターレンダリングは、この概念をより深く発展させ、2Dから3Dに移行します。2次元ビューポートの分割を停止することなく、カメラ可視性のピラミッド全体の3Dブレークダウンを実行し、Z軸に沿ってスライスを作成します。
各「ブロック」は「クラスター」と呼ばれ、「可視性のピラミッドの形のボクセル 」と呼ぶこともできます。
以下は、4 x 2ビューポートの単純な分割です。 5つの深度スライスは、可視性ピラミッドを40のクラスターに分割します。

DOOMでは、カメラのピラミッドは3072個のクラスター(サイズは16 x 8 x 24)に分割され、深度スライスはZ軸に沿って対数的に配置されます。
クラスターレンダラーの場合、一般的なアルゴリズムは次のようになります。
- 最初に、CPUは各クラスターの照明に影響する要素のリストを計算します:光源、デカール、立方体テクスチャ...
これを行うために、これらの要素はすべて「ボクセル化」されているため、それらの影響範囲はクラスターとの交差を確認できます。 データはGPUバッファーのインデックス付きリストに保存されるため、シェーダーはそれらにアクセスできます。 各クラスターには、最大256個の光源、256個のデカール、256個のキュービックテクスチャを含めることができます。
- 次に、GPUはピクセルをレンダリングします。
- 座標とピクセル深度から、それが属するクラスターに計算されます
- このクラスターのステッカー/光源のリストが読み込まれます。 この場合、オフセットを使用した間接アドレス指定とインデックス計算が使用されます(以下の図を参照)。
- コードはクラスター内のすべてのデカール/光源を通過し、それらの影響度を計算して追加します。
ライトとデカールの使用
このパッセージでピクセルシェーダーがライトとデカールのリストを取得する方法は次のとおりです。







まったく同じ方法でアクセスできるプローブのリストもあります(上の図には示されていません)。 ただし、この文章では使用されていないため、後で説明します。
クラスターの要素のリストを事前に作成することでCPUに影響を与えるコストは、パイプラインでのGPUでのレンダリング計算の複雑さを大幅に削減することで報われます。
最近、クラスターダイレクトレンダリングに注意が払われました。単純なダイレクトレンダリングよりも多くの光源を処理できる優れた機能を備えています。 また、複数のG-bufferからの書き込みと読み取りが必要な遅延シェーディングよりも高速に動作します 。
ただし、私は何も言及しませんでした。この文章は、書き込みバッファーに1つの書き込み操作を渡すだけではありません。 MRTを使用して実行された場合、2つのシンGバッファーが作成されます。

法線マップ

反射マップ
法線マップは浮動小数点R16G16形式で保存され、反射マップはR8G8B8A8に保存されます。アルファチャネルには平滑化係数が含まれます。 そのため、DOOMは、直接レンダリングと遅延レンダリングの組み合わせとハイブリッドアプローチを思慮深く使用しています。 これらの新しいGバッファーは、反射などの特別な効果を追加するときに役立ちます。
もう1つ見逃しました。同時に、メガテクスチャシステム用に160x120のフィードバックバッファが作成されます。 これには、テクスチャとそのミップテクスチャリングについて報告するストリーミングシステムの情報が含まれています。
メガテクスチャエンジンは、フィードバックの原則に基づいて動作します。レンダリングを通過した後、テクスチャがないことが報告された場合、エンジンはそれらをロードします。
GPUパーティクル
次に、 計算シェーダーが起動され、パーティクルシミュレーション(位置、速度、寿命)が更新されます。
パーティクルの現在の状態、および法線と深度のバッファ(衝突を検出するため)を読み取り、シミュレーションステージを再現し、新しい状態をバッファに保存します。
スクリーンスペースアンビエントライト(SSAO)
この時点で、 SSAOマップが生成されます。
狭い縫い目、折り目などの周りの色を暗くするように設計されています
また、 反射クリッピングを適用して、クリッピングされたグリッドで発生する明るいアーチファクトを回避するためにも使用されます。
マップは、深度バッファー、法線マップ、および反射からデータを読み取るピクセルシェーダーで元の解像度の半分で計算されます。
最初の結果はかなりうるさいです。

SSAOマップ
スクリーン反射
これで、ピクセルシェーダーがSSRカードを作成します。 画面上の情報を使用して、レイトレーシングによって反射を処理し、光線がビューポート内の各ピクセルで反射し、光線が当たったピクセルの色を読み取ります。
![]() 深さ | ![]() 普通 | ![]() リフレクション | ![]() 前のフレーム |
![]() | |||
![]() SSRカード |
SSRは、シーンの動的な反射をリアルタイムで作成し、不変の負荷を作成するための優れた技術であり、それほど高価ではありません。 没入感とリアリズムを大幅に向上させます。
しかし、彼女は自分のアーティファクトを持っています。なぜなら彼女はスクリーン空間でのみ働き、「グローバルな」情報を欠いているからです。 たとえば、シーンに美しい反射を見ることができますが、見下ろすと反射の量が減り、足を見るとほとんど反射が見られません。 SSRはDOOMにうまく統合されており、画質が向上しているように見えますが、同時に目立たないため、具体的に従わないとSSRの消失に気付かないでしょう。
静的立方体テクスチャ反射
前のパスのすべての動的反射(およびその制限)を計算したら、 IBLを使用して静的反射を作成します。
この手法は、生成された128 x 128の立方体テクスチャの使用に基づいています。これは、マップ上のさまざまな場所の環境の照明に関する情報です(「環境プローブ」とも呼ばれます)。 可視性のピラミッドをクラスター化する段階でデカール付きの光源と同様に、プローブはクラスターごとにインデックスが付けられます。
すべてのキュービックレベルテクスチャは配列に格納されます。 数十個ありますが、シーンに主に貢献しているのは5個(この部屋のキュービックテクスチャ)です。
![]() | ![]() | ![]() | ![]() | ![]() |

静的反射マップ
カードブレンディング
この時点で、計算シェーダーは以前に生成されたすべてのカードを結合します。 深度マップと反射マップを読み取り、直接通過照明を混合します。
- SSAO情報付き
- 問題のピクセルが利用可能になったときに、そのピクセルのSSRを使用
- SSR情報が利用できない場合、静的な反射マップデータが代替として使用されます
- 霧の効果も計算されます

ミキシングとフォグ:アップ

混合と霧:後

霧-霧、プローブ反射-プローブ反射
粒子照明
シーンには煙の粒子があり、各スプライトの照明が計算されます。 各スプライトは、世界の空間にあるかのようにレンダリングされます。その位置から、光源とそれに対応するシャドウマップのリストを取得し、その後、四辺形(パーティクル)の照明が計算されます。 次に、結果が4kアトラスタイルに保存されます。 タイルは、パーティクルからカメラまでの距離、品質設定などに応じて、さまざまなサイズにすることができます。 アトラスは、単一解像度のスプライトの領域を強調しています。これは、64 x 64スプライトの外観です。

粒子照明アトラス
この低解像度では、 照明情報のみが保存されます。 後で、パーティクルが実際に描画されると、テクスチャが最大解像度で使用され、照明四角形のスケールが大きくなり、テクスチャと混ざります。
ここで、DOOMはパーティクルライティングの計算をゲームのメインレンダリングから分離します。プレイする解像度(720p、1080p、4k ...)に関係なく、パーティクルライティングは常に固定サイズの小さなタイルに計算され、保存されます。
ズームアウトしてぼかし
シーンのサイズは数倍に縮小され、40ピクセルになります。 最小のズームレベルは、垂直パスと水平パスを別々に使用してぼかします(「ぼかしチェーン」が作成されます)。

なぜこんなに早くぼかしますか 通常、このプロセスは後処理中に最後に行われ、明るい領域にブルーム効果を作成します。
しかし、これらのさまざまなレベルのぼかしはすべて、メガネの屈折をレンダリングするための次の文章で役立ちます。
透明なオブジェクト
すべての透明なオブジェクト(ガラス、パーティクル)はシーンの上部にレンダリングされます:

透明なオブジェクト:to

透明オブジェクト:後
DOOMでは、特に霜や汚れで覆われたガラスが非常に美しくレンダリングされます。デカールはガラスの一部のみに影響し、屈折を多少ぼやけさせます。 ピクセルシェーダーは、「ぼかし」屈折係数を計算し、「ぼかしチェーン」セットからぼかし係数に最も近い2つのカードを選択します。 彼はこれらの2枚のカードを読み取り、2つの値を線形補間して、屈折ブラーの最終色を近似します。 このプロセスのおかげで、メガネはさまざまなレベルのぼかしでピクセル単位の美しい屈折を作成できます。
歪みマップ

歪みマップ
非常に高温の領域では、画像に熱変形が生じる可能性があります。 私たちのシーンでは、血の巣が画像を少し歪ませています。
低解像度の歪みマップを作成するために、歪みは深度バッファを基準にしてレンダリングされます。
赤と緑のチャンネルは、水平軸と垂直軸に沿った歪み値を表します。 青いチャネルには、適用されたぼかしの量が含まれています。
実際の歪み効果は、後処理のように後で適用され、歪みマップを使用して必要なピクセルを移動します。
このシーンでは、歪みは非常に小さく、ほとんど見えません。
ユーザーインターフェース

UIは、 LDR形式で保存された事前乗算アルファモードで別のレンダリングバッファーにレンダリングされます。
完成したフレームの上に直接描画するのではなく、UI全体を個別のバッファーに保存する利点は、ゲームがフィルタリング/後処理、たとえば色収差や光学歪みをすべてのUIウィジェットに1回のパスで適用できることです。
レンダリングでは、バッチ処理手法を使用せず、約120の描画呼び出しでインターフェース要素を1つずつ描画します。
後続のパスでは、UIバッファーがゲーム画像の上にミックスされ、最終結果が作成されます。
一時的なアンチエイリアス(TAA)とモーションブラー
TAAとモーションブラーは、前のフレームの速度マップとレンダリング結果を使用して適用されます。
フラグメントを追跡できるため、ピクセルシェーダーは現在の処理済みピクセルが前のフレームのどこにあったかを認識します。1秒おきにレンダリングすると、グリッドの投影が0.5ピクセルだけわずかにシフトします。これにより、サブピクセルの歪みのアーティファクトが除去されます。

TAAとモーションブラーの前:TAAとモーションブラーの前

:後
結果は非常に良好です。グリッドが滑らかになるだけでなく、反射の歪み(フレーム内に別の明るいピクセルが表示される)も減少します。品質は、FXAA後処理方法を使用して達成できるものよりもはるかに優れています。
シーンの明るさ
この段階で、シーンの平均輝度が計算されます。これは、後でトーン圧縮のために送信されるパラメーターの1つです。
HDRライティングバッファーは、解像度から半分ずつ周期的に減少し、2 x 2のテクスチャになります。各反復で、ピクセルカラー値は、大きなマップからの4つの「親」ピクセルの輝度の平均として計算されます。
ブルーム

ブルーム
画像の最も暗い領域をミュートするために、輝度フィルターが適用されます。次に、輝度フィルターを使用した結果は、上記と同様の方法で周期的に縮小され、ぼやけます。
レイヤーは垂直パスと水平パスに分割されたガウスぼかしを使用してぼかしられ、ピクセルシェーダーは1つの軸に沿って加重平均値を計算します。
次に、ぼやけたレイヤーを組み合わせてブルームエフェクトを作成します。ブルームエフェクトは、元の解像度の4倍のHDRテクスチャです。
最終後処理
このステップ全体は、単一のピクセルシェーダーで実行されます。
- 熱変形は、歪みマップデータを使用して適用されます
- ブルームテクスチャがHDRライティングバッファーの上に追加されます
- ケラレ、汚れ/グレアなどの効果を適用する
- 平均輝度は、2x2輝度マップをサンプリングして計算され、追加の露出パラメーター、トーン圧縮、グレーディングが使用されます。

トーン圧縮:トーン圧縮前:

トーン圧縮後
は、広範囲の明るさで変化する色を含むHDRイルミネーションバッファーを取得し、8ビット/コンポーネント(LDR)の色に変換して、フレームをモニターに表示できるようにします。
映画のトーンマッピング演算子式に基づいて
(x(Ax+BC)+DE) / (x(Ax+B)+DF) - (E/F)
、この色調圧縮未知2も適用されるGTA V。
また、シーンの全体的な赤い色合いは色補正によって取得されることに注意してください。
UIとフィルムグレイン
そして最後に、UIはゲームフレームの上に配置されます。同時に、わずかなフィルムグレイン効果が追加されます。

UIと木目:

UIと木目:後
フレームの処理が完了すると、表示のためにモニターに送信する準備が整います。多くの計算が行われましたが、すべて16ミリ秒未満で発生しました。
DOOMは、以前のフレームで計算された古いデータを賢く使用するため、ゲームの高速で高品質の画像を作成できます。合計で、1331の描画呼び出し、132のテクスチャ、および50のレンダリングバッファーがありました。
ボーナス情報
メガネの詳細
ガラスのレンダリング結果は非常に良好です。ただし、上記で検討したかなり簡単な方法で達成されました。
- « » // ; .

:

:
研究コンタクトフレームを見ることができない被写界深度を:ので、次のシーンでの外観は、その適用前と後に聞かせて、

被写界深度:へ

被写界深度:の後に
すべてのゲームが正常に被写界深度は適用されない:ナイーブなアプローチは、多くの場合、1つに浸食のガウスぼかしと実装を使用することですピクセル深度に応じて渡します。このアプローチは単純で経済的ですが、いくつかの問題があります。
- ガウスぼかしはブルーム効果に適していますが、ボケを正しく作成しません。明るいピクセルの光がディスクまたは六角形全体に広がるように平らな中心が必要です。ガウスぼかしでは、美しいボケ形状を作成できません。
- ピクセルシェーダーの1ステップで被写界深度を適用すると、境界でアーティファクトが発生しやすくなります。
DOOMでは、被写界深度が正しく適用されます。私の経験では、最良の結果が得られるアプローチの1つが選択されました。被写界深度の大きい画像と小さい画像が作成されます。ピクセルの選択は、被写界深度と被写界深度のパラメータに応じて実行されます。
- 浅い深さの画像は非常に不鮮明になり、背後のピクセルに「広がる」ほど良くなります。
- 奥行きの大きい画像もぼやけますが、焦点の合った領域/被写界深度の浅い部分からピクセルを読み取らないため、前景のオブジェクトが誤って背景に「広がる」問題を回避できます。
ボケぼかしを作成するために、DOOMは半分の解像度で動作し、64のテクスチャオーバーレイで円形のぼかしを実行します。各フラグメントは同じ重みを持つため、ガウスぼかしとは異なり、明るさは実際に広がります。
円の直径は、ピクセルの散乱スポットの値に応じて、ピクセルごとに異なる場合があります。
その後、ボケは16オーバーレイのブラーでさらに広がりますが、今回は加重平均値は計算されませんが、サンプルの値は単純に累積され、隣接するオーバーレイの最大値が保存されます。これは最初のブラーを拡大するだけでなく、最初のパスの小さなアーティファクト(サンプリングのギャップ)を排除します。アルゴリズムの最後の部分はから取られるマッキントッシュの作業(マッキントッシュ)。
複数のパスを反復処理するこの手法を使用すると、非常に美しい大きなぼかしを得ることができますが、同時にパフォーマンスの面では効果的です。最終的な円形のぼかしの半径が大きいため、ピクセルあたりのテクスチャオーバーレイの数はまだ非常に少ないです。

大きな被写界深度

大きな被写界深度とは、1ブラー

大きな被写界深度及び1,2ブラー
フィールドの深さの最終的な効果を作成するために、アルファチャンネルを混合して、元のシーンに重畳されているフィールドの大浅い深さの最後の画像を。このパスは、モーションブラーを適用する直前に実行されます。
追加ソース
idTech 6テクノロジーをさらに掘り下げたい場合は、幸いなことに、このトピックに関する多くの講義や出版物があります。
- The devil is in the details: idTech 666 (Siggraph 2016), Tiago Sousa and Jean Geffroy
- Tech Interview: Doom , Digital Foundry
- id Software Tech Interview , DSOGaming
- QuakeCon 2016: Doom Uncapped – Part1 Part 2
- Doom: The definitive interview , VentureBeat
- Graphics Gems CryEngine 3 (Siggraph 2013), idTech 6.