2016.02.03

レイキャスティング

最も単純な3次元図形「球」を対象として, レイキャスティング法によるレンダリングプログラムを作成する. シェーディング(陰付け)とシャドウイング(影付け)を実装し, Fig.1 のように(ある程度)リアルな画像を生成することを目標とする.

要するに,POV-Ray の劣化版を作るんだ.

なお,Fig.1 では, 拡散反射率や鏡面反射率の調整によって, 質感の異なる球体が表現されている.

Fig.1. レイキャスティング法による球体のレンダリング例

Fig.1 では,手前がプラスチック的,奥が金属的な物質のつもり...

光線と物体の交差計算

レイキャスティング法(ray-casting)では, ある点から発射された光線(照明光線,視線)を追跡し, どれかの物体との衝突を検出する. 実際には,次の2種類の処理で利用される:

なお,POV-Ray などの実用的な CG ソフトウェアで採用されている レイトレーシング法(ray-tracing)では, 現実の光学現象をより忠実に考慮し, 衝突後にさらに反射・屈折した光線をも再帰的に追跡する. が,その基本の処理はレイキャスティングである.

ここでは,簡単のため物体の形状を球に限定し, レイキャスティングをベクトル的に解析する. Fig.2 のような配置を考えよう.

Fig.2. 球体のレイキャスティング

各変数の意味は次の通り:

<B><I>P</I></B><SUB>0</SUB>: 球の中心点の位置ベクトル,      <I>R</I>: 球の半径
<B><I>P</I></B><SUB>1</SUB>: 光線の始点の位置ベクトル,      <B><I>D</I></B>: 光線の速度ベクトル
<B><I>P</I></B>(<I>t</I>): 交点の位置ベクトル,        <I>t</I>: 光線が交点に到達するのにかかる時間

また,光線と交点,球と交点との関係は,それぞれ,次のようになっている:

P(t) = P_1 + D t    :光線の方程式(直線運動の方程式)

R = |P(t) - P_0|    :球面の方程式

これらの2式から P(t) を消去すると, 次の二次方程式が得られる:

この方程式を解いて t を求めれば, 交点 P(t) を算出できる. なお,交点の個数は1個とは限らないことに注意しよう.

二次方程式の解の個数は,2個または1個または0個だった.

以下,簡単のため方程式を次のように置き換えて解説する:

ただし,

  


シェーディング

シェーディング(shading)では, 視線方向(の逆方向)への反射光の強さを求める. この処理も,ベクトル演算によって計算される. ベクトルの配置を Fig.3 に示す.

Fig.3. 光線等のベクトル配置

入射光線 L と 視線 V の方向に注意. 光線の進行方向とは逆向きに定義されている.

また,各変数の記号も違うかもしれない. 適宜,置き換えて考えよう.

以上の反射光強度の合計 LaLdLs が シェーディングの結果となる.

ここで示したベクトル計算は複雑すぎる... 簡単化のため,次のように対処することがよくある:


デプスバッファ法による隠面処理

以前利用した BSP-tree 法では, ポリゴンモデルの隠面処理を高速に実現していた. しかし,この方法は曲面体に対してはうまく適用できない. そこで今回は,デプスバッファ法(depth-buffering)を利用する.

デプスバッファは,画像の一種であり, 色情報の代わりに深度(視点から物体表面までの距離) を画素値としている.

ちなみに,通常の出力画像は,フレームバッファと呼ばれる.

デプスバッファ法のアルゴリズムは次の通り:

これで,木生成やソートなどの前処理を施さなくても,隠面処理を実現できる. 教科書 p.107 および配布資料 p.66 の「Zバッファ法」を参照せよ.

アルゴリズム的には,力ずく(非効率)な方法ではあるが, 単純なので,現代の GPU(CG 専用のプロセッサ)を使えば, ハードウェア的に(高速に)処理できる. ただし,今回は,GPU の機能は使わず,ソフトウェア的に処理してみよう.

基本プログラム

ダウンロードして実行してみよう:

この基本プログラムでは, 環境光と拡散反射のシェーディングのみが実装されている. 鏡面反射およびシャドーイングについては実装されていない. (これが本日の課題.)

なお,ベクトル演算の注意点として, 計算しようとしている量が ベクトル or スカラのどちらであるのか? 見極めることが非常に重要である. たくさんの関数の中から,適切な関数を選び出すこと.


シャドーイング

基本プログラムでは, 照明光が物体を通過して他の物体を照らしている状況であった. しかし,現実の世界では, 光は最初に交差した物体によってさえぎられ, そこに影が生じるハズである. 余裕のある者は, この現象をプログラムに実装してみよう.

基本的なアルゴリズムを示す:

  1. 注視点(視線と物体の交点)の位置ベクトル Plookat を求める.
  2. これが交差計算における始点 P1 に対応する. Fig.2 を参照せよ. なお,このベクトルの計算については, ビューイング処理の結果として既に求められているハズであり, 基本プログラム raycast.c でも実装済み.
  3. 照明光の方向ベクトル L逆ベクトル L' = −L を求める.
  4. これが交差計算における速度 D となる. 逆方向にする理由は,この光線の始点が光源ではなく,物体であるためだ.
  5. すべての物体に対して (注視している物体以外に対して) 交差計算を実行する.

要するに,視点の代わりに注視点, 視線の代わりに照明光線を使って交差判定すればよい. 他の物体との間に交差があれば, その注視点は影領域内ということになる.

なお,自分自身を検査対象から外すのは, 注視点の計算結果に誤差が含まれているためだ. 算出された注視点は,ぴったり表面上にはなく, 約50%の確率で球のちょっとだけ内部に入り込んでいる. で,自分自身によってさえぎられている,と誤解してしまうことになる. (本来ならば,このような自己隠蔽についても検査対象とすべきなのだが, 今回のような凸図形であれば,対象外としても問題ない.)


本日の課題

基本プログラム raycast.c を元にして,鏡面反射を実装せよ. そして,球の配置・材料や照明の方向などのパラメータを自由に変更し, オリジナルな画像を生成すること.

ヒント: 基本的には,反射光のベクトル計算式をそのままコード化するだけ. ベクトル演算の関数については,ファイル vect.h 等を参照.
ありがちなまちがい: クドいが,光の強度は常に正だ.負は,ありえない. そしてこれは,合計についてだけでなく,各反射光の成分についての話でもある.

また,余裕のある者はさらに,シャドーイングについても実装せよ. (この場合,もちろん,影が写っている画像を作ること.)

さらにやる気のある者は, 他の形状(平面,円柱,など)も表示できるようにしてみては? (この場合,交差計算とデータ構造についても考案する必要がある.)

レポート提出

実行結果の画像を作るには,前回同様,import を使えばよい.


(c) 2016, yanagawa@kushiro-ct.ac.jp