DirectXの話 第126回
Virtual Point Light
上の画像だといまいちわかりにくいかもしれませんが、今回はVirtual Point Light(VPL) を利用した簡易大域照明です。
VPLの生成はReflective Shadow Mapを利用しています。
正直、よく出来たサンプルとは言えないのですが、今後に繋がりそうなので公開してみました。
大域照明(グローバルイルミネーション)は現世代機でもすでにリアルタイム手法がいくつか出てきています。
しかし、次世代機ともなるとよりリアリティのある手法が求められるようになると思います。
『GPU Pro 3』では次世代機用の大域照明手法がいくつか紹介されていましたが、基本はレイトレーシングのようです。
もちろん、レンダリング全てをレイトレーシングは無理があるので、色々簡略化したり大域照明やシャドウのみに使用したりといった具合になります。
さて、そんな大域照明手法の1つとして割と昔から議論に上がっていたのがVPLです。
大域照明の基本は間接照明で、光がオブジェクトに反射し、それがまた別のオブジェクトを照らすという代物です。
そういえば、一時期インテリアとして間接照明が流行ったことがありましたね。今はどうなのか知りませんが。
スポットライト的なものを壁などに反射させることで直接のきついライトと違って柔らかめの(ただし暗め)のライトになるというので流行っていたようです。
まあ、おしゃれな感じにしたりムードを出したりといった理由なんでしょうね。
これは、実際には光源ではない壁などに少し強めにライトを当てることによって壁を光源代わりにしよう、という考えなわけです。
つまり、壁に光源があるものとして考えればいいじゃない!というのがVPLの基本理論です。
すなわち、実際には光源が存在しないけれど、反射光がオブジェクトをライティングするのであれば光源があるのと同じじゃね?ってわけで、
それなら仮想のポイントライト、Virtual Point Lightを置けば間接照明が実現できる、という理屈になります。
間接照明を実装するにはVPLが相当数必要になるのですが、前回やったTile-based Renderingのおかげで実現可能性が高くなってきました。
VPLがシーン中に512個くらいあったとしても、TBDRやTBFRでそれなりの速度で描画できるわけです。今までなら考えられませんでした。
問題は、この数百個のVPLをどう配置するかという点です。
今回はその解決方法としてReflective Shadow Mapを利用しました。
Reflective Shadow Mapは名前からシャドウイングを行う手法と思われるかもしれませんが、実際には間接照明の手法です。
名前の由来はライト方向からレンダリングをする、つまり通常のShadow Mapと同じレンダリングを行う点から来ています。
通常のShadow Mapが深度値を出力するのに対し、Reflective Shadow Mapはディフューズの放射輝度を求めます。
この強さと色からVPLがあるものと仮定して間接照明を行うのが通常のReflective Shadow Mapですが、今回は本当にVPLを置いてしまったわけです。
サンプルでは16*16と32*32のReflective Shadow Mapを描画しています。それぞれ256個と1024個のVPLが生成されます。
描画するのはライティングを行った色、座標、法線の3種類です。
このVPLはオブジェクトに反射したものなので、オブジェクトの法線方向の逆側にはライティングの処理をしないようにしています。
座標はVPLの位置を指定するために必要なのは言わずもがな、カラーはライトのカラーとVPLの半径を求めるのに使用します。
通常のShadow Mapと解像度が違うため、同時には描画できません。
Reflective Shadow Mapを描画したらCompute Shaderを用いてVPLを生成します。
UAVへの書き込みが出来ればいいので、Pixel Shaderを用いてVPLを生成することも出来ますが、どちらを使っても速度的にそんなに変わらないと思います。
で、あとはこのVPLを利用してTile-based Renderingを行うだけです。ね、簡単でしょ?
静止画で見るとまあそれなりにうまくいっているように見えます。
しかし、ライトを動かすとやっぱり厳しいと言うことがよくわかります。
所詮32*32程度のレンダリング画像では、VPLが置かれるべき部分にそれなりの密度でVPLが置かれる、という状況にはならないようです。
Reflective Shadow Mapでももう少し高い解像度で描画するのが普通ですし、やはり解像度を上げざるを得ないというのが個人的な感想です。
解決策としては、高い解像度で描画した後、VPLを生成する際に不要なピクセルを間引く手段でなんとかするしかないかな、と思います。
例えば128*128でReflective Shadow Mapを描画、タイル状に区切って各タイルごとにVPLを生成。
各タイルで近い位置に存在するならまとめて1つのVPLを生成し、そうでないなら複数のVPLを生成、ある程度弱い部分は生成しない、などの手法が考えられます。
とはいえ、それでも安定したVPLが求められるかというと疑問ですし、別の手段で生成した方がいいんじゃないかと思ったりもします。
シーンが固定ならそれこそ事前に計算しておいた方がいいだろ、って話になりますし、難しいところですね。
では、サンプルは以下から。
マウス左ドラッグでカメラを、マウス右ドラッグでライトを動かせます。
ライトを動かすとVPLの生成が粗いためにあまり綺麗に見えないのが丸わかりですね。
Reflective Shadow MapをPSM手法で描画する、っていう手法もありかもしれませんが、より安定しなそうな気もします。
VPLのリアルタイム生成で軽くて実装しやすくてクオリティも高い手法ってないものでしょうかねぇ…。