しゅみぷろ

プログラミングとか

シェーディングまとめ

はじめに

 

esprog.hatenablog.com


引き続き、CGの基礎的なところをおさらいしていこうと思います。

の内容と関連がありますので参考までに。

今回はこちらの本を参考に、シェーディングについてまとめます。

シェーディングの考え方

ライティングの物理

フォトン(光子)」は振動数を$\nu$として $$E=h\nu$$ であるようなエネルギー$E$を持ちます。$h$はプランク定数です。ここでエネルギーの話をしたのは、光の強さとフォトンの持つエネルギーが密接に関わっているためです。

「光源」は連続的にフォトンを放出します。フォトンは真空中を真っ直ぐ移動しますが、原子と衝突することで以下のような影響を受けます。

  • 進行方向が変わる
  • 進行方向と振動数が変わる
  • 原子に吸収される

フォトンに色はありません。人間はフォトンの振動数に応じて色を知覚します。ここで注意すべきことは、振動数が高い(エネルギーが大きい)フォトンほど明るい色だと感じるわけではないということです。物理的なフォトンのエネルギー量のことを「物理量」、それに対して人間が感じる光の量を「測光量(心理量)」と呼び区別します。CGの場合、基礎を物理現象のシミュレーションに置くため、扱う量はほとんどの場合物理量です。

レンダリング方程式

フォトンの流れ(光の進行)は「束(フォトン束,光束)」といいます。物体表面上のある位置$p$で、ある方向(視点方向)$v$へ出て行く束を$R(p,v)$と表します。$R$のように「ある位置からある方向へ出て行く束」の量を「ラジアンス(放射輝度)」と呼びます。

f:id:es_program:20160426010751p:plain

$R(p,v)$は、位置$p$の表面が自ら発光する量$R_{\mathrm{emission}}$と、光源から受けた束を反射する量$R_{\mathrm{reflection}}$の2つの量の和で表すことができます。つまり $$R(p,v)=R_{\mathrm{emission}}(p,v)+R_{\mathrm{reflection}}(p,v)$$ です。この式は「レンダリング方程式」と呼ばれます。自然発光しない物体の表面では$R_{\mathrm{emission}}=0$です。

レンダリングの目的は、このレンダリング方程式を解くことです。すなわち、反射面上の各点におけるラジアンスを計算することが目的となります。$R_{\mathrm{emission}}$は物体固有の性質として適当な値を渡してやればいいので深入りする必要はありません。問題は$R_{\mathrm{reflection}}$で、厳密な計算は困難であるため、様々な近似が考えられています。

実際にラジアンスを計算してみましょう。簡単のために、1個の点光源がある場合の反射について考えてみます。反射面上の位置$p$から視点方向$v$に反射するラジアンス$R_{\mathrm{reflection}}(p,v)$を計算するには、光源が位置$p$から見て$l$方向にあるとして、この光源から位置$p$に降り注ぐ束を$L(p)$とします。

f:id:es_program:20160426010800p:plain

このとき、反射のラジアンス$R_{\mathrm{reflection}}$は入射する束の量$L$に比例するはずなので $$R_{\mathrm{reflection}}=kL(p) ; 0 \leq k \leq 1$$ が言えます。この式は最も単純な反射モデルの例です。$k$は反射係数で、位置$p$、光源方向$l$、視点方向$v$に依存するため $$k=k(p,v,l)$$ が言えます。

N個の光源$L_1,L_2,...,L_N$がある場合は、重ね合わせの原理が成り立つため $$ \begin{align} R_{\mathrm{reflection}}(p,v)&=k(p,v,l_1)L_1(p)+k(p,v,l_2)L_2(p)+...+k(p,v,l_N)L_N(p)\\ &=\sum_{i=1}^{N}k(p,v,l_i)L_i(p) \end{align} $$

となります。 ここまで簡単に反射のラジアンス$R_{\mathrm{reflection}}$について見てみましたが、反射すべきフォトンはいつも光源からやってくるわけではありません。大抵は何か他の物体に反射してからやってきます。これは「多重パスによる反射」と呼ばれます。多重パスによる反射をいかにうまくやり通すかがレンダリングテクニックの見せ場となります。このようなレンダリングテクニックには「レイトレーシング」、「ラジオシティ」、「フォトンマッピング」などがあります。

リアルタイムシェーディング

CGの世界で、リアルタイムで動作する(見れる程度のfpsで計算を終えることができる)シェーダープログラムのことをリアルタイムシェーダーと呼ぶことにします。このリアルタイムシェーダーを用いてシェーディングを行うことをリアルタイムシェーディングと呼びます。

リアルタイムシェーディングで使われるテクニックの一つに、多重パスによる反射計算を一切無視して可能な限り反射モデルを簡略化するものがあります。初期のシェーダー(OpenGL 1.3やDirectX 7世代のハードウェア等)では

  • 多重パスによる反射の影響は無視する
  • シャドウイングは基本的に行わない
  • 反射は「古典的ライティングモデル」によって近似する
  • シェーダーはプログラマブルでない(ハードウェアかライブラリの深部に焼き付けられる)

といった制約を課すことでリアルタイムシェーディングを実現していたそうです。ハードウェアかライブラリの深部に焼き付けられたシェーダーは「固定機能シェーダー」と呼ばれます。また、固定機能シェーダーしかサポートしないハードウェアは「固定レンダリングパイプライン」と呼ばれます。

古典的ライティングモデル

古典的ライティングモデルは、シェーディングの単純化法のひとつで、少ない計算量でそこそこ写実的なレンダリング結果を得ることができる代表的な近似法です。古典的ライティングモデルは

  • 物体に光をあてると「物体固有の色」が見える部分と「光の色」が見えるハイライト部分と分かれる
  • 物体固有の色が見える部分は見る位置を変えても明るさは変化しない
  • ハイライト部分は、見る位置を変えると移動する

というような物理的な洞察に基づいています。この現象では、物体表面では2つの異なる反射が混在していることを示唆しています。このような考え方を「2色性反射モデル」と呼びます。 物体固有の色が見える部分の反射を「ディフューズ反射(拡散反射)」、ハイライト部分の反射を「スペキュラー反射(鏡面反射)」と呼びます。古典的ライティングモデルでは、反射成分をディフューズ反射$R_{\mathrm{diffuse}}$とスペキュラー反射$R_{\mathrm{specular}}$の和に分解し $$R_{\mathrm{reflection}}(p,v)=R_{\mathrm{diffuse}}(p)+R_{\mathrm{specular}}(p,v)$$ と表します。この$R_{\mathrm{diffuse}}$と$R_{\mathrm{specular}}$をそこそこ簡単に計算しましょう、というのが古典的ライティングモデルの基本的な考え方です。

ディフューズ反射の基礎

ディフューズ反射は「拡散反射」とか「乱反射」とも呼ばれます。CGでディフューズ反射といえば、「反射するラジアンスが見る角度に依存しない反射」のことです。こもような反射を特に「ランバート(Lambert)反射」と呼びます。また、ディフューズ反射では物体固有の色が観測されるといった特徴があります。

ランバート反射モデル

f:id:es_program:20160426010816p:plain

ランバート反射では位置$p$に飛び込んだ束は、乱雑に反射され、位置$p$から均等に出て行きます。ランバート反射モデルにおける反射の強さは入射角$\phi$に依存し、$\cos\phi$に比例します。$\cos\phi$は2つのベクトル$n$と$l$の内積を用いて$<n,l>$と表現できます(ただし$n$と$l$は正規化されたベクトルです)。ランバート反射は入射する束の量$L(p)$と$<n,l>$に比例するので、比例係数を$k_{\mathrm{diffuse}}$とすると、ランバート反射は $$R_{\mathrm{diffuse}}=k_{\mathrm{diffuse}}(p)<n,l>L(p)$$ となります。実際にこれを実装する場合、内積$<n,l>$が負にならないよう $$R_{\mathrm{diffuse}}=k_{\mathrm{diffuse}}(p)max(<n,l>,0)L(p)$$ とします。

アンビエント光とアンビエント反射

光源から出た束は壁や床など、様々なオブジェクトに跳ね返されて周囲をやわらかく照らすことになります。つまり、壁や床などの光を跳ね返すオブジェクトは間接的な光源になり得ます。これらの現象を近似的に計算するために考えられたのが「アンビエント光(環境光)」と「アンビエント反射(環境反射)」です。

アンビエント光はScene全体をほのかに照らす照明です。アンビエント光はどこから光をあてるという考え方はなく、ただオブジェクト全体を均一に照らします。オブジェクトを均一に照らしても、オブジェクトによって反射率が異なるのは当然です。そこで、オブジェクトごとに固有のアンビエント反射率係数$k_{\mathrm{ambient}}(p)$を考えます。位置$p$のアンビエント反射を$k_{\mathrm{ambient}}(p)$、アンビエント光の束を$L_{\mathrm{ambient}}$とすると $$R_{\mathrm{ambient}}=k_{\mathrm{ambient}}(p)L_{\mathrm{ambient}}$$ でアンビエント光を表現できます。 ディフューズ反射モデルを考えた時のある光源からの束を$L_{diffuse}(p)$とすると、アンビエント光を考慮した反射は $$ \begin{align} R_{\mathrm{reflection}}&=R_{\mathrm{diffuse}}+R_{\mathrm{ambient}}\\ &=k_{\mathrm{diffuse}}(p)L_{\mathrm{diffuse}}+k_{\mathrm{ambient}}(p)L_{\mathrm{ambient}} \end{align} $$ となります。

グローバルイルミネーション

直接照明だけでなく、間接照明を考慮に入れた照明全体を「グローバルイルミネーション」といいます。レンダリング方程式の簡略化方法のひとつとして、Scene内のすべての反射面がランバート反射であると仮定する方法があります。この結果得られる方程式を「ラジオシティ方程式」といいます。このラジオシティ方程式を用いたレンダリング手法を「ラジオシティ」といいます。ラジオシティ法は主に「レイトレーシング」と組み合わせて利用されます。 レイトレーシング法は、最終的に視点に飛び込んでくる束(レイ)だけを考えようという発想から生まれました。視点に飛び込む束を逆向きに光源に当たるまでトレースすることで、光源から目に届く束の量を計算します。

スペキュラー反射の基礎

スペキュラー反射は、物体のつやつやした部分の反射です。鏡の反射はスペキュラー反射の一番極端な例です。スペキュラー反射の特徴は

  • 見る角度に依存する
  • 物体色ではなく光源色が見える

が挙げられます。

正反射モデル

鏡面は、反射の中でも特殊な「正反射(鏡面反射)」をします。正反射とは、角度$\phi$で入射した束がそのまま角度$\phi$で反射方向に反射されることです。

f:id:es_program:20160426010829p:plain

ここで、反射ベクトル$r$は $$\frac{r+l}{2}=<n,l>n$$ より $$r=2<n,l>n-l$$ となります。

フォン反射モデルとブリンの近似

磨かれた金属などは鏡のような正反射だけでなく、正反射方向から僅かに視線をそらしてもハイライトを観察することができます。この現象をモデル化したのがフォン(Phong)で、「フォンのスペキュラー反射モデル」として有名です。

f:id:es_program:20160426010835p:plain

フォン反射モデルの考え方は、$v=r$のとき正反射であるから、入射した束$L$に反射係数$k_{\mathrm{specular}}$を掛けた$k_{\mathrm{specular}}L$が反射されるというものです。 一方、$v \neq r$の時は、視点方向$v$が正反射$r$にどれだけ近いかで反射の強さが異なります。ベクトル$v$とベクトル$r$のなす角を$x$とすると、$x$が大きくなるにつれて急激に反射は減る筈です。

フォンはスペキュラー反射が$\cos ^{S}x$に比例して減衰すると考えました。$S$は「シャイニネス」または「鏡面反射指数」という量で、素材ごとにそれらしく見えるように設定する量です。完全な鏡面反射の場合$S \to \infty$です。内積の定義から $$\cos x=<r,v>$$ なので、フォン反射モデルは $$R_{\mathrm{specular}}(p,v)=k_{\mathrm{specular}}(p)<r,v>^{S}L(p)$$ となります。フォンの反射モデルは物理的根拠を持たず、しかも物理的には間違っています。しかし、スペキュラー反射をうまくモデル化できる上に、後述するハーフベクトルを用いた簡易計算法があることと相まって、長く使われています。

先の計算$<r,v>$は $$<r,v> = <(2<n,l>n-l),v>$$ なので、まず内積$<n,l>$を計算し、2倍してベクトル$n$の各成分に掛け、ベクトル同士の引き算を行い、もう一度内積を計算する必要があります。計算機の処理性能が低い頃にはさけたい演算でした。フォンの反射モデルには次のような簡易計算法があります。まず $$h=\frac{v+l}{||v+l||}$$ という「ハーフベクトル」$h$を用意します。$h$は視線方向$v$と入射方向$l$の中間の方向を向いていて、ノルムを1に正規化されています。正規化を $$\mathrm{normalize}(a)=\frac{a}{||a||}$$ としておくと、ハーフベクトルは $$h=\mathrm{normalize}(v+l)$$ と表現できます。 簡易計算法では、スペキュラー反射を次のように計算します。 $$R_{\mathrm{specular}}(p,v)=k_{\mathrm{specular}}(p)<n,h>^{S}L(p)$$

これはフォンの考えたモデルとは異なりますが、レンダリング結果はかなりフォンのモデルに近くなります。この式は「ブリン(Blinn)の近似」、として知られています。

フォン/ブリンのモデルで異なるのは内積の計算で $$<(2<n,l>n-l),v> \to <n,\mathrm{normalize}(v+l)>$$ という風に変化しています。

また、これらを実装する際には内積が負にならないよう気をつける必要があり、次のようにします。 $$R_{\mathrm{specular}}(p,v)=k_{\mathrm{specular}}(p)\max(<r,v>)^{S},0)L(p)$$ $$R_{\mathrm{specular}}(p,v)=k_{\mathrm{specular}}(p)\max(<n,h>^{S},0)L(p)$$

金属の反射モデル

フォンの反射モデルでは、金属質の反射をうまく再現できません。そこで、フォンの反射モデルの改良版がいくつか提唱されています。これらの改良モデルの共通点は、フォン/ブリンの反射モデルに現れる$<n,h>^{S}$の項を物理的に意味のある量に置き換えようとすることです。そもそも$<n,h>^{S}$の項は、ハイライト(鏡面反射)のボケを表す項です。このボケがどこからくるのかといえば、物体表面が鏡のようにツルツルしているわけではなく、微細な凸凹があるためです。この微細な凸凹を構成する微小な面ひとつひとつのことを「マイクロファセット」と呼びます。マイクロファセットが色々な方向を向いているから、ハイライトがボケるわけです。ブリンは以上のような仮定から、フォンの反射モデルを次のように書き換えることを提唱しました。 $$R_{\mathrm{specular}}(p,v)=k_{\mathrm{specular}}(p) \frac{D(<n,h>)F(<n,l>, η )G(n,v,h,l)}{<n,v>} L(p)$$ $D$は「マイクロファセット分布関数(NDF:Normal Distribution Function)」、$F$は「フレネル(Fresnel)反射係数」、$G$は「幾何学的減衰係数」です。分母の$<n,v>$は視点から見たマイクロファセットの濃度を正規化する係数です。

マイクロファセット分布関数$D$は、マイクロファセットが面の法線$n$に対してどの程度ズレて分布しているのかを記述する関数です。関数$D$は様々なものが提案されていますが、最も有名なのはクックとトランスの $$D(d)=\frac{1}{S^{2} \cos^{4} d} \exp \left(-\left(\frac{\tan d}{S}\right)^{2} \right)$$ です。ここで、$d$には$<n,h>$を代入することになります。$S$は面の荒さを決める定数で、小さいほどシャープなハイライトになります。この関数$D$は「ベックマン(Beckmann)分布」として知られています。 一方、「トランススパロウのモデル」として知られている反射モデルでは、関数$D$として $$D(d)=S'\exp\left(-(S''d)^{2}\right)$$ が使われます($S'$と$S''$は定数)。この関数$D$は「ガウス(Gauss)分布」です。 なお、ブリンはもともとマイクロファセット分布関数として $$D(d)=\left(\frac{S'''^{2}}{(S'''^{2}-1)\cos^{2}d+1}\right)^{2}$$ を提案しました($S'''は定数$)。この関数$D(d)$は楕円を表し、定数$S'''$は楕円の長軸と短軸の比です。

幾何学的減衰係数$G$は、マイクロファセットが他のマイクロファセットの反射の邪魔をする効果を表します。

f:id:es_program:20160426010855p:plain

反射光が他のマイクロファセットに遮られる場合(ベクトル$v$がベクトル$n$にほぼ垂直の場合)の効果は $$G_{out}(n,v,h)=2\frac{<n,h><n,v>}{<v,h>}$$ となります。一方、入射光が他のマイクロファセットに遮られる場合(ベクトル$l$がベクトル$n$にほぼ垂直の場合)の効果は $$G_{in}(n,v,h,l)=2\frac{<n,h><n,l>}{<v,h>}$$ となります。 これらトータルでの影響は、反射光がマイクロファセットに遮られるか、入射光がマイクロファセットに遮られるか、あるいは全く遮られないかであるので $$G(n,v,h,l)=\min(G_{in},G_{out},1)$$ です。

フレネル反射係数$F$は、フレネルの反射の法則から導かれる係数です。フレネルの反射の法則は、なめらかで均質な金属表面でフォトンが正反射方向にどの程度の割合で反射されるのかを調べた結果をまとめた法則です。反射されなかったフォトンは吸収されます。入射角が$\phi$のとき、フレネル反射係数$F$は $$F(\phi,η)=\frac{(\zeta -c)^{2}}{(\zeta +c)^{2}}\left(1+\frac{(c(\zeta +c)-1)^{2}}{(c(\zeta -c)+1)^{2}}\right)$$ $$c=\cos \phi$$ $$\zeta=\sqrt{η^{2}+c^{2}-1}$$ で与えられます。ηは材質毎によって異なる定数で、「複素屈折率」と呼ばれる複素数です。

シュリック(Schlick)は、フレネル反射係数$F$を次のように近似できることを発見しました。 $$F(\phi)\simeq F_{0}+(1-F_{0})(1-\cos\phi)^{5}$$ ここで、$F_{0}$は垂直入射時($\phi=0$)のフレネル係数の実部です。

カラー

人間の視覚はL(Long)、M(Middle)、S(Short)の3周波にそれぞれ反応する細胞から成ります。CGではこの3種から代表的な周波数をそれぞれひとつずつ選び(光の3原色)、3周波数のみについて計算することがほとんどです。Lは赤、Mは緑、Sは青(正確には藍に近い)に相当します。

物体に固有の反射係数$k_{\mathrm{diffuse}}$と$k_{\mathrm{specular}}$のうち、ランバート反射で登場する$k_{\mathrm{diffuse}}$は振動数に依存する関数です。つまり $k_{\mathrm{diffuse}}=k_{\mathrm{diffuse}}(\nu)$ が言えます。ディフューズ反射では、ある特定の周波数帯を選んでフォトンを反射し、それ以外の周波数帯のフォトンは吸収されます。一方、スペキュラー反射に登場する$k_{\mathrm{specular}}$はあまり振動数に依存しません。これが物体色と光源色がそれぞれ現れるといった違いの由来になります(という風に2色性反射モデルでは解釈します)。

カラーの指定は一筋縄にはいきません。「Aさんの想像する赤」と「Bさんの想像する赤」が違うといったことは日常的にしばしばあることです。CGでも同じことで、「R=1, G=0, B=0」としたところで、ディスプレイ毎に表示されるRの色合い(分光放射率)が異なるからです。

カラーを絶対的に指定するひとつの方法は、「Rといったら440[THz]のフォトンのことだ」といったような取り決めをしておくことです。こういった取り決めには「sRGB」や「NTSC」、「Adobe-RGB」などがあります。

一般的な反射の基礎

ここで述べる一般的な反射は、ディフューズ反射やスペキュラー反射を含みます。これから述べる「双方向反射率分布関数(BRDF)」は計算コストが高く、工夫しなければリアルタイム演算向きではありません。また、「双方向表面化散乱反射率分布関数(BSSRDF)」もリアルタイム演算向きではありません。しかし、この2つの考え方は数多くのリアルタイムCGでの基礎として使われています。工夫次第で、リアルタイムでそれらしく見せることが可能です。

反射モデルのおさらい

先ほど紹介したディフューズ反射とスペキュラー反射についておさらいしておきます。使用したベクトルの定義は

ベクトル意味
$p$ 反射を考えている位置
$l$ $p$から見た光源の方向。ノルムは1
$v$ $p$から見た視点の方向。ノルムは1
$R(p,v)$ $p$から方向$v$への反射のラジアン
$L(p)$ $p$から方向$l$にある点光源から$p$へ降り注ぐ入射のラジアン
$n$ $p$における反射面の法線方向。ノルムは1
$r$ $p$に方向$l$から光が入射した場合の正反射方向。ノルムは1

でした。これまで考えた反射は

  • ランバート反射 $$R_{\mathrm{diffuse}}(p)=k_{\mathrm{diffuse}}(p)<n,l>L_{\mathrm{diffuse}}(p)$$
  • スペキュラー反射 $$R_{\mathrm{specular}}(p,v)=k_{\mathrm{specular}}(p)<r,v>^{S}L_{\mathrm{specular}}(p)$$

の2つでした(リアルタイムシェーディングの場合、ディフューズ反射計算用の光源とスペキュラー反射計算用の光源は別々に設定することが多いので$L_{\mathrm{diffuse}}$と$L_{\mathrm{specular}}$で分けてあります)。 上記2つの反射にアンビエント反射 $$R_{\mathrm{ambient}}(p)=k_{\mathrm{ambient}}(p)L_{\mathrm{ambient}}$$ を加えて $$R_{\mathrm{reflection}}=R_{\mathrm{diffuse}}+R_{\mathrm{specular}}+R_{\mathrm{ambient}}$$ としたのが、固定機能シェーダーのサポートする反射モデルです。

ラジアン

位置$p$から方向$l$を見たときの入射してくるラジアンスが$I(p,l)$であるような光源分布を考えてみます。この光源分布関数$I(p,l)$を位置$p$における「ラジアン」と呼びます。

f:id:es_program:20160426010910p:plain

式の上では、反射のラジアンス$R(p,v)$と入射のイラジアンス$I(p,l)$は同じ形をしていますが、意味は大きく異なります。ラジアンス$R(p,v)$については特定の方向$v$についてのみ興味があったのに対し、イラジアンス$I(p,l)$についてはあらゆる方向(イラジアンスを考える場合$l$は半球上を動く)について興味があります。

ラジアンス$I(p,l)$を記述するのは大変なので、CGではよく「環境キューブマップ」と呼ばれる手法で近似します。環境キューブマップは立方体で天空を与えてやり、すべての位置$p$から見たイラジアンスが同一であると仮定します。

双方向反射率分布関数(BRDF)

双方向反射率分布関数(BRDF:Bidirectional Reflectance Distribution Function)」は、反射率の一般化です。

ある位置$p$で、ある方向$l$から入射する束が、ある方向$v$に反射するとき、この時の反射係数を$k'(p,v,l)$とします。反射の係数は、入射する向き$l$と反射面の法線$n$の角度$\phi$の余弦に比例するので、この余弦項を$<n,l>$で表し $$k(p,v,l)=\frac{k'(p,v,l)}{<n,l>}$$ という関数$k$を定義します。この関数$k$がBRDFです

この定義から、もし天空に点光源が1個だけあり、その方向が$l$、光源から位置$p$に降り注ぐ束を$L(p)$とすると $$R_{\mathrm{reflection}}(p,v)=k(p,v,l)<n,l>L(p)$$ が言えます。光源が天空にN個あるとすると $$R_{\mathrm{reflection}}(p,v)=\sum_{i=1}^{N}(p,v,l_{i})<n,l_{i}>L_{i}(p)$$ であることまでは、ランバート反射モデルやフォン反射モデルの場合と同じです。

BRDFを考える時は、天空全体が光源であると考える(イラジアンスを考える)ことが普通です。 天空全体を考える場合は総和$\sum$の代わりに半球全体にわたる積分$\int_{\mathrm{hemisphere}}$を用いて $$R_{\mathrm{reflection}}(p,v)=\int_{\mathrm{hemisphere}}k(p,v,l)<n,l>I(p,l)dl$$ と計算します。 この式は球面積分に関する式変換によって $$R_{\mathrm{reflection}}(p,v)=\int_{0}^{\frac{\pi}{2}}\int_{0}^{\frac{\pi}{2}}k(p,v,(\theta,\phi))<n,(\theta,\phi)>I(p,(\theta,\phi))\sin\phi ~~ d\theta d\phi$$ のように書き直すことができます。 ここで、$(\theta,\phi)$は方位角$\theta$と仰角$\phi$でノルムが1のベクトル($l=(\theta,\phi)$)です。

BRDF $k$は材質ごとに異なります。再現したい材質の$k$は本物から計測し、適当な関数で近似されるのが一般的です。ちなみにランバートのBRDFは $$k(p,v,l)=k_{\mathrm{diffuse}}(p)$$ です。反射率が$k_{\mathrm{specular}}(p)$である完全な鏡面ではBRDFは $$k(p,v,l)=2k_{\mathrm{specular}}(p)\delta(\sin^{2}v_{\phi}-\sin^{2}l_{\phi})\delta(v_{0}-l_{0}\pm \pi)$$ です。ただし $$v=(v_{\theta},v_{\phi}),l=(l_{\theta},l_{\phi})$$ として、それぞれのベクトルの方位角、仰角を表し、関数$\delta$はディラックデルタ関数 $$ \delta(x)=\left\{ \begin{array}{rcl} 0 & (x \neq 0) \\ \infty & (x=0) \end{array} \right. $$ $$\int_{-\infty}^{\infty}\delta(x)dx=1$$ としています。

高速なBRDFシェーディング

先に述べたイラジアンスを考慮した反射 $$R_{\mathrm{reflection}}(p,v)=\int_{\mathrm{hemisphere}}k(p,v,l)<n,l>I(p,l)dl$$ はリアルタイムではまず解けません。計算を高速に行うためには以下が必要になります。

  • 結果の質を出来るだけ落とさずに途中の計算だけ減らす
  • 出来るだけ簡単なアルゴリズムで計算する

球面調和変換」と呼ばれるテクニックは、上記の2つをかなり満足させるものです。

$k(p,v,l)<n,l>$を関数$T$で表すとします($T$は入射角を考慮したBRDFという意味合いになります)。$T$は位置$p$、反射方向$v$、入射方向$l$の関数なので $$T=T(p,v,l)$$ です。イラジアンスを$I$、反射のラジアンスを$R$としてイラジアンスを考慮した反射の式を簡単に表すと $$R=\int TI ~ dl$$ です。ここで $$ \begin{align} \breve{x}_{l}^{m}&=SH\left[x(v)\right]_{l}^{m}\\ &= \int_{\mathrm{sh}} x(v) ~ y_{l}^{m}(v) ~ dv ; \hspace{15pt} (0 \leq l,-l \leq m \leq l) \end{align} $$ とします。 演算$SH$が球面調和変換で、関数$y_{l}^{m}$は「(正規化)球面調和関数」と呼ばれます。大雑把な言い方をすると、平面のフーリエ変換を球面にしたものが球面調和変換です。 量$\breve{x}$は関数$x$の「球面調和展開係数」と呼ばれます。 球面調和変換を使う効果は、以下の式の関係にあります。 $$ \begin{align} SH\left[\int TI ~ dl\right] &= \sum_{l} \sum_{m} SH\left[T\right]_{l}^{m} SH\left[I\right]_{l}^{m}\\ &=\sum_{l}\sum_{m}\breve{T}_{l}^{m}\breve{I}_{l}^{m} \end{align} $$ BRDF積分は実数の和に置き換えられます(計算も$n=5$程度で打ち切って良い)。これは「逆球面調和変換」で元のラジアンスに戻すことができます。球面調和展開係数$\breve{x}$から元の関数$x$は $$ \begin{align} x(v)&=SH^{-1} [\breve{x}] (v)\\ &=\sum_{l=0}^{n-1} \sum_{m=-l}^{l} \breve{x}_{l}^{m} y_{l}^{m} (v) \end{align} $$ で復元することができます。

双方向表面下散乱反射率分布関数(BSSRDF)

双方向表面下散乱反射率分布関数(BSSRDF:Bidirectional Subsurface Scattering Reflectance Distribution Function)」は表面化散乱反射を扱う反射モデルです。ほとんどの反射はBSSRDFを考えれば充分であることが経験的に知られています。

f:id:es_program:20160426010920p:plain

BSSRDFを考える場合、入射点と反射点が異なるケースを考慮する必要があります。注目する点を$p$とすると、$p$付近の入射点$q_{i}$からの反射を全て勘定に入れなければいけません。そこで、次のような面積分$\int_{\mathrm{area}}$を行うことになります。 $$R_{\mathrm{reflection}}(p,v)=\int_{\mathrm{area}}\int_{\mathrm{hemisphere}} k_{\mathrm{BSSRDF}}(p,g,v,l)I(q,l) ~ dldq$$ この関数$k_{\mathrm{BSSRDF}}$がBSSRDFです。