Chapter5. 陰を付ける

[—ATOC—] 1 [—AUTO_SECTION_NUMBER—] 1

陰を付ける

前回はレイと物体の交差判定のみを使い,物体の輪郭を可視化した. 今回は単純なシェーディングモデルに基づいた陰影処理を実装する.

beta shading
図1. (左)隠面消去のみ,(右)隠面消去+陰影処理

Phongの反射モデル

現実の光源の複雑さ

レイトレーシング法では,全ての種類の光源と反射現象を扱うことができるわけではない.
手法の性質上,光源と反射現象を単純化して扱う必要がある

光線追跡の難しさ
図2. 光線追跡の難しさ

レイトレーシング法は,光が光源から我々の目に届くまでの過程を目から光源に向かって 追跡しどのように見えるかを再現する手法であると以前に述べた.
このことは,レイトレーシング法では直接光以外の光を正確に追跡することがきわめて困難であることを意味する(図2).

レイトレーシング法では,まず視点からレイを飛ばし視線と交差する物体を探す.視線と物体の交点が見つかったら,その点に直接光を与える光源を探すのは容易である.
しかし,他の物体で何度も反射し交点に到達する全ての光を追跡することは,光源の側からも光線を追跡しない限り不可能である(双方向経路追跡法というレイトレーシング法を拡張した方法もある).

反射現象の単純化

物体表面の反射のしかたを近似するモデルとしてPhongの反射モデルがよく使われる.
Phongの反射モデルでは,物体表面の反射を以下の三つの要素で近似する.

  • 環境光(定数)
  • 直接光の拡散反射光
  • 直接光の鏡面反射光

環境光は,別の物体表面での反射を経て,物体表面にとどく光のことである(間接光ともいう).前述のようにこれを正確にシミュレーションすることは容易ではない. Phongのモデルでは環境光とそれによる反射光の強さは定数として扱う

また,物体表面での反射を拡散反射鏡面反射の2種類のみとして扱う.

  • 拡散反射:ざらざらした物体表面で光が入射点から全方位に反射する現象
  • 鏡面反射:なめらかな物体表面で光が正反射方向に反射する現象(ハイライト)

これらの反射は直接光のみ考慮する.これらの単純化を表1にまとめる.

表1. 光源と反射現象の単純化
光源\反射現象 拡散反射 鏡面反射
直接光 考慮する 考慮する
環境光 定数として扱う


光源

自然界には様々な光源が存在するが,本実験では特に物体表面における光線の方向を計算しやすい点光源平行光源のみを考える(図3,図4).

点光源

点光源は,空間中のある位置から光が全ての方向に広がる光源である.物体表面における光線の方向ベクトル$$\vec{\bf d_{\ell}}$$は 点光源の位置ベクトル$$\vec{\bf p_{\ell}}$$と物体表面の任意の点の位置ベクトル$$\vec{\bf p_{\cal i}}$$によって決まる(たとえば$$\vec{\bf d_{\ell}}=\vec{\bf p_{\cal i}}-\vec{\bf p_{\ell}}$$など.光源位置を始点ととるか終点ととるかは場合による(後述)).

点光源
図3. 点光源

平行光源

平行光源は,空間中のあらゆる位置で同じ方向をもつ光源である.したがって平行光源の方向ベクトルが$$\vec{\bf d_{\ell}}$$ならば 物体の位置に寄らず光線の方向ベクトルも$$\vec{\bf d_{\ell}}$$である.

平行光源
図4. 平行光源

光の減衰

実際の光は,光源からの距離の二乗に反比例して弱くなる(逆二乗の法則)が,簡単化のため本実験では光の減衰は考慮しない

(実装の面から言うと逆二乗の法則を考慮してしまうとシーンがきわめて暗くなる.これは直接光による照明しか考慮していないためである. 現実には我々の目に入る物体表面の反射光は,間接光による反射光が大きな割合を占めている.)

反射光の計算方法

最終的な色(物体表面の輝度)は環境光,拡散反射光,鏡面反射光の足し算となる(図5).

\( L_{r} = L_{a} + L_{d} + L_{s} \)


$$L_{a}$$, $$L_{d}$$, $$L_{s}$$はそれぞれ環境光,直接光の拡散反射光,直接光の鏡面反射光の輝度である.

+ + =
図5. 左から,環境光,拡散反射光,鏡面反射光,それらを足しあわせたもの

次節では各々の反射光の計算方法について解説する.

環境光

環境光(ambient light)あるいは間接光(indirect light)は,光源から発せられ一回以上の反射を経て物体表面に届く光である.
ここでは物体表面に届く環境光の強さは一定とし,その反射光の強さも反射面の方向によらず一定とする.

\( L_{a} = k_{a} E_{a} \)


$$k_{a}$$は環境光反射係数(環境光を反射する度合い)であり,物体表面ごとに異なる値となる.
$$E_{a}$$は環境光の照度であり,シーンごとに異なる値となる.

直接光の拡散反射光

拡散反射(diffuse reflection)は,光が物体表面で入射点からあらゆる方向に散乱する現象である(乱反射とも言う). 例えば紙面や石膏などは拡散反射面の例である.
散反射光は全方位に広がるため,その強さは観察者の視点位置によらない(図6).

理想的な拡散反射面
図6. 理想的な拡散反射面では全ての方向に同じ強さの光を反射する

拡散反射の強さはランバートの余弦則に従うことが知られている.

ランバートの余弦則:微小面から反射する放射強度は,面法線と光線の入射角の余弦に比例する.

この関係は光の入射角が大きくなるほど,照射される面積が大きくなり照度(単位面積あたりの光束)が小さくなることに起因する.(図7).

ランバートの余弦則
図7. ランバートの余弦則

拡散反射光の輝度は以下のように計算する.

\( \begin{array} \\ L_{d} & = & k_{d} E_{i} cos\theta \\ & = & k_{d} E_{i} \left(\vec{\bf n}\cdot\vec{\bf \ell}\right) \\ \end{array} \)


ここで$$k_{d}$$は拡散反射係数,$$E_{i}$$は入射光の照度,$$\vec{\bf n}$$は反射面の法線ベクトル,$$\vec{\bf \ell}$$は入射ベクトルである. $$\vec{\bf \ell}$$は,(呼び名に反して)面との交点から光源へ向かうベクトルであることに注意せよ.また,通常$$\left|\vec{\bf n}\right| = \left|\vec{\bf \ell}\right| = 1$$として扱う(図8).

法線ベクトルと入射ベクトル
図8. 法線ベクトルと入射ベクトル

法線ベクトルと入射ベクトルのなす角には注意する必要がある.
図9のように,法線ベクトルと入射ベクトルのなす角が$$\frac{\pi}{2}$$(=90°)を超えるとき,すなわち 光源が面の裏側から当たっているようなとき反射は起こらない

実装の際は,$$\left(\vec{\bf n}\cdot\vec{\bf \ell}\right)\lt0$$のとき拡散反射光の輝度はゼロとして扱う.

拡散反射が起こらない場合
図9. 拡散反射が起こらない場合

直接光の鏡面反射光

鏡面反射(specular reflection)は金属やプラスチックなどの滑らかな表面で起こる反射である.
このような表面では光源に近い部分に明るい部分(=ハイライト)が見える.

完全な鏡面では,光は入射角の正反射方向にのみ反射するが,現実的には正反射方向を中心とした範囲に広がる(図10).

鏡面反射
図10. 鏡面反射光は向きと広がりを持つ

鏡面反射による反射光は,拡散反射光と違い指向性を持つため,その強さは視点に依存して変化する(図11).

ハイライトの強さ
図11. ハイライトの強さ

Phongの反射モデルでは鏡面反射光の強さは以下のように近似する.

\( \begin{array}{lll} \\ L_{s} & = & k_{s}E_{i}cos^{\alpha}\phi \\ & = & k_{s}E_{i}\left(\vec{\bf v}\cdot\vec{\bf r}\right)^{\alpha} \\ \end{array}\)


ここで,$$k_{s}$$は鏡面反射係数,$$E_{i}$$は入射光の照度,$$\vec{\bf v}$$は視線ベクトルの逆ベクトル,$$\vec{\bf r}$$は入射光の正反射ベクトル, $$\phi$$は$$\vec{\bf v}$$と$$\vec{\bf r}$$のなす角, $$\alpha$$は光沢度(shininess)といいハイライトの鋭さを決める係数($$1\le\alpha$$)である(図12).また,$$\left|\vec{\bf v}\right|=\left|\vec{\bf r}\right|=1$$である.

この式では$$\vec{\bf v}$$と$$\vec{\bf r}$$のなす角$$\phi$$が小さくなるほど大きな値となる.
また,$$\alpha$$が大きい値になるほど$$cos^{\alpha}\phi=\left(\vec{\bf v}\cdot\vec{\bf r}\right)^{\alpha}$$の値の変化は急激になる(図13).

Phongの鏡面反射モデル
図12. Phongの鏡面反射モデル

累乗された余弦関数の軌跡
図13. 累乗された余弦関数の軌跡

法線ベクトルと入射ベクトルのなす角,および視線ベクトルの逆ベクトルと正反射ベクトルのなす角には注意する必要がある.
拡散反射光と同様に,法線ベクトルと入射ベクトルのなす角が$$\frac{\pi}{2}$$より大きいとき, すなわち$$\left(\vec{\bf n}\cdot\vec{\bf \ell}\right)\lt0$$のときは光が当たらないため鏡面反射光もゼロとなる.

逆ベクトルと正反射ベクトルのなす角が$$\frac{\pi}{2}$$より大きいとき(図14)も鏡面反射はおこらない.

実装の際は,$$\left(\vec{\bf v}\cdot\vec{\bf r}\right)\lt0$$のときは鏡面反射光はゼロとして扱う.

鏡面反射が起こらない場合
図14. 鏡面反射が起こらない場合

なお,正反射ベクトル$$\vec{\bf r}$$は以下のように計算することができる(図15).

\( \vec{\bf r}=2\left(\vec{\bf n}\cdot\vec{\bf \ell}\right)\vec{\bf n}-\vec{\bf \ell} \)

正反射ベクトル
図15. 正反射ベクトル

まとめ

以上の反射光に関する式を以下にまとめる.

物体表面の輝度
\( L_{r} = L_{a} + L_{d} + L_{s} \)
  • $$L_{a}$$:環境光の反射光の輝度
  • $$L_{d}$$:直接光の拡散反射光の輝度
  • $$L_{s}$$:直接光の鏡面反射光の輝度
環境光の反射光の輝度
\( L_{a} = k_{a} E_{a} \)
  • $$k_{a}$$:環境光反射係数
  • $$E_{a}$$:環境光の照度
直接光の拡散反射光の輝度
\( L_{d} = k_{d} E_{i} \left(\vec{\bf n}\cdot\vec{\bf \ell}\right) \)
  • $$k_{d}$$:拡散反射係数
  • $$E_{i}$$:入射光の照度
  • $$\vec{\bf n}$$:物体面の法線ベクトル($$\left|\vec{\bf n}\right|=1$$)
  • $$\vec{\bf \ell}$$:光の入射方向ベクトル($$\left|\vec{\bf \ell}\right|=1$$)
    • 光源が始点

※ $$\left(\vec{\bf n}\cdot\vec{\bf \ell}\right)\lt0$$のとき拡散反射光の輝度はゼロである.

直接光の鏡面反射光の輝度
\( L_{s} = k_{s}E_{i}\left(\vec{\bf v}\cdot\vec{\bf r}\right)^{\alpha} \)
  • $$k_{s}$$:鏡面反射係数
  • $$E_{i}$$:入射光の照度
  • $$\vec{\bf v}$$:視線ベクトルの逆ベクトル($$\left|\vec{\bf v}\right|=1$$)
  • $$\vec{\bf r}$$:入射光の正反射ベクトル($$\left|\vec{\bf r}\right|=1$$)
  • $$\alpha$$:光沢度($$1\le\alpha$$)

※ $$\left(\vec{\bf n}\cdot\vec{\bf \ell}\right)\lt0$$あるいは$$\left(\vec{\bf v}\cdot\vec{\bf r}\right)\lt0$$のとき鏡面反射光の輝度はゼロである.

正反射ベクトル
\( \vec{\bf r}=2\left(\vec{\bf n}\cdot\vec{\bf \ell}\right)\vec{\bf n}-\vec{\bf \ell} \)
  • $$\vec{\bf n}$$:物体面の法線ベクトル($$\left|\vec{\bf n}\right|=1$$)
  • $$\vec{\bf \ell}$$:光の入射方向ベクトル($$\left|\vec{\bf \ell}\right|=1$$)
    • 光源が始点

補足:レイと平面/球の交点における法線ベクトル

前回はレイ(半直線)と平面や球の交点を求める方法について説明した.上述のように物体表面の輝度を求めるには,物体表面の法線ベクトルが必要である. ここにレイと平面/球の交点における法線ベクトルの求め方について補足する.

平面の場合

平面の場合は,交点における法線ベクトルは面の法線ベクトルに等しい.

球の場合

球の場合,球の中心の位置ベクトルが$$\vec{\bf p_{\cal c}}$$,交点の位置ベクトルが$$\vec{\bf p_{\cal i}}$$ならば, 交点における法線ベクトルは,$$\left(\vec{\bf p_{\cal c}}-\vec{\bf p_{\cal i}}\right)$$となる (実際に使用する際はこのベクトルを正規化する必要がある).

課題 [—EXCLUDE—]

課題2-1. 最初の陰影処理(拡散反射光のみ)

前回の課題1-7の実装に,拡散反射による陰影処理を追加する.

シーンは,4-1.3. シーン設定点光源(光源位置を$$\vec{\bf p_{\ell}}=(-5,5,-5)$$)を追加する(図16).

  • 視点位置
    • $$\vec{\bf p_{\cal e}}=(0,0,-5)$$
  • スクリーン位置
    • 左上$$(-1,1,0)$$, 右上$$(1,1,0)$$, 右下$$(1,-1,0)$$, 左下$$(-1,-1,0)$$
    • 中心位置$$\vec{\bf p_{\cal c}}=(0,0,5)$$
    • 半径$$1.0$$
  • 光源位置
    • $$\vec{\bf p_{\ell}}=(-5,5,-5)$$
  • 背景色
    • 前回は背景(球と交差しない場合)の色は$$RGB=(0,0,255)$$としたが好きな色に変えてよい
    • 以降の出力例では$$RGB=(100, 149, 237)$$(cornflowerblue)である.

シーン設定
図16. シーン設定

ソースファイル名はdiffuse_only.cとする.

画像サイズは幅512ピクセル,高さ512ピクセルとせよ.

生成画像は図17のようになる.

生成画像
図17. diffse_only.cの生成画像

プログラムのビルド方法および実行方法

以下のコマンドでプログラムの実行形式を生成する.

$ gcc -std=c89 -Wall diffuse_only.c vector_utils.c -lm -o diffuse_only

問題なくビルドできたら,以下のコマンドでPPM形式画像を生成し表示する.

$ ./diffuse_only > result.ppm $ display result.ppm


ヒント1

+ クリックで表示 クリックで非表示

入射ベクトルと法線ベクトルの内積を画素値として使う($$[0,1]\rightarrow[0,255]$$のマッピングは必要).

-クリックで非表示

ヒント2

+ ソースを日本語で書き下したもの クリックで非表示
  1. 判別式$$D=0$$のとき
    • $$t=\frac{-B}{2A}$$
  2. 判別式$$D>0$$のとき
    • $$t=\frac{-B+\sqrt{D}}{2A},\frac{-B-\sqrt{D}}{2A}$$
    • 二つの$$t$$のうち,値が正でかつ小さい方を$$t$$とする
  3. $$t\gt0$$のとき
    1. 交点位置$$\vec{\bf p_{\cal i}}$$を計算する.
      • $$\vec{\bf p_{\cal i}}=\vec{\bf p_{\cal e}}+t\vec{\bf d_{\cal e}}$$
    2. 入射ベクトル$$\vec{\bf \ell}$$を計算する.
      • $$\vec{\bf \ell}=\vec{\bf p_{\ell}}-\vec{\bf p_{\cal i}}$$
      • 正規化が必要であることに注意せよ.
    3. 法線ベクトル$$\vec{\bf n}$$を計算する.
      • $$\vec{\bf n}=\vec{\bf p_{\cal i}}-\vec{\bf p_{\cal c}}$$
      • 正規化が必要であることに注意せよ.
    4. 内積$$\left(\vec{\bf n}\cdot\vec{\bf \ell}\right)$$を計算する.
    5. 内積$$\left(\vec{\bf n}\cdot\vec{\bf \ell}\right)$$の値を$$[0,1]$$の範囲に切り詰める.
      • 内積なので値の範囲は$$[-1,1]$$である.
      • 内積が0以下のときは光が当たっていないので0として扱う.
    6. 色を出力する.
      • $$gray=255\left(\vec{\bf n}\cdot\vec{\bf \ell}\right)$$
      • $$RGB=(gray, gray, gray)$$を出力する.
  4. $$t\gt0$$以外のとき
    • 背景色(ここでは$$RGB=(100, 149, 237)$$)を出力する.
-クリックで非表示

ヒント3

+ ソース穴埋め クリックで非表示
-クリックで非表示

ヒント4

+ シェフの気まぐれ穴埋めソース~ファイルを添えて クリックで非表示
-クリックで非表示

課題2-2. 最初の陰影処理(環境光,拡散反射光,鏡面反射光)

課題2-1の実装に,環境光と鏡面反射による陰影処理を追加する. シーン設定は課題2-1に準じる.

ライティングに関わるパラメータは以下とせよ.

  • 環境光反射係数
    • $$k_{a}=0.01$$
  • 拡散反射係数
    • $$k_{d}=0.69$$
  • 鏡面反射係数
    • $$k_{s}=0.3$$
  • 光沢度
    • $$\alpha=8$$
  • 環境光の照度
    • $$E_{a}=0.1$$
  • 直接光の照度
    • $$E_{i}=1.0$$

ソースファイル名はsimple_shading.cとする.

画像サイズは幅512ピクセル,高さ512ピクセルとせよ.

生成画像は図18のようになる.

生成画像
図18. simple_shading.cの生成画像

プログラムのビルド方法および実行方法

以下のコマンドでプログラムの実行形式を生成する.

$ gcc -std=c89 -Wall simple_shading.c vector_utils.c -lm -o simple_shading

問題なくビルドできたら,以下のコマンドでPPM形式画像を生成し表示する.

$ ./simple_shading > result.ppm $ display result.ppm

ヒント1

+ ソースを日本語で書き下したもの クリックで非表示

交差がある場合($$t\gt0$$)の場合の処理は以下のようになる.

  1. 環境光の輝度$$L_{a}$$を計算する
    • $$L_{a}=k_{a}E_{a}$$
  2. 交点位置$$\vec{\bf p_{\cal i}}$$を計算する.
  3. 入射ベクトル$$\vec{\bf \ell}$$を計算する.
  4. 法線ベクトル$$\vec{\bf n}$$を計算する.
  5. 内積$$\left(\vec{\bf n}\cdot\vec{\bf \ell}\right)$$を計算する.
  6. 内積$$\left(\vec{\bf n}\cdot\vec{\bf \ell}\right)$$の値を$$[0,1]$$の範囲に切り詰める.
  7. 直接光の拡散反射光の輝度$$L_{d}$$を計算する
    • $$L_{d}=k_{d}E_{i}\left(\vec{\bf n}\cdot\vec{\bf \ell}\right)$$
  8. $$\left(\vec{\bf n}\cdot\vec{\bf \ell}\right)\gt0$$のとき
    1. 正反射ベクトル$$\vec{\bf r}$$を計算する.
      • $$\vec{\bf r}=2\left(\vec{\bf n}\cdot\vec{\bf \ell}\right)\vec{\bf n}-\vec{\bf \ell}$$
    2. 視線ベクトルの逆ベクトル$$\vec{\bf v}$$を計算する.
      • $$\vec{\bf v}=-\vec{\bf d_{\cal e}}$$
      • 正規化が必要であることに注意せよ.
    3. 内積$$\left(\vec{\bf v}\cdot\vec{\bf r}\right)$$を計算する.
    4. 内積$$\left(\vec{\bf v}\cdot\vec{\bf r}\right)$$の値を$$[0,1]$$の範囲に切り詰める.
    5. 直接光の鏡面反射光の輝度$$L_{s}$$を計算する
      • $$L_{s}=k_{s}E_{i}\left(\vec{\bf v}\cdot\vec{\bf r}\right)^{\alpha}$$
      • 累乗の計算にはpow関数を用いる.
  9. $$\left(\vec{\bf n}\cdot\vec{\bf \ell}\right)\gt0$$以外のとき
    1. 直接光の鏡面反射光の輝度$$L_{s}$$はゼロとする.
  10. 反射光の輝度$$L_{r}$$を計算する
    • $$L_{r}=L_{a}+L_{d}+L_{s}$$
  11. $$L_{r}$$の値を$$[0,1]$$の範囲に切り詰める.
  12. 色を出力する.
    • $$gray=255L_{r}$$
    • $$RGB=(gray, gray, gray)$$を出力する.
-クリックで非表示

ヒント2

+ ソース穴埋め クリックで非表示
-クリックで非表示

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>