Loading [MathJax]/extensions/TeX/AMSmath.js

Unity のライトにはエリアライトというのがあって、下の図のように矩形(もしくは楕円)の上に一様に分布された光源を使うことができます。ただし、エリアライトは HDRP を除いてベイク専用で、ライトマップやライトプローブに焼き付けて使うことになります。このエリアライトにリアルタイムな影をつけてみようかなーと考えていて、そのためには、エリアライトの明るさを、おおざっぱでもいいので、リアルタイムで計算する必要がありました。

そこで、エリアライトの計算をやってみたのですが、ディフューズの計算自体はできたものの、Unity でライトマップにベイクした結果と比べると全然違う結果に…。とはいえ、それっぽい結果も得られたので、積分計算のやり方として、とりあえずブログに清書して残しておくことにしました。
正しいエリアライトの計算は次回のブログで!

ちなみに、 HDRP ではテクスチャ配列に入れたルックアップテーブルを使って実装しているみたいで、シェーダーで積分計算をしているわけではないみたいです。


ある物体の表面上の点 \vec{p} でのディフューズ反射の値を計算することを考えます。その点での面の法線を \vec{n} = (n_x,\ n_y,\ n_z) として、被積分関数をなるべく簡単にするために、座標系を上の図のようにとります。x-y 平面上にエリアライトが分布していて、矩形の辺は x 軸と y 軸に平行です。また今回の計算ではベクトルの外積を使わないので、右手系か左手系かは気にせず、0 \le n_x,\ n_y となるように x 軸と y 軸の向きを決めることにします。

原点は点 \vec{p}x-y 平面に垂直に投影した場所にあるとします。つまり、点 \vec{p} から x-y 平面までの距離を D とすると
\vec{p} = D \vec{z}
となります。エリアライト上の点 \vec{a}\vec{a} = (x, y, 0), 点 \vec{p} から点 \vec{a} の方向を向いた単位ベクトルを \vec{l} すると、\vec{l}
\vec{l} = \frac{\vec{a}-\vec{p}}{|\vec{a}-\vec{p}|} = \frac{1}{\sqrt{D^2+x^2+y^2}}(x, y, -D)
となり、点 \vec{p} でのディフューズの値 I
I = \int_{x_0}^{x_1}\!\!\!\!dx\int_{y_0}^{y_1}\!\!\!\!dy \frac{L_0 max(\vec{n}\cdot\vec{l}, 0)}{|\vec{a}-\vec{p}|^2} = L_0\int_{x_0}^{x_1}\!\!\!\!dx\int_{y_0}^{y_1}\!\!\!\!dy \frac {max(xn_x+yn_y-Dn_z, 0)}{(D^2+x^2+y^2)^{\frac{3}{2}}}
となります。ここで、L_0 はライトの明るさ(色)を表わすパラメーターで、点 \vec{a} から出た光はどの方向にも一様な強さで、距離の2乗に反比例して減衰するとしています (どうも「どの方向にも一様な」という前提が違っていたみたいで、最後のページで Unity のベイクしたエリアライトと比較したところ、違う結果になってしまいました)。また、x_0 \lt x_1,\ y_0 \lt y_1 とします。

max がやっかいですが、y の積分区間に押し付けて
I = L_0\int_{x_0}^{x_1}\!\!\!\!dx\int_{y’_0}^{y’_1}\!\!\!\!dy \frac {xn_x+yn_y-Dn_z}{(D^2+x^2+y^2)^{\frac{3}{2}}}
と書くことにします。積分区間 [y’_0, y’_1]x の関数となる場合があるので、先に y について積分します。

2種類の積分
\begin{eqnarray*} A & = & \int_{y’_0}^{y’_1}\!\!\frac{1}{(a^2 + y^2)^{\frac{3}{2}}}dy \\ B & = & \int_{y’_0}^{y’_1}\!\!\frac{y}{(a^2 + y^2)^{\frac{3}{2}}}dy \end{eqnarray*}
を計算するがあるわけですが、順番にやっていきます。

まずは簡単な B の方。
\begin{eqnarray*} B & = & – \left[ \frac{1}{\sqrt{a^2 + y^2}} \right]_{y’_0}^{y’_1} \\ & = & \frac{1}{\sqrt{a^2 + {y’_0}^2}}-\frac{1}{\sqrt{a^2+{y’_1}^2}} \end{eqnarray*}

次に A の積分ですが、y = a\tan\theta とおくと
\begin{eqnarray*} & dy & = & \frac{a}{\cos^2\theta} d\theta, \\ & a^2+y^2 & = & \frac{a^2}{\cos^2\theta} \end{eqnarray*}
となるので、y’_0 = a\tan\theta_0,\ y’_1 = a\tan\theta_1 とすると、
\begin{eqnarray*} A & = & \int_{\theta_0}^{\theta_1}\frac{\cos\theta}{a^2} d\theta \\ & = & \frac{1}{a^2}\left\{\sin\theta_1-\sin\theta_0 \right\} \\ & = & \frac{1}{a^2}\left\{\frac{y’_1}{\sqrt{a^2+{y’_1}^2}}-\frac{y’_0}{\sqrt{a^2+{y’_0}^2}} \right\} \end{eqnarray*}
が得られます。a^2 = D^2+x^2 として元の積分に A, B を戻してやると、
\begin{eqnarray*} I & = & L_0\int_{x_0}^{x_1}\!\!\!\left\{ (xn_x-Dn_z)A+n_yB \right\}dx \\ & = & L_0\int_{x_0}^{x_1}\!\!\! \left\{ F(x, y’_1)-F(x, y’_0)\right\}dx, \\ F(x, y) & = & \frac{1}{\sqrt{D^2+y^2+x^2}}\left(\frac{n_xyx-Dn_zy}{D^2+x^2}-n_y\right) \end{eqnarray*}
です。y についての積分はそれほど難しくありませんね。もしエリアライトが x 軸に沿って棒状に分布していれば、x について積分する必要がないので、シェーダーで計算するのも現実的です。

次に、x について積分する前に、積分範囲について整理しておきましょう。0 \le n_x,\ n_y となるように座標系を選んだので、積分範囲は次の図の台形のような形になります。

ここで、
\begin{eqnarray*} x’_0 & = & max\left(x_0,\ min\left(x_1,\ \frac{Dn_z-n_y y_1}{n_x}\right)\right),\\ x’_1 & = & min\left(x_1,\ max\left(x_0,\ \frac{Dn_z-n_y y_0}{n_x}\right)\right),\\ y(x) & = & \frac{Dn_z-n_x x}{n_y} \end{eqnarray*}
とすると、積分範囲は次のように書けます。

I = L_0\int_{x’_0}^{x_1}\!\!\!F(x, y_1)\,dx-L_0\int_{x’_0}^{x’_1}\!\!\!F(x, y(x))\,dx-L_0\int_{x’_1}^{x_1}\!\!\!F(x, y_0)\,dx

つまり、y が定数(y_0 もしくは y_1) の場合と、y = \frac{Dn_z-n_x x}{n_y} の場合で F(x, y) を積分する必要があります。

次のページでは、y が定数の場合を計算していきます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

Anti Spam Code *