Imaginantia

思ったことを書きます

Resonark4 の光計算について

お手伝いをしました。

ケーブルが光ると環境もちゃんとその光を受けます。それをやりました。

折角なので実装について書いておきます。丁寧に書くのはめんどうなので断片だけ。

基本の仕組み

やりたいことは「各光源に対応した光の情報を加算する」だけ。GCではライトマップでやってましたが、今回はちょっと光源も物体も多いです。

なのでライトマップベースではなく Light Probe 的に処理をしています。空間に 16x16x32 の Light Probe を配置して、各光源から受ける光を L1 まで保存しています (参考)。

これによって空間内どこでも光の情報が得られるので、一応簡易的なフォグを入れることにも活かされています。

で、「光源」が問題で。

ケーブル一本一本やるに加えて、各ケーブルを大量に分割するとなると、こう。864個くらいになります。無理やね。

そこで、中心軸から見て角度8分割x距離3分割した上で、ケーブル自体は分割しないという選択をしました。

まぁ24個くらいなら許容範囲。実際にはこれに蛍光灯 (これも奥行きは分割してない) 2つ、あと空が手前・奥で2つ。合計28個です。

予め 16x16x32 の各テクセル?で 28回 Light Probe データのサンプリングを行い、その結果を各物体が自由に拾う、という形です。ライトマップサンプルよりはマシです。

 

ちなみに Light Probe を焼くときに、シーンを消してます。重かったので。つまり間接光は Light Probe に含まれていません!

なので GI (大域照明) じゃないですね。ただの動的照明です。名前つけたほうが良いんだろうか。

奥行方向について

ケーブルを分割しないと一気に光らせることしかできません。

が、まぁ正確性を犠牲にすれば疑似的な調節はいくらでもできます。

ある場所に来る光は、ケーブルの各点に関する重み付き積分になります。「全体が等しく光っているときの光量」は事前に求めたので、そこにいくらか割合が乗ってくる形になるはず。

つまり残り考えるべき重みというのは確率分布になってくれるということです。

日本語だとわかりにくいんですが、要はこれだけです (I(x) が各点の明るさ) :

\displaystyle \int_L I(x)w_p(x) \mathrm{d}x = W\int_L I(x)\frac{w_p(x)}{W}\mathrm{d}x\ \text{ where }\ W = \int_L w_p(x)\mathrm{d}x

自明ですね。

で、あとは確率分布 \displaystyle f_p(x) = \frac{w_p(x)}{W} を推定すればいいじゃん、という話になりますが、まぁ正規分布で良さそうな気がします。知らんけど。

特に何も考えてませんが、正規分布を仮定しました。

つまり \displaystyle \frac{w_p(x)}{W} \approx \frac{1}{\sqrt{2\pi\sigma^2}}\exp\left(-\frac{(x-p)^2}{2\sigma^2}\right) とします。\sigma がパラメータになっていますが、適当にワールドの中心軸からの距離に応じた関数を立てました (推定とかはしてないです)。つまりケーブルに近いほど?鋭くなって、遠いほどボケます。まぁ今回のシーンだとそこまで変わらないんですが…。

 

で、どうやって実際に値を計算するかなんですが、ここで I(x) に仮定を入れます。

\displaystyle I(x) = \begin{cases}A\exp(Bx+C)(0.5\cos(Dx+E)+0.5)\ \ \ \ (0\le x\le 1, Bx+C \le 0)\\ 0\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \  \ \ \ \ \ (\text{otherwise})\end{cases}

こういう関数です。

まぁこれくらい出来たら演出には困らないんじゃないの、っていう観点で決めました。正確に言うとこれくらいしかできないと思ったけどこれで十分だと思いました。

で、\cos 項を分離して C(x) = 0.5\cos(Dx+E)+0.5 とし、I(x) = E(x)C(x) とします。

積分 \displaystyle \int_L E(x)C(x)f_p(x) \mathrm{d}x がめんどくさいので、\displaystyle \left(\int_L E(x)f_p(x)\mathrm{d}x\right)\left(\int_L C(x)f_p(x)\mathrm{d}x\right) にしました。「E(x)C(x) をぼかしたやつは、E(x) をぼかしたやつとC(x) をぼかしたやつの積」と主張しています (もちろん成り立ちません)。これは全く根拠ない変形なんですが、まぁ今回のケースなら気にならないかなと思った。

で、右側 (C(x) 部分) は適当に近似します。\cos の係数をいい感じに調節すればいいと思ったので (ぼけても phase は変わらなそう) \sigma の関数を sqrt とかでなんとなくで入れてます。作ってたときにはもうちょっと根拠あったような気がするんですが忘れました。

左側 (E(x) 部分) は LUT で解決しています。パラメータ B はただ伸縮させるだけ、C はただずらすだけなので LUT に入れる必要がなく、p\sigma の2パラメータで収まります。これです。

たしか普通に C# で台形公式 256 分割くらいで計算しました。

これで「0 から無限までの積分値」が求まりますが、I(x-1) に対して同じ計算をすることで「1 から無限までの積分値」が求まります。引くと「0 から 1 までの積分値」が出ます (ケーブルは有限長なので…)。

というわけで材料が揃ったのでくっつけると光が計算できます。

 

ここまでの内容だと L1 の復元には至らないと思うんですが、その通りで、directionality は無視しています (全体が光っているときと同じ方向を向いています)。が、シーン的にそこまで問題がありません。

あとこれだけだと狭いところもそこそこ明るくなるので、AOマップを別で焼いてもらって乗算してます。

ちなみにちっちゃい点滅は光計算には考慮していません (どうせぼけるしuniformだし)。明/滅の割合で単純にスケールさせてるだけです。

そんなもんかな。

おわり

形状に縛りを入れるとまぁそこそこ変なことができるなあという例でした。

あと正確性は誰も気にしてないので、「それっぽさ」(近いケーブル位置の色の影響を受ける傾向、遠いほど指向性が弱まる傾向) さえあれば「それっぽくなる」ということがよくわかりますね。