web-dev-qa-db-ja.com

マルチパスレンダリング用の3Dレンダリングエンジンアーキテクチャ

ゲームにシンプルな3Dレンダリングエンジンを実装しています。DirectX11を使用しています。特定のレンダラーを含み管理する中央のレンダリングシステム(RenderingSystemクラス、シングルトン)を使用して、レンダリングエンジンのシンプルなアーキテクチャを作成しました。それぞれにレンダーキューがあり、特定のエンティティ(地形、スカイボックス、プレーヤーなど)。それはシンプルでおそらくナイーブなアーキテクチャですが、ECSや継承がゲームエンジンにとってどのように悪いのかについて多くの時間を費やした後、私は単純なbuf効率的なソリューションを選びました。

RenderingSystemクラスには、他のレンダラーのすべての作業を調整する一般的なRender()メンバー関数/メソッドがあります(必要なパラメーターを渡して、すべてのレンダラーのRenderメソッドを呼び出します)。
「集中型」レンダラーを使用すると、レンダリング状態を設定し、これが重要な場所でレンダリングの順序を決定できます(最初にSkyBoxをレンダリングし、深度テストを無効にしてから、たとえばシーンをレンダリングします)。

次に、ミラーに反射をレンダリングする必要があります。状況は次のとおりです。

enter image description here

どうすればいいのかわからない。私のRenderingSystemは、すべての異なるレンダラーのRender()関数を呼び出して、すべてのエンティティをレンダリングできます。また、テクスチャへのオフスクリーンレンダリングを実行するためのいくつかのFramebuffer/RenderToTextureメンバーがあります。

(RenderingSystemのRender()メソッドで)フレームバッファーを設定し、反射されたエンティティ/シーンを反射された視点からレンダリングします。私のカメラクラス(RenderingSystemが特定のレンダラーに渡される参照を持っている)に状態(フラグ)を設定することでこれを行うことができるため、特定のレンダラーがCamera :: GetViewMatrix()を呼び出すと、通常または反射を取得しますバリアントですが、これを行うには、シーンが反映されるPLANEをカメラに渡す必要があります(メソッドはCamara :: EnableReflection(plane)です)。問題は、平面がMirrorクラスのメンバーであり(そのためのゲッターがある)、MirrorRendererにレンダリングされるミラーのリストがあることです。

そのため、RenderingSystemで平面を取得できませんが、他のすべてのエンティティをオフスクリーンバッファーにレンダリングできるため、そこでレンダリングを実行する必要があります。

最初のレンダーパスで反射テクスチャを準備し、それをリストにあるすべての反射面/ミラーのMirrorRendererに渡すにはどうすればよいですか?

レンダラーにリストを要求し、プレーンを取得し、プレーンごとに1つのテクスチャーにレンダリングしてから、動的な配列/ベクターでレンダラーに送信することはできますが、よりエレガントなパターン/ソリューションを使用する必要がありますか?

C++を使用しています。

4
Luca

リアルタイムレンダリングパイプラインは、デザインに関してこれまでのキャリアで遭遇した最も困難なものの一部です(ただし、ユーザーが独自のレンダリングパスとシェーダーをプログラムできるようにするための要件もありました)。実装は最も難しいものではありませんでしたが、これまでに現実の領域でレンダリング設計の決定が発見/公開されていないため、私がこれまでのキャリアでバランスを取る必要があった最も困難なことの1つは、可能な設計決定の賛否両論です。時間レンダリングには、大きな目立つ欠点はありません。それらはすべて、遅延レンダリングでの透明度の難しさや、フォワードレンダリングと遅延レンダリングを組み合わせる複雑さの増大など、大きな短所を持つ傾向があります。ロゴや花のユニコーンのようなものがなくてはならず、私にぴったりとフィットするオーバーサイズのすべてのTシャツに相当するものはありませんでした。

自分を作成する場合に使用する言語、アーキテクチャ、またはパラダイムの種類に関係なく、非常に難しい設計領域に向かっているため、それを慰めの手段としてだけでなく、入門書/警告として開始したかっただけです。エンジン。エンジンがソフトシャドウ、DOF、エリアライト、間接照明、拡散反射などを処理するように精巧になると、設計の決定がますます困難になり、場合によっては、歯を一本にして、それを最大限に活用して、さまざまなアイデアを持った経験豊富な開発者からの無限の論文や記事を読みながら、どのような道を進むべきかについて圧倒されることなく、麻痺しないようにします。ユーザーと内部開発者がプロ​​グラムできるようにするために、確率的透明性とDSELをシェーディング言語として使用する据え置きパイプラインを使用しました(Inigo QuilezからのShadertoyのアプローチに多少似ていますが、これらは個別に、または製品のいずれかが存在する前に作業していました)。独自の据え置きのレンダリング/シェーディングパスは、Unrealのブループリントとは異なり、結局、視覚的なノードプログラミング言語に変わりました。今私はすでにボクセルコーントレーシングについてすべてを再考しており、AAAゲームエンジンの各世代は、主にレンダリングパイプラインの基本的な構造を変更したいという欲求によって促されたのではないかと思います。

とにかく、それを除いて、いくつかのことを大幅に再考する必要があるかもしれませんが、差し迫った設計の問題を解決するのに役立つと思われることがいくつかあります。もともといくつかの代替策を試していないわけではありませんが、すべての可能な解決策を比較する専門知識が不足していることを除いて、私は独断的ではありません。だからここに行く:

1。特にECSでは、レンダラーに何をレンダリングするかを指示しないでください。レンダラーがシーン(または、利用可能な場合は空間インデックス)をトラバースし、レンダーすることになっているものとレンダーするためにシーンで利用できるものに基づいて何をレンダーするかを考えさせます。これにより、前方反射パスでレンダリングする必要があるジオメトリ(例:平面)を渡す方法に関する懸念の一部が解消されます。プレーンがシーンに既に存在している場合(何らかの形で、マテリアルが反射としてマークされている三角形のように)、レンダラーはシーンを照会し、それらの反射プレーンを検出して、オフスクリーン反射テクスチャ/フレームバッファーにレンダリングできます。これには、レンダラーを特定のエンジンおよびシーンの担当者または少なくともインターフェイスに結合するという欠点がありますが、リアルタイムレンダラーの場合、これを最大限に活用するための実際的な必要性がしばしばあると思います。実用的な例として、アダプターを使用していても、UE 4がCryEngine 3からのシーンデータをレンダリングするとは限りません。リアルタイムレンダラーは、一般化してこの程度に分離する方法でプログラミングするには、難しすぎ、最先端(標準化が欠けている)です。

2。私は必然的に、特定のエンジンとそれらが格納する出力バッファー/テクスチャの数をハードコーディングする必要があると思います。遅延エンジンの場合、多くの開発者は次のようなgバッファー表現を使用します。

enter image description here

どちらの方法でも、G-Bufferまたは類似の同等物で必要なものを事前に決定するための途方もない事柄を簡素化します(圧縮されたG-Bufferではなく、特定のタイプのテクスチャーの配列で、 "Diffuse "テクスチャvs."スペキュラ/リフレクション "vs."シャドウ "またはエンジンに必要なものすべて)。次に、各レンダラーがそれを読み取り、必要なものを出力してから、次のレンダラーがこのデータの一部を読み取り、最終的に別々のパーツに出力して、最終的にすべてをユーザーへの最終的なフレームバッファー出力に合成できます。このように、レンダラーが常に入出力に使用できる反射(別名:鏡面反射)テクスチャデータを並べ替えて、反射パスが関連するテクスチャに出力し、後続のレンダラーとパスがそれを入力できるようにします。それらを使用して、それらを組み合わせた結果をレンダリングします。

現在の懸念の多くは、あるレンダラーから次のレンダラーへ、またはシーンから特定のレンダラーへの例外的なユースケースで、特殊なケースのデータのように渡すことであるように思われ、すべてのレンダラーがそのすべてにアクセスできる場合、それは単純で簡単になります具体的に必要な種類のデータを一意に渡さなくても、事前に必要になる可能性があるデータこれは、サウンドエンジニアの慣例に違反して、各レンダラーに異種のデータへの幅広いアクセスを提供しますが、レンダラーではニーズを事前に予測することが非常に難しいため、事前に必要となる可能性のあるすべてのものへのアクセスを許可するだけで時間を節約できる傾向があります。また、特定の目的に必要なものに読み書きできるようにします(ただし、テクスチャ/ G-バッファのみを書き込む場合)。それ以外の場合は、これらすべてのデータをパイプライン経由で渡す方法を見つけようとしているときに、レンダラーの内部と外部の両方で常に変更を加えて必要な入力を与え、必要な出力を次のパスにフィードしようとしているため、 。

6
Dragon Energy