照明計算のための二乗半径と逆二乗半径の有用性は何ですか?


16

「DirectX 11 Rendering in Battlefield 3」PowerPointのスライドの1つで、次のコードに気付きました。

struct Light {
    float3 pos; float sqrRadius;
    float3 color; float invSqrRadius;
}

単に半径を保存するのではなく、なぜ半径の2乗、さらには逆2乗(単に1の2乗の半径)を保存するのか理解できません。彼らは計算でこのデータをどのように使用していますか?さらに、コーンライトとラインライトはどうですか?この構造体はポイントライト専用である必要があります。他のタイプでは機能しないことがわかります-データが十分ではありません。それでも、私は彼らがその正方形とinvSquareをどのように使用しているかを知りたいです。

更新: OK

これは、ネット上で簡単に見つけられる古典的な光減衰方程式です。

float3 lightVector = lightPosition - surfacePosition;

float attenuation = saturate(1 - length(lightVector)/lightRadius);

これをlength(lightVector)実際に行っているので、比較的コストがかかります。

length(lightVector) = sqrt(dot(lightVector, lightVector);

さらに、分割操作(/lightRadius)も非常にコストがかかります。

この方法で光の減衰を計算する代わりに、次の方法で計算できます。これははるかに高速です。

attenuation = saturate(1 - dot(lightVector, lightVector)*invRadiusSqr);

invRadiusSqrはCPUレベルで事前に計算され、シェーダー定数として渡されます。

さらに、結果として(前者の場合は線形ではなく)2次光減衰が得られます。これは、IRL光が2次減衰を示すため、さらに優れています。

皆さん、助けてくれてありがとう!


13
パディング。シェーダーは16バイトで整列されます。そして、コードがどのようにフォーマットされているかを見ると、2つのfloat4にパックされることがわかります。キャッシュから無料で入手するときに便利なものを保存してみませんか?
トルディン14

回答:


23

これは、単にinvSqrRadius = 1/SqrRadiusライトをキャッシュするたびに各ライトの逆二乗半径を計算するのではなく、単純な最適化の一種です。理由は、少なくとも乗算と比較すると、除算は通常「遅い」操作だからです。

この最適化は特に関連しています。

  • 操作がライトごとに膨大な回数行われると、値をキャッシュすると余分なCPU / GPUサイクルが解放されます
  • また、値を読み取るためのメモリアクセス時間が実際には再計算よりも速いと仮定します。

使用方法については、具体的な実装についてはわかりませんが、については1/sqrRadius、これは単に光の減衰、減衰、およびカリングに使用されます。指向性とスポットライトにも関連します。スポットライトの場合の唯一の違いは、減衰を適用した後に スポットライト係数を計算する必要があることです。太陽のような指向性ライトについては、通常、減衰や減衰がないため、無視されると思います。

[編集]さらに詳しく述べると、無関係なデータではありません。光の放射照度は、次の式を使用して計算できます。

E =ファイ/ 4 * pi * rSqr;

どこ

Eはフラックスの面積密度です。

Phiは放射束です。

4 * pi * rSqrは、球の表面積です。

この式は、受信したエネルギー量が距離の2乗で減少する理由を説明しています。

別のポイントは、頂点とライトの間の距離を計算して特定の頂点のライトの寄与を計算する必要があることです(この値はキャッシュされない可能性が高い)。Radius Squareカリングに便利な場所。

ライトの減衰とカリングを計算するための実用的な例が必要な場合、これはタイルベースの遅延レンダラーで特に役立ちます。以下に例を示します。


呪い、あなたは私を7秒倒しました!;)(さらに包括的な答えも!)
トレバーパウエル14

詳細なコメント、特にリンクをありがとう!後者のリンクから私が理解したことから、Battlefield 3は半径ではなく、光源と受光器の間の実際の距離を保存しますよね?それが記事で使用する「d」値です。
cubrman 14

@cubrmanはコードを見ずに推測するのは難しい。私の推測では、逆のradiusSqrであると思われます。そして、彼らが使用する方程式は、記事とは大きく異なる可能性があります。
concept3d 14

しかし、照明の計算で裸の反転した光の半径をどのように使用すればよいですか?ネット上で見つけた光源はすべて、受光面と光源の間の距離を見つけ、後者を元の光の半径で除算し、その結果を二乗する必要があることを教えてくれます。平方半径またはinvSqrRadiusはどこで使用しますか?それは私にとって完全に無関係なデータのようです。
cubrman 14

1
@cubrmanが回答を更新しました。
concept3d 14

6

invSqrRadiusは1ではありません-sqrRadius; 1 / sqrRadiusです。

これは、sqrRadiusで除算する代わりにinvSqrRadiusで乗算できることを意味します(除算は通常、乗算よりもはるかに高価であるため)


6

ここでの他の回答は逆平方半径を扱っていますが、代わりに平方半径を見ていきます(concept3dが触れましたが、さらなる議論に値すると思います)。

正方形が役立つのは距離の比較です。2点間の距離の計算には平方根が必要であり、平方根の計算にはコストがかかることはわかっていますが、やりたいのは距離を比較することです(どちらが小さいか大きいかを見つけて、結果)平方根を捨てることができます。

sqrt(x)> sqrt(y)の場合、x> yでもあります。

ライトの場合、半径の2乗は、ライトの中心から最大範囲までの距離と同じです。もちろん、2乗です。

照明の計算では、これは早期のケースに使用できます。ライティングしているポイントとライトの中心(2乗)の間の距離が2乗半径よりも大きい場合、ポイントはライトを受け取らず、残りの計算を実行する必要はありません。したがって、これは単なる最適化(かなり一般的なもの)です。高価な平方根を使用せずに、減算とドット積を犠牲にして、半径の2乗を使用して距離を比較できます。

もちろん、これがBF3 が正確に使用しているのかどうかはわかりませんが、それほど遠くないことを期待しています。


正しく理解できた場合、コードは次のようになります:if(dot((lightPos-surfacePos)、(lightPos-surfacePos))> lightRadiusSqr)ライティングをしないでしょ?
cubrman 14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.