私はこのデモに似た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による改善に取り組んでいます。現在、私は通常の計算をソリューションに組み込んでいますが、結果にも欠陥があります。
これが私の現在の実装のプレビューです:
各フラグメントの拡散項を計算する手順は次のとおりです。
このソリューションの問題は、照明の計算が最近傍点から行われ、照明を照らしている可能性があるライトサーフェス上の他のポイントを考慮しないことです。さらに断片化します。その理由を説明してみましょう…
次の図を検討してください。
エリアライトは、サーフェスに対して垂直であり、サーフェスと交差しています。サーフェス上の各フラグメントは、常に、サーフェスとライトが交差するエリアライト上の最近点を返します。サーフェス法線と頂点から光へのベクトルは常に垂直であるため、それらの間の内積はゼロです。その後、表面に光が広がっていても、拡散の寄与の計算はゼロです。
エリアライトの最近傍点から光を計算するのではなく、最大のドットを生成するエリアライト上の点から計算することを提案します頂点から光へのベクトル(正規化)と頂点法線の間の積。上の図では、これは青い点ではなく、紫色の点になります。
そして、これが私があなたの助けを必要とするところです。私の頭の中では、この点がどのように導き出されるかについてかなり良い考えがありますが、解決策に到達するための数学的な能力はありません。
現在、フラグメントシェーダーで次の情報を利用できます。
このすべての情報を視覚的なコンテキストに入れるために、この図を作成しました(それが役立つことを願っています)。
私の提案をテストするには、エリアライトにキャストポイントが必要です。これは、赤い点で表されるため、頂点間でドット積を実行できます。 -to-casting-point(ノーマライズ済み)および頂点ノーマル。繰り返しますが、これは可能な最大の貢献値をもたらすはずです。
現在実装している数学を視覚化するインタラクティブなスケッチをCodePenに作成しました。
注目すべき関連コードは、行318です。
castingPoint.location
はTHREE.Vector3
のインスタンスであり、不足しているパズルのピースです。また、スケッチの左下に2つの値があることに注意してください。これらの値は動的に更新され、関連するベクトル間の内積を表示します。
解決策には、頂点の法線の方向に整列し、ライトの平面に垂直な別の疑似平面が必要になると想像しますが、間違っている可能性があります。
良いニュースは、解決策があることです。しかしまず悪いニュース。
内積を最大化するポイントを使用するアプローチは根本的に欠陥があり、物理的に妥当ではありません。
上記の最初の図で、エリアライトが左半分のみで構成されていると仮定します。
「紫色」の点(左半分のドット積を最大化する点)は、両方の半分のドット積を最大化する点と同じです。
したがって、提案されたソリューションを使用する場合、エリアライトの左半分はライト全体と同じ放射を放出すると結論付けます。明らかに、それは不可能です。
エリアライトが特定のポイントに投じる光の総量を計算するためのソリューションはかなり複雑ですが、参考のために、1994年の論文で説明を見つけることができます(部分的に遮蔽された多面体光源の放射ヤコビアン) ここ 。
図1とセクションのいくつかの段落をご覧になることをお勧めしますセクション1.2-次に停止します。 :-)
簡単にするために、遅延型ではなく、three.js WebGLRenderer
を使用してソリューションを実装する非常に単純なシェーダーをコーディングしました。
編集:ここに更新されたフィドルがあります: http://jsfiddle.net/hh74z2ft/1/
フラグメントシェーダーのコアは非常にシンプルです
// 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 );
より良いニュース:
警告:
WebGLRenderer
はエリアライトをサポートしていないため、「シーンにライトを追加」して機能させることはできません。これが、必要なすべてのデータをカスタムシェーダーに渡す理由です。 (もちろんWebGLDeferredRenderer
はエリアライトをサポートしています。)three.js r.73
うーん。奇妙な質問!非常に具体的な近似から始めて、現在は正しい解決策に向かって進んでいるようです。
拡散のみにこだわっており、表面が平坦である(法線が1つしかない)場合、入ってくる拡散光は何ですか?すべての入射光に固執し、方向と強度がある場合でも、allin = integral(lightin)((lightin)。(normal))* lightを取得するのは難しいです。したがって、問題全体がこの積分を解決しています。ポイントライトを使用すると、合計にしてライトを引き出すことで不正行為をします。これは影のないポイントライトなどでうまく機能します。今、あなたが本当にやりたいことは、その積分を解くことです。これは、ある種の光プローブ、球面調和関数、またはその他の多くの手法で実行できることです。または、長方形からの光の量を推定するためのいくつかのトリック。
私にとって、あなたが照らしたい点より上の半球を考えることは常に助けになります。入ってくるすべての光が必要です。重要でないものもあれば、重要なものもあります。それがあなたの常識です。プロダクションレイトレーサーでは、数千のポイントをサンプリングして、適切な推測を行うことができます。リアルタイムでは、はるかに速く推測する必要があります。そして、それがあなたのライブラリコードがすることです:良い(しかし欠陥のある)推測のための迅速な選択。
そして、それはあなたが後退していると私が思うところです:あなたは彼らが推測をしていること、そしてそれが時々(それは推測の性質です)吸い込むことに気付きました。今、彼らの推測を修正しようとしないでください、しかしより良いものを考え出してください!そして、おそらく彼らがその推測を選んだ理由を理解してみてください。適切な近似とは、コーナーケースではなく、十分に劣化することです。これは私にはこのように見えます。 (もう一度、申し訳ありませんが、今はthree.jsコードを読むのが面倒です)。
だからあなたの質問に答えるには:
お役に立てれば。私はここで完全に間違っているかもしれないし、いくつかの簡単な数学を探している誰かをとりとめているかもしれません。その場合、私は謝罪します。
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環境に依存
しばらく前ですが、gpu gems 5には、「最も近いポイント」ではなく「最も重要なポイント」を使用してエリアライトの照明積分を概算する記事があります。
http://gpupro.blogspot.com/2014/03/gpu-pro-5-physically-based-area-lights.html
キャストポイントは常にエッジ上にあることに同意します。
「照明された部分」は、その法線に沿って押し出された光のクワッドによって表される空間の部分であるとしましょう。
サーフェスポイントが照明された部分にある場合、そのポイントを保持する平面を計算する必要があります。これは、法線ベクトルと光の法線です。その平面とライトの交点は、オプションとして2つのポイントを与えます(ポイントは常にエッジ上にあるため、2つだけです)。したがって、これら2つをテストして、どちらがより多く貢献しているかを確認します。
点が照らされた部分にない場合は、4つの平面を計算できます。それぞれに表面点、その法線、および光の四角形の頂点の1つがあります。それぞれの明るい四角形の頂点に対して、最も寄与する2つのポイント(頂点+もう1つの交差ポイント)をテストします。
これでうまくいくはずです。反例があったらフィードバックを送ってください。