web-dev-qa-db-ja.com

WebGLおよびThreeJSでのエリア照明の改善

私はこのデモに似たWebGLのエリアライティングの実装に取り​​組んできました。

http://threejs.org/examples/webgldeferred_arealights.html

上記のthree.jsでの実装は、gamedev.net上のArKano22の作業から移植されました。

http://www.gamedev.net/topic/552315-glsl-area-light-implementation/

これらのソリューションは非常に印象的ですが、どちらにもいくつかの制限があります。 ArKano22の元の実装の主な問題は、拡散項の計算が表面法線を考慮していないことです。

この問題を解決するために、私はこのソリューションを数週間拡張しており、redPlantによる改善に取り組んでいます。現在、私は通常の計算をソリューションに組み込んでいますが、結果にも欠陥があります。

これが私の現在の実装のプレビューです:

area lighting teaser

前書き

各フラグメントの拡散項を計算する手順は次のとおりです。

  1. エリアライトが置かれている平面に頂点を投影し、投影されたベクトルがライトの法線/方向と一致するようにします。
  2. 投影ベクトルをライトの法線と比較して、頂点がエリアライトプレーンの正しい側にあることを確認します。
  3. ライトの中心/位置からの平面上のこの投影された点の2Dオフセットを計算します。
  4. この2Dオフセットベクトルをクランプして、ライトの領域(幅と高さで定義)の内側に配置します。
  5. 投影およびクランプされた2Dポイントの3Dワールド位置を導出します。これは、頂点のエリアライト上の最近傍点です。
  6. 頂点から最も近い点へのベクトル(正規化)と頂点法線の間の内積をとることで、ポイントライトの場合と同じように通常の拡散計算を実行します。

問題

このソリューションの問題は、照明の計算が最近傍点から行われ、照明を照らしている可能性があるライトサーフェス上の他のポイントを考慮しないことです。さらに断片化します。その理由を説明してみましょう…

次の図を検討してください。

problematic area lighting situation

エリアライトは、サーフェスに対して垂直であり、サーフェスと交差しています。サーフェス上の各フラグメントは、常に、サーフェスとライトが交差するエリアライト上の最近点を返します。サーフェス法線と頂点から光へのベクトルは常に垂直であるため、それらの間の内積はゼロです。その後、表面に光が広がっていても、拡散の寄与の計算はゼロです。

可能な解決策

エリアライトの最近傍点から光を計算するのではなく、最大のドットを生成するエリアライト上の点から計算することを提案します頂点から光へのベクトル(正規化)と頂点法線の間の積。上の図では、これは青い点ではなく、紫色の点になります。

助けて!

そして、これが私があなたの助けを必要とするところです。私の頭の中では、この点がどのように導き出されるかについてかなり良い考えがありますが、解決策に到達するための数学的な能力はありません。

現在、フラグメントシェーダーで次の情報を利用できます。

  • 頂点位置
  • 頂点法線(単位ベクトル)
  • ライトの位置、幅、高さ
  • 軽い法線(単位ベクトル)
  • ライトライト(単位ベクトル)
  • 点灯(単位ベクトル)
  • 頂点からライトプレーン(3D)への投影点
  • ライトの中心からの投影点オフセット(2D)
  • クランプオフセット(2D)
  • このクランプされたオフセットのワールド位置–最近点(3D)

このすべての情報を視覚的なコンテキストに入れるために、この図を作成しました(それが役立つことを願っています)。

available lighting information

私の提案をテストするには、エリアライトにキャストポイントが必要です。これは、赤い点で表されるため、頂点間でドット積を実行できます。 -to-casting-point(ノーマライズ済み)および頂点ノーマル。繰り返しますが、これは可能な最大の貢献値をもたらすはずです。

更新!!!

現在実装している数学を視覚化するインタラクティブなスケッチをCodePenに作成しました。

http://codepen.io/wagerfield/pen/ywqCp

codepen

注目すべき関連コードは、行318です。

castingPoint.locationTHREE.Vector3のインスタンスであり、不足しているパズルのピースです。また、スケッチの左下に2つの値があることに注意してください。これらの値は動的に更新され、関連するベクトル間の内積を表示します。

解決策には、頂点の法線の方向に整列し、ライトの平面に垂直な別の疑似平面が必要になると想像しますが、間違っている可能性があります。

59
wagerfield

良いニュースは、解決策があることです。しかしまず悪いニュース。

内積を最大化するポイントを使用するアプローチは根本的に欠陥があり、物理的に妥当ではありません。

上記の最初の図で、エリアライトが左半分のみで構成されていると仮定します。

「紫色」の点(左半分のドット積を最大化する点)は、両方の半分のドット積を最大化する点と同じです。

したがって、提案されたソリューションを使用する場合、エリアライトの左半分はライト全体と同じ放射を放出すると結論付けます。明らかに、それは不可能です。

エリアライトが特定のポイントに投じる光の総量を計算するためのソリューションはかなり複雑ですが、参考のために、1994年の論文で説明を見つけることができます(部分的に遮蔽された多面体光源の放射ヤコビアン) ここ

図1セクションのいくつかの段落をご覧になることをお勧めしますセクション1.2-次に停止します。 :-)

簡単にするために、遅延型ではなく、three.js WebGLRendererを使用してソリューションを実装する非常に単純なシェーダーをコーディングしました。

編集:ここに更新されたフィドルがあります: http://jsfiddle.net/hh74z2ft/1/

enter image description here

フラグメントシェーダーのコアは非常にシンプルです

// direction vectors from point to area light corners

for( int i = 0; i < NVERTS; i ++ ) {

    lPosition[ i ] = viewMatrix * lightMatrixWorld * vec4( lightverts[ i ], 1.0 ); // in camera space

    lVector[ i ] = normalize( lPosition[ i ].xyz + vViewPosition.xyz ); // dir from vertex to areaLight

}

// vector irradiance at point

vec3 lightVec = vec3( 0.0 );

for( int i = 0; i < NVERTS; i ++ ) {

    vec3 v0 = lVector[ i ];
    vec3 v1 = lVector[ int( mod( float( i + 1 ), float( NVERTS ) ) ) ]; // ugh...

    lightVec += acos( dot( v0, v1 ) ) * normalize( cross( v0, v1 ) );

}

// irradiance factor at point

float factor = max( dot( lightVec, normal ), 0.0 ) / ( 2.0 * 3.14159265 );

より良いニュース:

  1. このアプローチは物理的に正しいです。
  2. 減衰は自動的に処理されます。 (ライトが小さくなると、より大きな強度値が必要になることに注意してください。)
  3. 理論的には、このアプローチは長方形のポリゴンだけでなく、任意のポリゴンでも機能するはずです。

警告:

  1. 私は拡散コンポーネントのみを実装しました。それがあなたの質問が対処するものだからです。
  2. 妥当なヒューリスティックを使用してスペキュラーコンポーネントを実装する必要があります。これは、すでにコーディングしたものと同様です。
  3. この単純な例では、エリアライトが「部分的に水平線の下」にある場合、つまり、4つの頂点すべてが面の平面上にあるわけではありません。
  4. WebGLRendererはエリアライトをサポートしていないため、「シーンにライトを追加」して機能させることはできません。これが、必要なすべてのデータをカスタムシェーダーに渡す理由です。 (もちろんWebGLDeferredRendererはエリアライトをサポートしています。)
  5. 影はサポートされていません。

three.js r.73

40
WestLangley

うーん。奇妙な質問!非常に具体的な近似から始めて、現在は正しい解決策に向かって進んでいるようです。

拡散のみにこだわっており、表面が平坦である(法線が1つしかない)場合、入ってくる拡散光は何ですか?すべての入射光に固執し、方向と強度がある場合でも、allin = integral(lightin)((lightin)。(normal))* lightを取得するのは難しいです。したがって、問題全体がこの積分を解決しています。ポイントライトを使用すると、合計にしてライトを引き出すことで不正行為をします。これは影のないポイントライトなどでうまく機能します。今、あなたが本当にやりたいことは、その積分を解くことです。これは、ある種の光プローブ、球面調和関数、またはその他の多くの手法で実行できることです。または、長方形からの光の量を推定するためのいくつかのトリック。

私にとって、あなたが照らしたい点より上の半球を考えることは常に助けになります。入ってくるすべての光が必要です。重要でないものもあれば、重要なものもあります。それがあなたの常識です。プロダクションレイトレーサーでは、数千のポイントをサンプリングして、適切な推測を行うことができます。リアルタイムでは、はるかに速く推測する必要があります。そして、それがあなたのライブラリコードがすることです:良い(しかし欠陥のある)推測のための迅速な選択。

そして、それはあなたが後退していると私が思うところです:あなたは彼らが推測をしていること、そしてそれが時々(それは推測の性質です)吸い込むことに気付きました。今、彼らの推測を​​修正しようとしないでください、しかしより良いものを考え出してください!そして、おそらく彼らがその推測を選んだ理由を理解してみてください。適切な近似とは、コーナーケースではなく、十分に劣化することです。これは私にはこのように見えます。 (もう一度、申し訳ありませんが、今はthree.jsコードを読むのが面倒です)。

だからあなたの質問に答えるには:

  • あなたはそれを間違った方法で行っていると思います。高度に最適化されたアイデアから始めて、それを修正しようとしています。問題から始めた方がいい。
  • 一度に1つずつ解決します。スクリーンショットには多くの鏡面反射があります。これは問題とは関係ありませんが、非常に視覚的であり、モデルを設計する人々におそらく大きな影響を与えました。
  • あなたは正しい軌道に乗っており、ほとんどの人よりもレンダリングについてはるかに優れたアイデアを持っています。それはあなたのためにそして反対して働くことができます。いくつかの最新のゲームエンジンとそれらの照明モデルについて読んでください。あなたはいつもハックと深い理解の魅力的な組み合わせを見つけるでしょう。深い理解が正しいハックを選ぶきっかけとなります:)

お役に立てれば。私はここで完全に間違っているかもしれないし、いくつかの簡単な数学を探している誰かをとりとめているかもしれません。その場合、私は謝罪します。

2
starmole

http://s3.hostingkartinok.com/uploads/images/2013/06/9bc396b71e64b635ea97725be8719e79.png

私が正しく理解している場合:

l "ポイントx0のライト"を定義

L〜K/S ^ 2

S = sqrt(y ^ 2 + x0 ^ 2)

L = sum(k /(sqrt(y ^ 2 + x0 ^ 2))^ 2)、y = 0..infinity

L = sum(k /(y ^ 2 + x0 ^ 2))、y = 0..infinity、x> 0、y> 0

L =積分(k /(y ^ 2 + x0 ^ 2))、y = 0 ..無限大= k * Pi /(2 * x0)

http://s5.hostingkartinok.com/uploads/images/2013/06/6dbb7b6d3babc092d3daf18bb3c6e6d5.png

回答:

L = k * Pi /(2 * x0)

k環境に依存

1
user2471050

しばらく前ですが、gpu gems 5には、「最も近いポイント」ではなく「最も重要なポイント」を使用してエリアライトの照明積分を概算する記事があります。

http://gpupro.blogspot.com/2014/03/gpu-pro-5-physically-based-area-lights.html

1
user755921

キャストポイントは常にエッジ上にあることに同意します。

「照明された部分」は、その法線に沿って押し出された光のクワッドによって表される空間の部分であるとしましょう。

サーフェスポイントが照明された部分にある場合、そのポイントを保持する平面を計算する必要があります。これは、法線ベクトルと光​​の法線です。その平面とライトの交点は、オプションとして2つのポイントを与えます(ポイントは常にエッジ上にあるため、2つだけです)。したがって、これら2つをテストして、どちらがより多く貢献しているかを確認します。

点が照らされた部分にない場合は、4つの平面を計算できます。それぞれに表面点、その法線、および光の四角形の頂点の1つがあります。それぞれの明るい四角形の頂点に対して、最も寄与する2つのポイント(頂点+もう1つの交差ポイント)をテストします。

これでうまくいくはずです。反例があったらフィードバックを送ってください。

1