フラグメントシェーダーで任意の数のライトを使用する方法はありますか?


19

フラグメントシェーダーに任意の数のライトの位置(および色)を渡し、シェーダーでそれらをループする方法はありますか?

そうでない場合、複数のライトをどのようにシミュレートするのですか?たとえば、拡散指向性ライティングに関しては、シェーダーのライトウェイトの合計を渡すことはできません。


私はWebGLで作業したことはありませんが、OpenGLでは最大8つの光源があります。私の意見では、それ以上を渡したい場合は、たとえば均一変数を使用する必要があります。
zacharmarz

古い方法は、すべてのライトを常に渡すことでした。未使用のライトは0の輝度に設定されていたため、シーンには影響しません。おそらくあまり使用されていません;-)
パトリックヒューズ

7
このようなものをGoogleで使用する場合は、「WebGL」という用語を使用しないでください。これらの問題に対処することを考えても、人々にとって技術は若すぎます。テイクこの検索を例えば、「私はラッキー感じている」働いているだろう。WebGLの問題は、まったく同じOpenGLの問題にうまく変換されることを忘れないでください。
ジョナサンディキンソン

フォワードレンダリングでの8個以上のライトの場合、通常はマルチパスシェーダーを使用し、各パスに8個のライトの異なるグループを追加のブレンドを使用して処理します。
ChrisC

回答:


29

通常、これに対処するには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つのバッファ内で合計されます。

次に、全体の鏡面反射と拡散照明の計算を使用してジオメトリを再レンダリングし、表面の色と最終的に組み合わせて、全体の反射率を生成します。

ここでの利点は、(少なくとも、遅延よりも簡単に)マルチサンプリングが返されることです。オブジェクトごとのレンダリングは、フォワードレンダリングよりも少なくなります。しかし、これが提供することを延期する主なことは、異なる表面に対して異なる照明方程式を用意するのが簡単なことです。

遅延レンダリングでは、通常、ライトごとに同じシェーダーでシーン全体を描画します。したがって、すべてのオブジェクトは同じマテリアルパラメータを使用する必要があります。ライトプリパスを使用すると、各オブジェクトに異なるシェーダーを与えることができるため、最終的なライティングステップを単独で行うことができます。

これは、前方レンダリングの場合ほど自由度がありません。ただし、テクスチャの帯域幅に余裕がある場合は、さらに高速です。


-1:LPP / PPLに言及していない。-1遅延:レンダリングはDX9.0ハードウェア(私の「ビジネス」ラップトップでも)ですぐに勝ちます-これは2009年頃のベースライン要件です。DX8.0(遅延/ LPPを実行できない場合) Deferred / LPPはデフォルトです。最後に、「大量のメモリ帯域幅」は正気ではありません。通常、PCI-X x4をまだ飽和させていません。さらに、LPPはメモリ帯域幅を大幅に削減しています。最後に、コメントの場合は-1。このようなループはOKですか?これらのループは1フレームあたり2073600回発生していることを知っていますか?グラフィックカードの偏見があっても、それは悪いことです。
ジョナサンディキンソン

1
@JonathanDickinson彼のポイントは、遅延/ライトプリパスのメモリ帯域幅が、通常、フォワードレンダリングのメモリ帯域幅よりも数倍大きいことだと思います。これは、遅延アプローチを無効にしません。それを選択する際に考慮すべきことです。ところで:遅延バッファはビデオメモリ内にある必要があるため、PCI-X帯域幅は無関係です。重要なのはGPUの内部帯域幅です。展開されたループなどの長いピクセルシェーダーは、有用な作業を行っているかどうかを気にする必要はありません。そして、z-buffer prepassトリックには何の問題もありません。正常に動作します。
ネイサンリード

3
@JonathanDickinson:これはWebGLについて話しているので、「シェーダーモデル」の議論は無関係です。また、どの種類のレンダリングを使用するかは「宗教的なトピック」ではありません。実行しているハードウェアの問題です。「ビデオメモリ」が単なる通常のCPU RAMである組み込みGPUは、遅延レンダリングでは非常にうまく機能しません。モバイルタイルベースのレンダラーではさらに悪化します。遅延レンダリングは、ハードウェアに関係なく「瞬間的な勝ち」ではありません。他のハードウェアと同様に、トレードオフがあります。
ニコルボーラス

2
@JonathanDickinson:「また、z-buffer pre-passトリックを使用すると、描画されるオブジェクトとのzファイティングを排除するのに苦労します。」それは全くナンセンスです。同じオブジェクトを同じ変換マトリックスと同じ頂点シェーダーでレンダリングしています。マルチパスレンダリングはVoodoo 1日で行われました。これは解決された問題です。照明を累積しても、それを変えることはありません。
ニコルボーラス

8
@JonathanDickinson:しかし、私たちはワイヤーフレームのレンダリングについて話しているのではありませんか?以前と同じ三角形をレンダリングすることについて話している。OpenGL は、レンダリングされる同じオブジェクトの不変性を保証します(もちろん、同じ頂点シェーダーを使用している限り、そしてそれでも、invariant他の場合にそれを保証するキーワードがあります)。
ニコルボーラス

4

遅延レンダリングまたはプリパス照明を使用する必要があります。古い固定機能パイプラインの一部(読み取り:シェーダーなし)は、最大16または24のライトをサポートしていましたが、それだけです。遅延レンダリングにより、照明の制限がなくなります。しかし、はるかに複雑なレンダリングシステムが必要です。

どうやら、WebGLはMRTをサポートしているようです。これは、あらゆる形式の遅延レンダリングに絶対に必要なものです。私はそれがどれだけもっともらしいか分からない。

または、すぐにレンダリングを延期したUnity 5を調査することもできます。

これに対処するもう1つの簡単な方法は、単純にライトに優先順位を付け(おそらく、プレーヤーからの距離とカメラの錐台にいるかどうかに基づいて)、上位8のみを有効にすることです。出力の品質(たとえば、Far Cry 1)。

事前に計算されたライトマップを調べることもできます。Quake 1のようなゲームは、これらから多くのマイレージを得ました-そして、それらは非常に小さい場合があります(双線形フィルタリングは、引き伸ばされたライトマップを非常にうまく和らげます)。残念ながら、事前に計算除外100%のダイナミックライトの概念が、それは本当に見てい偉大を。これを8つのライトの制限と組み合わせることができます。たとえば、ロケットなどのみが実際のライトを持ちますが、壁などのライトはライトマップになります。

サイドノート:シェーダーでそれらをループしたくないですか?パフォーマンスに別れを告げます。GPUはCPU ではなく、たとえばJavaScriptと同じように動作するようには設計されていません。レンダリングする各ピクセルは(上書きされたとしても)ループを実行する必要があることに注意してください。したがって、1920x1080で実行し、16回実行する単純なループを実行すると、ループ内ですべてを効果的に33177600回実行します。グラフィックカードはこれらのフラグメントの多くを並行して実行しますが、これらのループは依然として古いハードウェアを消費します。


-1:「遅延レンダリングを使用する必要があります」これはまったく当てはまりません。遅延レンダリングは確かにそれを行う方法ですが、唯一の方法ではありません。また、ループは、特に均一な値に基づいている場合(つまり、各フラグメントに異なるループ長がない場合)、パフォーマンスの点でそれほど悪くはありません。
ニコルボーラス

1
4番目の段落をお読みください。
ジョナサンディキンソン

2

n個のライト(nは4または8のような小さな数字)をサポートするピクセルシェーダーを使用し、シーンを複数回再描画し、毎回新しいライトのバッチを渡し、加算ブレンドを使用してそれらをすべて結合します。

それが基本的な考え方です。もちろん、これを適切なサイズのシーンに十分に高速化するには、多くの最適化が必要です。すべてのライトを描画するのではなく、目に見えるライトのみを描画しないでください(錐台とオクルージョンカリング)。実際に全体を再描画しないでくださいパスごとにシーン、そのパスのライトの範囲内にあるオブジェクトだけをます。異なる数のライト(1、2、3、...)をサポートするシェーダーの複数のバージョンがあるので、必要以上のライトを評価するのに時間を浪費しません。

他の回答で述べたように遅延レンダリングは、多くの小さなライトがある場合に適していますが、唯一の方法ではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.