LIFE LOG(MochiMochi3D)

MochiMochi3D

3D関係の記事を書きます

Punctual Lightの理論的取り扱い

点光源やディレクショナルライトなどのリアルタイムでお馴染みなPunctual LightがPBRにおいてどのような扱いがされているのかまとめた記事です。Punctual Lightによるハイライトが異常に明るくなる原因などについて簡単に書いています。

Punctual Lightとは

Punctual Lightとは点光源(Point Light)、スポットライト(Spot Light)、ディレクショナルライト(Directional Light)といった面積を持たない光源の総称です。Unityなどのゲームエンジンとかやっている人ならお馴染みなあのライト達です。

一般にPunctual LightはIntensityといった何らかの光の強さを表すパラメーターを持っており、例えば点光源からの反射光の輝度 LはBSDF f(\omega_i,\omega_o)、点光源の位置 x_c、点光源の方向 l_c、反射場所の位置 xを用いると次のような形で書かれます。

 \displaystyle{
L_o(\omega_o) = f(l_c,\omega_o) \cos{\theta_c} \frac{1}{|x - x_c|^2} \times intesity
}

一方でディレクショナルライトの場合は次のように書かれます。

 \displaystyle{
L_o(\omega_o) = f(l_c,\omega_o) \cos{\theta_c} \times intesity
}

疑問

ここでちょっと疑問がありました。BSDFは一見反射率を示す関数の様に見えて値が0から1の範囲に制限されるように思えますが、BSDFは厳密には「放射照度Eに対して輝度Lを返す関数」であり、その範囲は0から∞までの値を取ることが可能です。実際GGXのBSDFでは粗さ \alphaが0.01だと角度によっては平然と1を超えて非常に高い値を返すこともあります

 \displaystyle{
0 \le f(\omega_i,\omega_o) < \infty
}

この性質を考慮しながら先ほどの点光源やディレクショナルライトの式を思い出してみると、BSDFが1よりはるかに大きければその反射光の輝度はIntensityを超えることが十分にあり得ますし、何なら無限に発散することすら許されます。つまり、Intensityがどんな小さい値でも反射光はいくらでも明るくなりえることが予測されます

GGXのBSDF(α=0.1)を実装してIntesityが1の平行光源を用意し、反射光の輝度の値が1以上になる場所を赤色で表示してみるとハイライト部分が1以上になっていることが分かります。この例ではハイライトの中心部分は更に10以上の値を持つことが確認されました。

Intesityが1の平行光源を当てたGGX(α = 0.1)、左の図は反射光の輝度が1以上になった所を赤色で示した図

つまるところ、Punctual LightはBSDFによっては自らの明るさを示すIntensityをはるかに超える反射光を生じさせることがあるわけです。Intensityが輝度みたいなものと考えるとなんだかエネルギー保存が破られているように感じます。

Punctual Lightの理論的取り扱い

先ほどの議論ではPunctual Lightの式は一見してエネルギー保存則を破っているように思えました。この疑問に対する答えを先に言いいますとPunctual lightの輝度は無限大であるためです。意味がわからないと思いますが、これを正しいと認めれば反射光の輝度がいくら明るくなろうともエネルギー保存は守られることになります。

輝度が無限ならIntensityってなんなんだと言うことになりますが、ここで一度Intensityという量についてもう少し詳しく知るため、それぞれの物理量としての単位を見てみましょう。輝度の単位は \rm{W \cdot m^{-2} \cdot Sr^{-1}}、BSDFの単位は \rm{ Sr^{-1}}であることが知られていますので、ディレクショナルライトの式の単位について考えてみると、

 \displaystyle{
L_o \lbrack \rm{W \cdot m^{-2} \cdot Sr^{-1}} \rbrack= \rm{f(\omega_i,\omega_o)} \cos{\theta_c} \lbrack \rm{Sr^{-1}} \rbrack \rm{\times intesity} \lbrack \rm{W \cdot m^{-2}} \rbrack
}

ということになります。両辺の単位は一致しなければいけないので、Intensityの単位は放射照度の単位 \rm{W \cdot m^{-2}}に相当することになります。一方で点光源の式では距離の二乗で割れているのでその分単位が変わり、エネルギーの単位 \rm{W}を持つことになります。

 \displaystyle{
L_o \lbrack \rm{W \cdot m^{-2} \cdot Sr^{-1}} \rbrack= \rm{f(\omega_i,\omega_o)} \cos{\theta_c} \lbrack \rm{Sr^{-1}} \rbrack \rm{\frac{1}{|x - x_c|^2}} \lbrack \rm{m^{-2}} \rbrack \rm{\times intesity} \lbrack \rm{W} \rbrack
}

こうして見てみるとどうやらIntensityは少なくともPunctual Lightの輝度ではないことがわかります。Punctual Lightの式は単純な輝度の掛け算をしている訳ではないようです。

話を戻してPunctual Lightの輝度が無限大になっているというのはどうゆうことか話していきます。そもそも光源の放射輝度とは光源が放つ光の放射束の面積、立体角微分として定義されています。Punctual Lightはそもそも定義として面積を持ちません。従って、なので放射束の面積微分(放射発散度)は無限大へと発散し、それに伴い放射輝度も無限大へと発散することになります。

具体例として点光源を球光源の面積0への極限としてモデル化して考えてみます。放射束 \Phiの光を一様に放つ半径 rの球光源を考えて、その放射発散度 Eは面積 S

 \displaystyle{
E = \frac{\Phi}{S} = \frac{\Phi}{4\pi r^2}
}

という風に書くことが出来ます。この時、方向 \omegaにおける放射輝度 L(\omega)

 \displaystyle{
L(\omega) = \frac{\Phi}{8\pi^2 r^2}
}

となります。ここで点光源の極限として半径 rに対して r \rightarrow 0の極限を考えると放射発散度 E放射輝度 Lは無限大へと発散することが分かります。

このようにPunctual Lightはその性質から輝度が無限大であり、反射光の輝度は如何なる値でもエネルギー保存を満たします。なので、実はハイライトがめっちゃ明るくなるのは正常な挙動であると言えます*1

Punctual Lightの式の導出

Punctual Lightは一見すると単純なように見えて、真面目に考えると結構難しい概念です。シンプルにまとめられているあのPunctual Lightの式もその導出は単純ではありません。ここでは[ Naty Hoffman 2013]の資料を基にPunctual Lightの式の導出を見ていきます。

面積を持つ小さな光源からの光輸送を考えます。照らされている点 xにおいて xから光源の中心への方向ベクトル l_cとして、光源はそこから \epsilonの角度の立体角で収められているものとします。

 \displaystyle{\begin{align} 
L(\omega_i) &\left\{ \begin{array}{}
 \neq 0 & (\angle(\omega_i, l_c) < \epsilon) \\
 = 0 & (\angle(\omega_i, l_c) > \epsilon)
\end{array} \right.
\end{align}
}
\tag{2.4}

遮蔽とか他の光源とかはないとして、この時 xにおける入射光の輝度関数 L(\omega) \omega l_cの角度が \epsilonより大きければ0(光源がない)、小さければ何らかの値を持つという形で表すことが出来ます。

 xレンダリング方程式は以下の様になります。

 \displaystyle{
L_o = \int_{\Omega} f(\omega_i,\omega_o) \cos{\theta_i} L(\omega_i) d\omega_i \tag{3.1}
}

ここから光源をPunctual Lightへと変化させる極限を考えてPunctual Lightの式を導出していきます。まずは、Punctual Lightの極限は光源をその中心に向かって無限に小さくする形で考えるとします。この極限は光源の範囲を表す \epsilonによって \epsilon \rightarrow 0という極限として表すことが出来ます。従って、Punctual Lightにおけるレンダリング方程式は

 \displaystyle{
L_o(\omega_o) = \lim_{\epsilon \rightarrow 0} \lbrace \int_{\Omega} f(\omega_i,\omega_o) \cos{\theta_i} L(\omega_i) d\omega_i \rbrace
}

という極限で表現されます。ここで、 \epsilonが0に向かうとするとBSDFとコサインは積分から取り出して次のように書けると述べられています。積分と極限の順序を入れ替えてるみたいですが、正しいのはちょっと追えていません。ここでは正しいとして話を進めます。

 \displaystyle{
L_o(\omega_o) =f(l_c,\omega_o) |l_c \cdot n| \lim_{\epsilon \rightarrow 0} \lbrace \int_{\Omega} L(\omega_i) d\omega_i \rbrace
}

一番最後の極限値について求めるため、いったん球光源が真上に存在し l_c = n、照射点のBSDFがLambertである状況を考えます。この時の反射光の輝度 L_oはLambertなので出射方向 \omega_oに依存せず、その値を c_lと定義します。

 \displaystyle{
c_l = \frac{1}{\pi} \int_{\Omega} \cos{\theta_i} L(\omega_i) d\omega_i
}

ここでPunctual Lightの極限を「 c_lを保存しながら \epsilon \rightarrow 0」という極限で明確に定義することとします。従って、Punctual Lightの極限を適用すると次のような方程式を得られます。

 \displaystyle{
c_l = \frac{1}{\pi} |l_c \cdot n| \lim_{\epsilon \rightarrow 0}\int_{\Omega} L(\omega_i) d\omega_i
}

すなわち、輝度の極限は次のように求まります。

 \displaystyle{
\lim_{\epsilon \rightarrow 0}\int_{\Omega} L(\omega_i) d\omega_i = c_l \pi
}

急に c_lを保存する極限としてPunctual Lightを再定義しましたが、これは前節の議論の際Punctual Lightの放つ放射束は一定とするところから来ています。

分かりづらいですが c_l \piは出射光の放射発散度を表す量であり、これが保存されているということは照射されている点への放射束(エネルギー)が一定であることを示します。すなわち、Punctual Lightからの放射束が一定であることを示しているわけです。従って、Punctual Lightの極限は c_lが保存する極限として定義することが出来ます。

この結果を代入すれば

 \displaystyle{
L_o(\omega_o) =f(l_c,\omega_o) |l_c \cdot n| c_l \pi
}

Intesityを定数 c_l \piと定義すれば今までのあのPunctual Lightの式を求めることが出来ます。

 \displaystyle{
L_o(\omega_o) =f(l_c,\omega_o) \cos{\theta_c} intesity
}

今までの導出は立体角表現のレンダリング方程式によって行っていたため、この式が適用可能なライトというのは「輝度を方向から指定できる」ようなものでないといけません。すなわち、これはディレクショナルライトの式となります。

一方で点光源は位置 x_cによって指定するものであるため、その式を求めるには3点形式のレンダリング方程式を用いる必要があります。(3.1)式を3点形式に変換すると

 \displaystyle{
L_o = \int_{M} f(\omega_i,\omega_o) \cos{\theta_i} \frac{| - \omega_i \cdot n_i|}{|x - x_i|^2} G(x \rightarrow x_i) L(x \rightarrow x_i) dx_i
}

という形で書くことが出来ます。ここでは Mを光源上の点の集合、 n_iは光源上の点 x_iの法線、 Gは可視関数と定義しています。球光源の場合 n_i = -\omega_iであるため、Punctual Lightの極限を適用すると以下の様になります。

 \displaystyle{
L_o = f(l_c,\omega_o) \cos{\theta_c} \frac{1}{|x - x_c|^2} \lim_{\epsilon \rightarrow 0}\int_{M} G(x \rightarrow x_i) L(x \rightarrow x_i) dx_i
}

Directional Lightと同様の議論を行えばいいので、点光源の式として次のように導出することができます。

 \displaystyle{
L_o = f(l_c,\omega_o) \cos{\theta_c} \frac{1}{|x - x_c|^2} intensity
}

まとめ

Punctual Lightはその式の単純さに反して、理論的な扱いはかなり複雑です。抽象化されたライトであるため、輝度が無限大であったりと非直感的な性質を持っていたりします。また、明るさを表すパラメーターであったIntesityというのは「ランバート面に真上から照射した時の放射照度(放射束)」に相当する量であったことが分かりました。

ここでの話はあくまで「PBRでPunctual Lightを解釈するならこんな感じだよね」というものであり、正直な所ここまで厳密に考える必要はないと思います(ほんとに厳密かはともかく)。実装ではもちろんIntesityの中身がどうこうとかは考えずに設定してもいいですし、理論的にもシンプルにデルタ関数として表現しても結論自体は変わらないと思います(極限がデルタ関数の近似になるし)。

参考文献

[Naty Hoffman 2013] SIGGRAPH 2013 Course: Physically Based Shading in Theory and Practice Background: Physics and Math of Shading

*1:Unityとかだとハイライトに上限値を入れて発散を防いでいたりします