last modified: 2024-03-11 02:02:15
このページではMathJaxによる数式表示を行っています。対応しているブラウザーであれば少し待てば数式が表示されます。
(画面左下にMathJaxのタイプセットステータスが表示されます。)
環境によっては表示が崩れているかもしれません。その際はブラウザーを変更してみてください。

スペクトラルレンダリング (Spectral Rendering)

多くのコンピューターグラフィックスはRGB三原色で表現・計算されていますが、より物理的に正しい光輸送計算を行うには「スペクトラムとRGBの関係」で解説したようなスペクトラムを考慮したレンダリングを実装する必要があります。ここでは、スペクトラムを考慮したレンダリング方程式やスペクトラルレンダリングを実装する際の注意点などについて書きます。

レンダリングにおけるRGB・スペクトラム表現の違い

RGB三原色による色の表現はコンパクトかつ、人間にとって知覚可能な色ほぼ全てを表現できるため非常に便利です。結果ほとんどのディスプレイはRGB三原色による色の再現を行っており(少なくとも入力するデータはRGB、もしくはRGBに準ずる形式)、コンピューター上で扱う画像もRGB画像ばかりです。
コンピューターグラフィックスの世界でも、多くのレンダリングシステムはRGB三原色を基にした3Dモデルデータを入力とし、計算過程から結果出力に至るまでRGBを使用しています。計算をRGBで行い具体的なスペクトラムを考えないレンダリングをRGBレンダリングと呼んだりします。RGBレンダリングで生成される画像は十分に美しく、問題の無いように見えますが、物理的な正しさという観点で見てみると問題があります。これに対してスペクトラムを考慮した計算をスペクトラルレンダリング(spectral rendering)と呼び、その計算結果はRGBレンダリングによるものとは異なります。 RGB/スペクトラルレンダリングの違いを図1に示します。

スペクトラルレンダリングとRGBレンダリング
図1. スペクトラルレンダリングとRGBレンダリング

物体の色は基本的に光源のスペクトラム $ I(\lambda) $ と物体の反射スペクトラム $ \rho(\lambda) $ の積 $ I(\lambda) \rho(\lambda) $ によって表されます。スペクトラルレンダリングでは、基本的にこれらの関係をそのままの形で扱います。つまり、光源色や物体の反射率といった入力データをスペクトラムデータで与えます。そして反射光もスペクトラムの上で計算し、最終的にディスプレイに出力する段階で、等色関数を用いてRGBに変換します。一方RGBレンダリングでは、光源色、物体の反射率などを最初からRGBで与える、もしくは既知のスペクトラムデータを等色関数(など)で予めRGBに変換したもので与えます。反射光もRGBで表されるため、ディスプレイへの出力も基本的にそのままで行えます(ガンマ補正などはありますが)。
ここで問題となるのが、前者の結果 $ (R, G, B) $ と後者の結果 $ (R', G', B') $ が異なるということです。現実世界で人間の目に至る光は当然前者に従っているため、RGBレンダリングは不正確な計算を行っていることになります。また、反射率など直接的に色に関わるものならともかく、例えば屈折率などの波長の関数はどのようにしてRGBに変換するのが良いのか明らかではありません。

スペクトラルレンダリング

この節では、スペクトラルレンダリングを考慮するにあたって解くべき問題や、実装する際の注意点などについて書きます。

レンダリング方程式

スペクトラルレンダリングを考えるにあたって解くべき問題は、基本的には「レンダリング方程式1」で紹介したレンダリング方程式から変わりません。ただし、明示的に波長成分を考えることで変数 $ \lambda $ が増えて以下の形になります。 \begin{equation*} {L_\lambda}_o(\vx, \vomega_o, \lambda) = {L_\lambda}_e(\vx, \vomega_o, \lambda) + \int_{\mathcal{S}^2} {f_s}_\lambda(\vx, \vomega_i, \vomega_o, \lambda) {L_\lambda}_i(\vx, \vomega_i, \lambda) \absb{\vomega_i \! \cdot \vec{n}} \d\vomega_i \end{equation*} 放射輝度やBSDFに $ \lambda $ が付加されているのがわかると思います。前者は分光放射輝度(spectral radiance)と呼び記号は $ L_\lambda $ で表し、単位は $ \mathrm{[W \cdot m^{-2} \cdot {sr}^{-1} \cdot {nm}^{-1}]} $ です。後者は分光BSDF(spectral BSDF)と呼びますが、ここでは光の波長が反射・透過時に変わらないと考えているため単位は通常のBSDFと同じ $ \mathrm{[{sr}^{-1}]} $ です。

レンダリング方程式2」で紹介した経路積分形式のレンダリング方程式への変形も同様に可能です。ただし波長を追加で考慮しているので、経路空間上で積分した結果そのままではまだ波長の関数です。等色関数などの応答関数をかけてさらに積分することで値として結果が求まります。例えばX成分の計測値は以下の式で表されます(経路の変数の文字がXの等色関数の文字とかぶってしまうので、前者を $ \bar{p} $ としています)。 \begin{equation*} I_{j, X} = \frac{1}{\int_{\Lambda} \bar{y}(\lambda) \d\lambda} \int_{\Lambda} \bar{x}(\lambda) \int_{\Omega} f_j(\bar{p}, \lambda) \d\mu(\bar{p}) \d\lambda \end{equation*}

※蛍光を考慮したレンダリング方程式

まず始めに、この節は筆者が勝手に考えているだけなので間違ったことを書いている可能性が他より高くなります。参考文献もありません。
蛍光(fluorescent)物質は、電磁波を吸収して特定の波長分布で発光します。発光の強度は吸収した電磁波の量によって変わります。紫外線や赤外線といった不可視光も有効なエネルギー源となるので、発光する波長領域が可視光であれば、周囲の可視光環境に対しては不自然に明るく(むしろ自発光しているようにも)見えます。蛍光インクなどが明るく見えるのも名前の通り蛍光現象によります。
蛍光現象によって発せられるスペクトラムは入射光のスペクトラムに依存せず発光までの時間差も極僅かなため、ある波長の光が反射時に波長を変えたようも見えます。これをBSDFの一部としてモデル化することが考えられます。その一般化したBSDF $ {f_s}'_\lambda $ は次のように表されます。 \begin{eqnarray*} {f_s}'_\lambda(\vx, \vomega_i, \vomega_o, \lambda_i, \lambda_o) = \frac{\d^2 {L_\lambda}_r(\vx, \vomega_o, \lambda_o)}{\d {E_\lambda}_i(\vx, \vomega_i, \lambda_i) \d\lambda_i} = \frac{\d^2 {L_\lambda}_r(\vx, \vomega_o, \lambda_o)}{{L_\lambda}_i(\vx, \vomega_i, \lambda_i) \absb{\vomega_i \! \cdot \vec{n}} \d\vomega_i \d\lambda_i} \end{eqnarray*} ここで、分母の $ {E_\lambda}_i $ は分光放射照度で単位は $ \mathrm{[W \cdot m^{-2} \cdot {nm}^{-1}]} $ です。ある方向 $ \vomega_i $、ある波長 $ \lambda_i $ による微小放射照度に対してどれだけの分光放射照度が反射(透過)されるかを表しています。結果として単位は $ \mathrm{[{sr}^{-1} \cdot {nm}^{-1}]} $ となります。非蛍光物質のBSDFでは、ある波長が別の波長の反射光に影響を与えないため、この枠組みでは次のように通常のBSDF $ f_s $ とデルタ関数との積で表されます。 \begin{equation*} {f_s}'_\lambda(\vx, \vomega_i, \vomega_o, \lambda_i, \lambda_o) = \delta(\lambda_i - \lambda_o) \; {f_s}_\lambda(\vx, \vomega_i, \vomega_o, \lambda_o) \end{equation*} この一般化されたBSDFを使って蛍光現象を考慮したレンダリング方程式を考えてみると、次のようになります。 \begin{equation*} {L_\lambda}_o(\vx, \vomega_o, \lambda_o) = {L_\lambda}_e(\vx, \vomega_o, \lambda_o) + \int_{0}^{\infty} \int_{\mathcal{S}^2} {f_s}'_\lambda(\vx, \vomega_i, \vomega_o, \lambda_i, \lambda_o) {L_\lambda}_i(\vx, \vomega_i, \lambda_i) \absb{\vomega_i \! \cdot \vec{n}} \d\vomega_i \d\lambda_i \end{equation*} 蛍光による発光を反射の一部としてモデル化しているため、反射光のある波長の分光放射輝度を求めるには、全波長域に関して積分する必要があります。ちなみに蛍光を発光側でモデル化すると以下のようになると思います。 \begin{eqnarray*} {L_\lambda}_o(\vx, \vomega_o, \lambda_o) &=& \Brk{{L_\lambda}_e(\vx, \vomega_o, \lambda_o) + \int_{0}^{\infty} \int_{\mathcal{S}^2} F(\vx, \vomega_i, \vomega_o, \lambda_i, \lambda_o) {L_\lambda}_i(\vx, \vomega_i, \lambda_i) \absb{\vomega_i \! \cdot \vec{n}} \d\vomega_i \d\lambda_i} + \\ && \hspace{5mm} \int_{\mathcal{S}^2} {f_s}_\lambda(\vx, \vomega_i, \vomega_o, \lambda_o) {L_\lambda}_i(\vx, \vomega_i, \lambda_o) \absb{\vomega_i \! \cdot \vec{n}} \d\vomega_i \end{eqnarray*} ここでは分光BSDFはそのままにしておき、波長 $ \lambda_i $ の入射光による波長 $ \lambda_o $ の反射光への寄与を関数 $ F $ で表しています。
燐光(phosphorescence)現象を考える場合はさらに時間の積分も増えます。

スペクトラムデータの表現

離散近似
(a) 離散近似
折れ線連続関数近似
(b) 補間による連続関数近似
基底関数近似
(c) 基底関数近似
図2. スペクトラムの表現方法

スペクトラルレンダリングを実現するにあたって、光源の放射や物体の反射率のスペクトラムを入力データとして与える必要があります。また、計算途中や結果のデータをどういう形式で保持・保存するかも考慮する必要があります。スペクトラムデータの表現方法には次のようなものが考えられます。

離散表現

ある波長範囲を一定間隔でサンプリングした値、もしくは一定間隔毎の平均値などでスペクトラムを離散的に表現します。

  • RGBレンダリングで3要素分計算していたものが増えるだけなので、簡単な拡張で実装できる。
    入力、計算途中、計算結果のデータすべてに使用可能。
  • 輝線を含んだスペクトラムを正確に表現するにはサンプル間隔を非常に細かく設定する必要がある。

任意のサンプル点間の補間による連続関数表現

スペクトラム形状に応じて任意にサンプル点をとり、サンプル点間を線形補間(や二次、三次補間)することで連続関数を表現します。

  • あらゆるスペクトラムを素直に表現できる。輝線も容易に表現可能。
  • スペクトラムの和や積を考えるのが難しいため、波長次元に関してもモンテカルロ積分などを行う必要がある。
    その場合、計算途中データは波長次元をポイントサンプリングしたスペクトラム、計算結果は上記の離散表現になるのが典型的。
    基本的に入力データ専用の表現と考えられる。

基底関数の和による連続関数表現

スペクトラムを基底関数の和によって連続関数表現します。保持するデータは各関数の係数となります。

  • シーン毎に適した基底関数を選ぶことで少ないデータ量でスペクトラム情報を保持できる。逆に言えばシーンごとに適切な基底関数を選ぶ必要があり、それはレンダリングしてみないとわからないことが多い。
  • 積の計算が難しい。光の分散現象などの取り扱いにも難がある。

※ポイントサンプリングされたスペクトラム

連続関数によるスペクトラムをある(複数の)波長でサンプリングした点における値(の集合)。

  • 基本的に計算途中データ専用の表現と考えられる。
  • 和や積の演算が行われるスペクトラム間で波長と波長数が統一されてさえいれば、波長数自体は自由に変更できる。

離散表現:代表値か積分値か

スペクトラムを離散表現で表す場合、実装に関して注意点があります。
離散表現では、サンプル点の値を用いる方法と、小区間毎に積分した値を用いる方法が考えられますが、後者に関して2通りの表現を考えることができます。1つ目は積分値をそのまま用いる場合、2つ目は小区間の長さで積分値を割ることによって求められる小区間の平均値です。反射・透過率は0〜1の範囲の値で無いとわかりづらいため、後者の表現を使うと思いますが、光源のスペクトラムなどはどちらも使うことも考えられます。光源の出力を例にとれば、前者の表現なら単位は $ \mathrm{[W]} $、後者の表現なら $ \mathrm{[W \cdot {nm}^{-1}]} $ となり値も当然異なるものとなります。連続関数表現の場合やサンプル値の場合の単位も $ \mathrm{[W \cdot {nm}^{-1}]} $ です。
これらの違いを忘れて実装してしまうと当然計算結果は間違ったものとなり、主に明るさが間違った結果となるでしょう。ここでは離散表現の場合に注意する点と書きましたが、折れ線による連続関数などによって入力データを表現し、波長次元でもモンテカルロ積分を行う場合も同様の注意が必要です。そのような場合でも計算結果は画像次元におけるピクセルのように、波長次元における一定間隔の"箱"を用意することになると思います。その箱に入った離散的なスペクトラムと等色関数の積を考える場合に上記の考慮が必要です。

RGB画像への変換

スペクトラルレンダリングで画像を生成すれば、各ピクセルの値がスペクトラムで表されたデータが手に入ります。それをRGBのディスプレイで見る場合にはRGB画像に変換する必要があります。等色関数を用いて一旦XYZ色空間のデータに変換した後、表示するディスプレイに応じて適切な変換行列を用いてRGB画像に変換(さらにガンマ補正)するのが良いでしょう。
ちなみに全波長同じ値のフラットなスペクトラム(xy色度図の座標は1/3, 1/3)を、D65に白色点を持つ一般的なsRGB色空間に変換すると、輝度が $ 1 $ なら $ (1.205, 0.9484, 0.9086) $ となりRGBの数値としては赤が強くなります。この値を最大値が1になるようスケーリングした上でガンマ補正、最大値255となるようにすると(255, 229, 225)になります。(   こんな色   )フラットなスペクトラムは(周りの環境や見慣れた白にもよりますが)結構赤く感じるかもしれません。

参考文献

  • [Radzisweski2009] M. Radzisweski, K. Boryczko, W. Alda - "An Improved Technique for Full Spectral Rendering", 2009

Today : 00000064 Total : 00314985
Copyright © 2024 shocker.