環境は光源
以前、スフィアマッピングを使って Phong の陰影付けモデルにおける鏡面反射成分を表現する例を示しました。このようにスフィアマッピングやキューブマッピングのような環境マッピングは、物体表面への映り込みを再現する手法としてだけでなく、鏡面反射によるハイライトの生成や陰影付け自体にも利用できます。
その例として、映り込みに表面材質の影響を加味した方法 [Cabral] があります。また、この環境マップを因子分解して、その陰影付けを高速に実行する方法 [Ramamoorthi] や、これに影や相互反射による影響も考慮した方法 [Sloan] (PDF) なども提案されています。
一方、環境に用いるテクスチャの画像自体も、光源として利用するために従来の 8bit 程度の量子化ではなく、実数で表現することによってダイナミックレンジの非常に大きなものが利用される [Debevec] ようになっています。
したがって、これまでの「この位置に点光源を置いて」といったような照明の設定方法に加えて、環境のテクスチャを空間に分布している光源として取り扱うという考え方が、今後は一般的なものになっていくと予想されます。実際、最近のグラフィックスハードウェアでは、このような手法の実装を容易にするような拡張が行われています。
ところで陰影付けの復習
Phong の陰影付けモデルでは、鏡面反射成分の分布を光線の反射光ベクトルと視線ベクトル(あるいは視線の反射ベクトルと光線ベクトル)をもとに決定します。

その一方、OpenGL で採用されている方法(多分、Blinn のモデルに由来していると思うけど、実は経緯をよく知らない)では、視線ベクトルと光線ベクトルとの中間ベクトルと、物体表面の法線ベクトルをもとにして、これを決定します。

どちらも似たような効果が得られるのですが、これらの右側の図にあるように、実際にはハイライトの形(鏡面反射成分の分布)が微妙に違います。
ハイライトの形
同じように陰影を求めているのに、このように見掛けが違ってくるというのは妙な話ですね。どちらの形がいいかどうかという話をする前に、それぞれの物理的な意味を考えてみましょう。
1つ目の例は視線の反射ベクトルと光線ベクトルとの内積から鏡面反射光強度を求めています。これは見方を変えれば、視線の反射ベクトル方向に明度が同心円状に分布する物体があって、それが映り込んでいる状態と等価であるとみなせます。

これに対して2つ目の例は、光源の中心位置が映り込んでいる物体表面上の位置を中心に、鏡面反射光が分布しているとみなすことができます。

ハイライトの形状が視線の方向に向かって延びている状況は、たとえば夕日が海に映えるようなシーンで目にすることができます。個人的には、ゲームでウェットな路面へのヘッドライトの移り込みがこのように再現されていなかったりすると、「けっ」と思ってしまいます。下は 2000 年度の卒業生の篠原君1が作ったサンプルムービーです。
| 正反射ベクトルを軸に分布する場合 | 中間ベクトルを軸に分布する場合 |
|---|---|
![]() |
![]() |
表面の粗さをどのようにモデル化するか2
上の2つはどちらが正しいというわけではありません。違いは物体の表面お粗さをどのように捉えるか(モデル化するか)という点にあります。そのモデルの違いがの違いがハイライト形状の差として現れているに過ぎません。
また、現実の光源は光の放射面積をもつため、本当ならハイライトの形状は、光源の形状を反映しているはずです。でも、このどちらも光源の形状は反映していません。どちらも光源を点とみなしています。それでも、一応それらしい陰影は得られます。何より陰影計算を大幅に簡略化できるというメリットの方がありがたいのです。しかし、この近似が成立しないこともしばしば発生します。「リアルなんだけど 3DCG っぽい」などと感じることがあるとすれば、多分どこかでこの近似が破綻しているのでしょう。
環境テクスチャを光源として用いる手法は、光源を点とみなすよりずっと良い近似が得られます。そしてグラフィックスハードウェアの進化によって、この計算も非常に高速に行えるようになりました。
GL_REFLECTION_MAP と GL_NORMAL_MAP
キューブマッピングでは (s, t, r) の3つの軸についてテクスチャ座標を生成しますから、テクスチャ座標を反射方向ベクトルとして生成するのか中間ベクトルとして生成するのかを glTexGeni(…, GL_TEXTURE_GEN_MODE , …) で選べるようになっています。つまり、上の2つの方法のどちらにも対応できるわけですね。
本当は今回、スフィアマッピングで Phong シェーディングする話をキューブマップに置き換えて説明する予定だったんですが、ちょっと長くなったのと絵を描くのに疲れたので、実際のプログラムに関しては次回に回します。すみません。

