このデモに似た WebGL でのエリア照明の実装に取り組んでいます。
http://threejs.org/examples/webgldeferred_arelights.html
上記の three.js の実装は、gamedev.net 上の ArKano22 の作業から移植されました。
http://www.gamedev.net/topic/552315-glsl-area-light-implementation/
これらのソリューションは非常に優れていますが、どちらにもいくつかの制限があります。ArKano22 の最初の実装の主な問題は、拡散項の計算が表面法線を考慮していないことです。
私は、この問題に対処するために redPlant による改善に取り組んで、数週間このソリューションを拡張してきました。現在、ソリューションに通常の計算が組み込まれていますが、結果にも欠陥があります。
これが私の現在の実装のプレビューです。
序章
各フラグメントの拡散項を計算する手順は次のとおりです。
- 投影されたベクトルがライトの法線/方向と一致するように、エリア ライトが置かれている平面に頂点を投影します。
- 投影ベクトルをライトの法線と比較して、頂点がエリア ライト プレーンの正しい側にあることを確認します。
- ライトの中心/位置から平面に投影されたこの点の 2D オフセットを計算します。
- この 2D オフセット ベクトルをクランプして、ライトの領域 (幅と高さで定義) 内に収まるようにします。
- 投影されクランプされた 2D ポイントの 3D ワールド位置を導き出します。これは、頂点に最も近いエリア ライト上のポイントです。
- 頂点から最近点へのベクトル (正規化された) と頂点法線の間のドット積を取得することにより、ポイント ライトに対して行う通常の拡散計算を実行します。
問題
このソリューションの問題点は、ライティングの計算が最も近いポイントから行われ、フラグメントをさらに照らしている可能性があるライト サーフェス上の他のポイントを考慮していないことです。その理由を説明してみましょう…</p>
次の図を検討してください。
エリア ライトは、サーフェスに対して垂直であり、交差しています。サーフェス上の各フラグメントは、サーフェスとライトが交差するエリア ライト上の最も近いポイントを常に返します。サーフェス法線と頂点からライトへのベクトルは常に垂直であるため、それらの間の内積はゼロです。その後、サーフェス上に光の大きな領域が迫っているにもかかわらず、拡散寄与の計算はゼロです。
考えられる解決策
エリア ライト上の最も近いポイントからライトを計算するのではなく、頂点からライトへのベクトル (正規化された) と頂点法線の間のドット積が最大になるエリア ライト上のポイントからライトを計算することを提案します。上の図では、これは青い点ではなく紫色の点になります。
ヘルプ!
ですから、ここであなたの助けが必要です。私の頭の中では、この点を導き出す方法についてかなり良い考えを持っていますが、解に到達する数学的能力はありません。
現在、フラグメント シェーダーで次の情報を利用できます。
- 頂点位置
- 頂点法線 (単位ベクトル)
- ライトの位置、幅、高さ
- ライト法線 (単位ベクトル)
- ライトライト (単位ベクトル)
- ライトアップ (単位ベクトル)
- 頂点からライト平面 (3D) に投影されたポイント
- ライトの中心からオフセットされた投影点 (2D)
- 固定オフセット(2D)
- このクランプされたオフセットのワールド位置 –最も近い点(3D)
このすべての情報を視覚的なコンテキストに入れるために、次の図を作成しました (役に立てば幸いです)。
私の提案をテストするには、エリア ライトのキャスト ポイント(赤い点で表される) が必要です。これにより、頂点からキャスト ポイント (正規化) と頂点法線の間のドット積を実行できます。繰り返しますが、これにより可能な最大の貢献値が得られるはずです。
アップデート!!!
現在実装している数学を視覚化するインタラクティブなスケッチを CodePen で作成しました。
http://codepen.io/wagerfield/pen/ywqCp
注目すべき関連コードは318行目です。
castingPoint.location
のインスタンスでTHREE.Vector3
あり、欠けているパズルのピースです。また、スケッチの左下に 2 つの値があることにも注意してください。これらは動的に更新され、関連するベクトル間の内積を表示します。
解決策には、頂点法線の方向に整列し、ライトの平面に垂直な別の疑似平面が必要になると思いますが、間違っている可能性があります!