前々から🦀のユーザーからモニターのspecularが欲しいという要望をもらっていたけど、色々面倒でやってなかった。 というのも、🦀はPRTの実装であり光源のTRSはstaticであることが前提にあるが、LTCはdynamicなものに対しても使えるわけで、その辺の組み合わせには自由度があるし、立て付けをどうするかが悩ましかった。
v3で欲しい機能があったら自分で作ってね、というスタンスにしたので、じゃあ自分の中での「まあこんくらいの機能があればいいでしょ」を実装に入れてもいいな、ということで以下の仕様で入れた。
- Texture LightのTRSはstaticしてTextureだけが動的に変わるとする。
- diffuseはこれまで通りPRTでやる。
- specularだけLTCでやる。
- specularの影をdiffuseの影で代用する。
- Texture Lightはシーンに3つまでで全て同じTextureを使う。
LTC自体は https://github.com/selfshadow/ltc_code/tree/master をVRC向けに移植したやつで特に珍しいことはしてない。 テクスチャのフィルタリングだけlox先生に教えてもらったサーフェスのシェーダーでmipmap使ってガウス関数を階段関数で近似するやつになってる。
LTCのちゃんとした影は https://eheitzresearch.wordpress.com/705-2/ があるけど、🦀に組み込むのは難しいのでdiffuseのvisivilityを使うことにした。
スペキュラのシャドウで欲しい値は、fをスペキュラのBRD(e.g. GGX)、Vをvisibilityとしたときに が影付きのライティングで、これが計算できないので、
を計算しましょうという話で、第1項はLTCで計算できて第2項を求めましょうという話でした。 それで、色々あきらめてdiffuseの影を使うというのはfをdiffuseのBRDF(e.g. 定数)にするということであり、
を使っているということになる。なのでもちろん全然正しくなくて、具体的には影の面積がせまくなってると思う。けど、ないよりはある方がそれっぽいのでヨシ!
それでとを計算しましょうということになるけど、 これはUnityでライトマップをベイクするときに直接光だけ(bounce=0)にして、影ありと影なしでベイクすれば作れる。 あとは焼いたライトマップ2枚を使って、(影ありの明るさ)÷(影無しの明るさ)を計算すればよくて、16bit floatで十分っぽい雰囲気だったので、 BC6H圧縮したHDRテクスチャ1枚にTexture Light3枚までのvisibilityを格納できるということになる(3枚までにしたのはこれが理由)。
実際に動いているのはこんな感じ。わかりにくいけどDJブースの手前にはスペキュラが映り込んでいないのがわかる。
— しばづけ (@shiva_duke28) 2023年6月18日
細かい話をすると、Unityのライトマッパーを使うとき、発光マテリアルのついたRendererを光源に使うと、影なしLightmapは焼けないです。 多分内部的には発光マテリアル付きRendererは光源としては扱われてないっぽくて、Bounce=0でベイクするとこれらのRendererから出てる光はライトマップに焼かれないっぽい。 MeshRendererのCastShadowフラグは光源向けの値っぽいので、なるほど、という感じがする。仕方ないので、🦀ではArea Lightを使ってる。