フラグメントシェーダーに任意の数のライトの位置(および色)を渡し、シェーダーでそれらをループする方法はありますか?
そうでない場合、複数のライトをどのようにシミュレートするのですか?たとえば、拡散指向性ライティングに関しては、シェーダーのライトウェイトの合計を渡すことはできません。
フラグメントシェーダーに任意の数のライトの位置(および色)を渡し、シェーダーでそれらをループする方法はありますか?
そうでない場合、複数のライトをどのようにシミュレートするのですか?たとえば、拡散指向性ライティングに関しては、シェーダーのライトウェイトの合計を渡すことはできません。
回答:
通常、これに対処するには2つの方法があります。最近では、これらはフォワードレンダリングと遅延レンダリングと呼ばれています。以下で説明するこれら2つのバリエーションが1つあります。
各オブジェクトに影響を与えるライトごとに1回ずつレンダリングします。これには、環境光が含まれます。加算ブレンドモード(glBlendFunc(GL_ONE, GL_ONE)
)を使用するため、各ライトの寄与は互いに加算されます。異なるライトの寄与は加算的であるため、フレームバッファーは最終的に値を取得します
浮動小数点フレームバッファーにレンダリングすることにより、HDRを取得できます。次に、シーンを最後に通過して、HDRライティング値を可視範囲に縮小します。これは、ブルームやその他のポストエフェクトを実装する場所でもあります。
このテクニックの一般的なパフォーマンス強化(シーンに多くのオブジェクトがある場合)は、カラーフレームバッファーに何も描画せずにすべてのオブジェクトをレンダリングする「プリパス」を使用することです(glColorMask
カラー書き込みをオフにするために使用)。これは、深度バッファを埋めるだけです。このように、別のオブジェクトの背後にあるオブジェクトをレンダリングすると、GPUはそれらのフラグメントをすばやくスキップできます。頂点シェーダーを実行する必要がありますが、通常はより高価なフラグメントシェーダーの計算をスキップできます。
これはコーディングが簡単で、視覚化が簡単です。また、一部のハードウェア(主にモバイルおよび組み込みGPU)では、他のハードウェアよりも効率的です。しかし、ハイエンドのハードウェアでは、多くの場合、多くの照明のあるシーンで代替品が優先されます。
遅延レンダリングはもう少し複雑です。
サーフェス上のポイントのライトを計算するために使用する照明方程式は、次のサーフェスパラメータを使用します。
フォワードレンダリングでは、これらのパラメーターは、頂点シェーダーから直接渡されるか、テクスチャから取得されるか(通常、頂点シェーダーから渡されるテクスチャ座標を介して)、またはその他のパラメーター。拡散色は、頂点ごとの色とテクスチャを組み合わせたり、複数のテクスチャを組み合わせたりすることで計算できます。
遅延レンダリングでは、これをすべて明示的にします。最初のパスでは、すべてのオブジェクトをレンダリングします。しかし、色はレンダリングしません。代わりに、表面パラメーターをレンダリングします。そのため、画面上の各ピクセルには表面パラメーターのセットがあります。これは、オフスクリーンテクスチャへのレンダリングを介して行われます。1つのテクスチャは、拡散色をRGBとして保存し、場合によっては鏡面光沢をアルファとして保存します。別のテクスチャは鏡面反射色を保存します。3番目は法線を保存します。等々。
通常、位置は保存されません。代わりに、2番目のパスで複雑になりすぎてここに入ることはできません。つまり、入力として深度バッファとスクリーン空間フラグメント位置を使用して、サーフェス上のポイントのカメラ空間位置を把握します。
したがって、これらのテクスチャは、シーン内のすべての表示ピクセルの基本的にすべての表面情報を保持しているので、フルスクリーンクワッドのレンダリングを開始します。各ライトは、フルスクリーンのクワッドレンダリングを取得します。表面パラメータテクスチャからサンプリング(および位置を再構成)し、それらを使用してその光の寄与を計算します。これがglBlendFunc(GL_ONE, GL_ONE)
画像に(再び)追加されます。ライトがなくなるまでこれを続けます。
HDRは再び後処理ステップです。
遅延レンダリングの最大の欠点は、アンチエイリアスです。アンチエイリアスを適切に行うには、もう少し作業が必要です。
GPUのメモリ帯域幅が大きい場合の最大の利点は、パフォーマンスです。実際のジオメトリを1回だけレンダリングします(シャドウマッピングを行う場合は、シャドウのあるライトごとに1 + 1)。私たちは、決してこの後に表示されていない隠されたピクセルまたは幾何学上の任意の時間を費やしていません。照明の通過時間はすべて、実際に見えるものに費やされます。
GPUに多くのメモリ帯域幅がない場合、ライトパスが実際に傷つき始めます。スクリーンピクセルごとに3〜5個のテクスチャを取得するのは楽しいことではありません。
これは、遅延レンダリングの一種のバリエーションであり、興味深いトレードオフがあります。
遅延レンダリングの場合と同様に、表面パラメーターを一連のバッファーにレンダリングします。ただし、表面データは省略されています。今回注目する表面データは、深度バッファ値(位置の再構成用)、法線、鏡面光沢です。
次に、各ライトについて、ライティング結果のみを計算します。表面の色との乗算はありません。ドット(N、L)と鏡面反射項だけで、表面の色はまったくありません。鏡面反射と拡散反射は別々のバッファに保管する必要があります。各ライトの鏡面反射と拡散反射の項は、2つのバッファ内で合計されます。
次に、全体の鏡面反射と拡散照明の計算を使用してジオメトリを再レンダリングし、表面の色と最終的に組み合わせて、全体の反射率を生成します。
ここでの利点は、(少なくとも、遅延よりも簡単に)マルチサンプリングが返されることです。オブジェクトごとのレンダリングは、フォワードレンダリングよりも少なくなります。しかし、これが提供することを延期する主なことは、異なる表面に対して異なる照明方程式を用意するのが簡単なことです。
遅延レンダリングでは、通常、ライトごとに同じシェーダーでシーン全体を描画します。したがって、すべてのオブジェクトは同じマテリアルパラメータを使用する必要があります。ライトプリパスを使用すると、各オブジェクトに異なるシェーダーを与えることができるため、最終的なライティングステップを単独で行うことができます。
これは、前方レンダリングの場合ほど自由度がありません。ただし、テクスチャの帯域幅に余裕がある場合は、さらに高速です。
invariant
他の場合にそれを保証するキーワードがあります)。
遅延レンダリングまたはプリパス照明を使用する必要があります。古い固定機能パイプラインの一部(読み取り:シェーダーなし)は、最大16または24のライトをサポートしていましたが、それだけです。遅延レンダリングにより、照明の制限がなくなります。しかし、はるかに複雑なレンダリングシステムが必要です。
どうやら、WebGLはMRTをサポートしているようです。これは、あらゆる形式の遅延レンダリングに絶対に必要なものです。私はそれがどれだけもっともらしいか分からない。
または、すぐにレンダリングを延期したUnity 5を調査することもできます。
これに対処するもう1つの簡単な方法は、単純にライトに優先順位を付け(おそらく、プレーヤーからの距離とカメラの錐台にいるかどうかに基づいて)、上位8のみを有効にすることです。出力の品質(たとえば、Far Cry 1)。
事前に計算されたライトマップを調べることもできます。Quake 1のようなゲームは、これらから多くのマイレージを得ました-そして、それらは非常に小さい場合があります(双線形フィルタリングは、引き伸ばされたライトマップを非常にうまく和らげます)。残念ながら、事前に計算除外100%のダイナミックライトの概念が、それは本当に見てい偉大を。これを8つのライトの制限と組み合わせることができます。たとえば、ロケットなどのみが実際のライトを持ちますが、壁などのライトはライトマップになります。
サイドノート:シェーダーでそれらをループしたくないですか?パフォーマンスに別れを告げます。GPUはCPU ではなく、たとえばJavaScriptと同じように動作するようには設計されていません。レンダリングする各ピクセルは(上書きされたとしても)ループを実行する必要があることに注意してください。したがって、1920x1080で実行し、16回実行する単純なループを実行すると、ループ内ですべてを効果的に33177600回実行します。グラフィックカードはこれらのフラグメントの多くを並行して実行しますが、これらのループは依然として古いハードウェアを消費します。
n個のライト(nは4または8のような小さな数字)をサポートするピクセルシェーダーを使用し、シーンを複数回再描画し、毎回新しいライトのバッチを渡し、加算ブレンドを使用してそれらをすべて結合します。
それが基本的な考え方です。もちろん、これを適切なサイズのシーンに十分に高速化するには、多くの最適化が必要です。すべてのライトを描画するのではなく、目に見えるライトのみを描画しないでください(錐台とオクルージョンカリング)。実際に全体を再描画しないでくださいパスごとにシーン、そのパスのライトの範囲内にあるオブジェクトだけをます。異なる数のライト(1、2、3、...)をサポートするシェーダーの複数のバージョンがあるので、必要以上のライトを評価するのに時間を浪費しません。
他の回答で述べたように遅延レンダリングは、多くの小さなライトがある場合に適していますが、唯一の方法ではありません。