影を付ける
陰と影
陰と影は異なる概念である.
日本語だと音が同じであるため紛らわしいが両者は異なる(図1).
陰(shading)は物体表面の形状によって,その物体自身に出来る濃淡である.Chapter5. 陰を付けるで議論している影である.
影(shadow)は物体の輪郭が別の物体に投影されたものである.本章ではこちらの影を扱う.
影の重要性
「課題2-7 光源を複数にする」の生成画像を再掲する(図2).
課題2-7を含む課題2-5以降では,シーン設定は以下のようになっている(図3).
- 球1
- 中心$$(3,0,25)$$,半径$$1$$
- 球2
- 中心$$(2,0,20)$$,半径$$1$$
- 球3
- 中心$$(1,0,15)$$,半径$$1$$
- 球4
- 中心$$(0,0,10)$$,半径$$1$$
- 球5
- 中心$$(-1,0,5)$$,半径$$1$$
- 平面
- 法線方向$$(0,1,0)$$, 位置$$(0,-1,0)$$
実はすべての球は床(平面)に接触している.
しかし図4を見ただけでは球と平面が接していることを汲み取ることは難しい.これは球によって作られるはずの影が存在しないためである. 影はシーンにおける物体の位置を読み取る重要な要素である.
付影処理
付影処理は比較的容易に実装できる.
視線と物体の交点が見つかり個々の光源からの反射光を計算するとき,に光源と交点の間に遮る物体が存在するか否かを調べればよい(図5).
視線とシーン内のすべての物体との交差判定を行うときと同じように,入射ベクトル方向のレイと物体が存在するかを調べる.レイの始点は視線と物体の交点である.このレイをシャドウレイと呼ぶことがある.
交差判定を行う際,物体までの距離に注意する.シャドウレイの方向に物体が見つかっても,光源よりも遠い位置にあるならば光を遮っているとは言えない.そのため視線と物体の交点と光源の間の距離と, シャドウレイと交差した物体までの距離を常に比較する必要がある.
点光源の場合
点光源の場合は,視線との交点と光源との距離$$D_{\ell}$$は容易に計算できる.
視線との交点の位置が$$\vec{\bf p_{\cal i}}$$, 点光源の位置が$$\vec{\bf p_{\ell}}$$ならば, その間の距離は$$D_{\ell}=|\vec{\bf p_{\ell}}-\vec{\bf p_{\cal i}}|$$となる.
これは正規化する前の入射ベクトル$$\vec{\bf \ell}$$の大きさ等しい.
平行光源の場合
平行光源の場合は,視線との交点と光源との距離$$D_{\ell}$$は特殊な扱いとなる.
平行光源は,光源の位置という概念はなく方向しかない.そのため平行光源は光源位置が無限遠方($$D_{\ell}=\infty$$)にあると考える.
付影処理の実装
Scene.testIntersectionWithAll
メソッドの修正
付影処理を実装するためには,シャドウレイとシーン内の物体の物体との交差判定を行う必要がある.
このためには視線レイとシーン内の物体との交差判定を行うのと同様にScene
クラスのtestIntersectionWithAll
メソッドを用いる.
testIntersectionWithAll
メソッドは,与えられた半直線とシーン内の全ての物体との交差判定を行い,最もレイの始点に近い物体を探すメソッドである.
このメソッドを使って,物体と視線の交点を始点とし光源の入射ベクトルを方向とする半直線とシーン内の物体との交差判定を行い,
交差が見つかればその光源からの光は遮蔽されていると判断できる.
ただしtestIntersectionWithAll
メソッドをそのまま使用するのは非効率である.
前回までで実装したtestIntersectionWithAll
メソッドは,引数で与えたシーンの物体のリスト全ての中から
半直線の方向でもっとも半直線の始点に近い物体を検索する.
しかし,付影処理では,交点と光源との距離の間に遮蔽物が存在するかどうかさえ分かればいいので,
最も近い物体を探す必要は無い.
このためにtestIntersectionWithAll
メソッドを以下のように変更する.
class Scene { // ..中略.. // 今までと同じ IntersectionTestResult testIntersectionWithAll(Ray ray); // 引数を増やしてオーバーロードする IntersectionTestResult testIntersectionWithAll(Ray ray, float maxDist, boolean exitOnceFound); }
Processing(Java)では同名で引数の数や型がことなるメソッドを定義することが可能である.これをメソッドのオーバーロードという.
6行目のtestIntersectionWithAll
メソッドは今まで通りの実装である.これに対して9行目のtestIntersectionWithAll
メソッドは2つ引数が増えている.これらの引数の意味は以下である.
ray
(Ray)- 交差判定を行う半直線
maxDist
(float)- 交差していると見なす最大距離.
exitOnceFound
(boolean)- 1つでも交点が見つかったら直ちに交差判定処理を切り上げる.
付影処理では交点と光源との間のみ交差判定を行えばよいため,交差していると見なす最大の距離をmaxDist
引数で
与えられるようにしている.交点がこの距離よりも遠くでが見つかった場合には,交差であると見なさない.
また,交点と光源との間にひとつでも遮蔽物が見つかればそれ以上交差判定を行う必要がないため,exitOnceFound
引数で
交差判定の早期終了を指示できるようにしている.この引数にtrue
を渡すと,交差を発見後した場合に直ちに交差判定処理を終了する.
ところで,いままでのtestIntersectionWithAll
メソッドの動作は,この新しいバージョンの動作のバリエーションと
見なすことができる.すなわち交差していると見なす最大距離が無限遠であり,交差判定の早期終了を行わないということである.
したがって,以下のように実装することが可能である.
class Scene { // ..中略.. // 今までと同じ IntersectionTestResult testIntersectionWithAll(Ray ray) { return testIntersectionWithAll(ray, Float.MAX_VALUE, false); } // 引数を増やしてオーバーロードする IntersectionTestResult testIntersectionWithAll(Ray ray, float maxDist, boolean exitOnceFound); }
Float.MAX_VALUE
は半精度実数で表現できる最大値を示す定数である.
シャドウレイ
シャドウレイの始点に注意する必要がある.
前節で「シャドウレイの始点は視線と物体の交点」と述べたが,これをそのまま実装してしまうとうまくいかない.そのようなシャドウレイは必ずその物体自身と交差する.
なぜなら,視線と物体の交点は,物体上の点であるため,その点をレイの始点としてしまうと交差判定の際にその物体自身が最も近い物体として判定されてしまう.これを避けるため,シャドウレイの始点を入射ベクトル側にほんの少しずらす必要がある(図7).
従ってシャドウレイおよび光源までの距離を以下のように変更する必要がある.
- シャドウレイ
- 始点:$$\vec{\bf p_{\cal i}}+\epsilon\vec{\ell}$$
- 方向:$$\vec{\ell}$$
- $$\vec{\bf p_{\cal i}}$$:交点の位置ベクトル
- $$\vec{\ell}$$:入射ベクトル(光源に向かう方向ベクトルであることに注意)
- $$\epsilon$$:微小距離
- 光源までの距離
- $$D’_{\ell }=D_{\ell}-\epsilon$$
$$\epsilon$$は微小距離であり,$$\frac{1}{32}$$,$$\frac{1}{128}$$, $$\frac{1}{512}$$などを使う.
$$\epsilon$$は定数であるため,スケッチの冒頭で以下のように定義しておくとよい.
final float C_EPSILON = 1.0 / 512;
注意: Processingでは,これとは別にEPSILON
という微少数を表す定数があるが,今回のような幾何計算に使用するには
値が小さすぎる.そのため誤ってEPSILON
定数を使用しないよう注意せよ.
課題
課題3-1. 付影処理を実装する
「課題2-9 rayTraceメソッドを実装する」のスケッチを改造して付影処理を実装せよ.
前述のようにtestIntersectionWithAll
メソッドとrayTrace
メソッドを変更することで実現すること.
シーン設定(物体と光源)などは課題2-9に準じる.
生成画像は図8のようになる.
ヒント1
rayTrace
メソッドで,各光源について処理する部分に追加する.
入射ベクトル$$\vec{\bf \ell}$$が確定した段階で,
- 交点と光源の距離$$D_{\ell}$$を計算する.
Lighting
クラスのdistance
属性に光源までの距離情報が入っている.
- シャドウレイを作る.
- 始点:$$\vec{\bf p_{\cal i}}+\epsilon\vec{\bf \ell}$$
- 方向:$$\vec{\bf \ell}$$
- ここでは$$\epsilon=\frac{1}{512}$$を用いる
- シャドウレイとシーン内の全ての物体との交差判定を行う.
- 交差があった場合
- その光源による反射光を計算を省略する.
- 交差がなかった場合
- 通常通り反射光の計算を行う.
- 交差があった場合
0 Comments.