点光源とBRDFをとりあえず説明しましたので、これらを用いて、実際にレンダリングするにはどうするかを考えてみたいと思います。
ピクセルシェーダーで計算するもの
ピクセルシェーダーで計算するべきものは、1ピクセル分の立体角で観測される放射束です。1ピクセル分の立体角は、レンダリングする画像の解像度と視錐体の形状で変化しますが計算できそうです。しかし、レンダリングされた画像は、再びディスプレイ上で拡散放射的に自発光することになるので、こちらは一概に決めることが出来ません。
したがって、ピクセルが観測する絶対量的な放射束を計算してもあまり意味はありません。ですから、ピクセルシェーダーでは、観測される放射束の密度のみを計算します。すなわちピクセル方向で観測されたRadianceそのものを、ピクセルシェーダーの出力とします。
BRDFを用いてRadianceを計算する
BRDF(f)はIrradiance(E)とRadiance(L)の比であると、以前の記事で言いました。
BRDF関数は一般的には自由度の高い関数で、さまざまな変数を取ります。
ただし、IrradianceとRadianceに限って書けば、とある平面のある位置に届く、方向(ωi)からの微小Irradiance(dE(ωi))によって、観測者の方向(ωo)に放射する微小Radiance(dL(ωo))の比ととすることができます。
計算したいのは、観測者の方向へのRadianceです。とある平面のある位置に届くIrradiance(E(ωi))とBRDF関数の積を(ωi)で半球積分できれば計算できます。
残念ながら、この積分を計算するためには、とある平面のある位置に届く、半球分のIrradianceを計算しなくてはなりません。しかしこれには膨大な時間がかかります。もしこれが計算できれば、光源からの直接光に限らず、影も間接光も正確に計算されることになります。
このままではリアルタイムで計算できないので、とある光源からの照射による反射、つまり直接光による反射のみを計算してみたいと思います。
直接光が方向(ωi)から来るとすると、以下のように書くことが出来ます。
次に、Irradiance(E(ωi))を、とある光源から届いたRadiance(Li(ωi))で書き換えます。Radianceは投影微小平面あたりの単位立体角あたりの放射束なので、平面の法線と光源の方向の成す角(θ)の余弦をかければ、Irradianceとなるはずです。したがって、以下の式で計算できそうです。
実際にBlinn-PhongのBRDFで計算してみる
より具体的にPixelShaderで行う計算をしてみたいと思います。
- 平面の法線ベクトル(N)
- 観測者のベクトル(V)
- ライトのベクトル(L)
- ライトが単位立体角あたりに放射する光束(I)
- ライトからの距離(R)
- DiffuseとSpecularのAlbedo(kd,ks)
- Blinn-PhongのBRDF関数(F(N,V,L,kd,ks))
PixelShaderで出力するべき値(Lo)は、下記の様に計算できます。
この式には3つの事象が含まれています。
- 光源の表面から自己放射のRadianceが発生
- 光源のRadianceからレンダリングする平面上でのIrradianceの計算
- 平面上のIrradianceからBRDFを用いて、観測されるRadianceの計算
ライトから始まり、ピクセルまでを、すべての値をエネルギーとして取り扱い、計算できるようになりました。