Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
古典的レイトレーサーの実装 2021/01/24 ~ 2021/02/21 OSK#p-ray Lecture02 ~ Lecture05
Slide 2
Slide 2 text
内容 • 画像が出来る仕組み • 古典的レイトレーサーについて • レイの表現 • レイと球体との交差計算 • レイとシーンとの交差計算 • ピンホールカメラ 理論編 実装編 • CMakeの使い方 • 分割コンパイルとヘッダオンリー • 古典的レイトレーサーの実装 • SSAA • OpenMPによる並列化 • Ambient Occlusion(AO)
Slide 3
Slide 3 text
表記と仮定 • Ԧ 𝑥でベクトルを表現し, 主に位置や方向を表すのに使用する • 方向ベクトルは長さ1に正規化されていると仮定する • ベクトルは縦ベクトルで表現する
Slide 4
Slide 4 text
理論編 画像が出来る仕組み
Slide 5
Slide 5 text
画像が出来る仕組み • カメラが3次元空間上に置かれているとする • カメラの各画素に届く光を計算すれば良い イメージセンサー レンズ
Slide 6
Slide 6 text
画像が出来る仕組み • 光は光源から画素に流れていくが、それを逆にして考える • 画素からシーン上の点にレイを飛ばし, その点から来る光の強さを計算する イメージセンサー ? シーン上の点からどれくらいの光が反射して画素に向かうのかを計算したい
Slide 7
Slide 7 text
補足: 光の強さとは? • 物理的には放射輝度に対応するが, それについては次回見ていく • CGでは光の強さはRGB(Red, Green, Blue)の3次元ベクトルで表されることが多い • RGBはそのまま画像として書き込める利点がある
Slide 8
Slide 8 text
補足: 現実世界はRGB…? • 現実世界では色の実体は波長ごとの光の強さの分布(Spectral Power Distribution) • 人間はそれを錯体細胞によってRGBのように認識していると考えられる • 波長を考慮したレンダリングをSpectral Renderingという https://en.wikipedia.org/wiki/Spectral_power_distribution
Slide 9
Slide 9 text
理論編 古典的レイトレーサー
Slide 10
Slide 10 text
古典的レイトレーサー • コンピューターグラフィクス黎明期の物理ベースではないレイトレーサー(e.g. Whitted Ray Tracing) An improved illumination model for shaded display, Whitted, 1979
Slide 11
Slide 11 text
古典的レイトレーサーの仕組み • 鏡, ガラス以外の面: まず光源から見えるかどうか確かめる ?
Slide 12
Slide 12 text
古典的レイトレーサーの仕組み 光源から見えた場合 𝜔𝑖 を入射方向ベクトル, 𝑛を法線とすると 光源から見えない場合 𝑎の部分は環境光と呼ばれる これは物理ベースではない, アドホックな計算方法 光の強さ𝐼は以下のように計算する
Slide 13
Slide 13 text
古典的レイトレーサーの仕組み • 鏡, ガラスの面: 反射, 屈折した先の光を持ってくる(再帰的) ?
Slide 14
Slide 14 text
レイの表現 • レイは始点位置と方向ベクトルの組( Ԧ 𝑜, Ԧ 𝑑)で表現できる 始点位置 方向ベクトル
Slide 15
Slide 15 text
レイとシーンの交差計算 • レイトレーサーを実装するにはレイとシーンの交差位置の計算が必要 • まずは球とレイの交差位置の計算を見ていく ? 交差位置の計算 交差判定
Slide 16
Slide 16 text
レイと球の交差計算 レイ上の点(𝑡はパラメーター) 半径𝑟, 中心Ԧ 𝑐の球の方程式( Ԧ 𝑝は球面上の点) レイ上の点が球面上にあると仮定し
Slide 17
Slide 17 text
レイと球の交差計算 を球の方程式に代入すると ノルムの2乗は内積で表せるので 展開して整理していくと
Slide 18
Slide 18 text
レイと球の交差計算 これは𝑡に関する2次方程式になっているので, 解の公式を用いて解を計算できる として 交差判定: 交差位置: 求めた𝑡を に代入 これで交差位置が計算できた
Slide 19
Slide 19 text
演習: レイと平面の交差計算 先程の流れを参考にし, 平面とレイの交差計算の式を導出してみましょう 平面は中心位置Ԧ 𝑐と法線𝑛を用いて と表せる( Ԧ 𝑝は平面上の点)
Slide 20
Slide 20 text
レイとシーンの交差計算 • シーンが複数の球で構成されているとする • レイとシーンの交差計算は, 全ての球と交差計算を行って最も距離が近いものを返せば良い hit? hit? hit?
Slide 21
Slide 21 text
注意: レイとシーンの交差計算 • 下図のような場合だと, 数値計算誤差によりレイが床と交差してしまう場合がある • このようなケースを防ぐために, レイに許容最小交差距離𝑡𝑚𝑖𝑛 を導入する
Slide 22
Slide 22 text
補足: 空間分割法 • 先ほど説明した方法は全探索で𝑂(𝑛) • Kd-treeやBVHといった空間分割法を用いると𝑂(log 𝑛) ポリゴンを含むようなシーンでは空間分割法を用いないと計算時間がかかりすぎる
Slide 23
Slide 23 text
カメラモデル • これまでの流れでレイとシーンの交差計算が分かった • 最初のレイはどうやって作れば良いのか? ?
Slide 24
Slide 24 text
カメラモデル • 画素からレンズ内の屈折をシミュレーションし, シーンにレイを飛ばせば良い • ここでは最も簡単なピンホールカメラモデルを考える イメージセンサー レンズ
Slide 25
Slide 25 text
補足: Realistic Camera Model • 現実の写真レンズ内の屈折をシミュレーションすれば, そのレンズで撮った画像 を表現できる
Slide 26
Slide 26 text
ピンホールカメラ • ピンホールカメラ: 画素に入ってくる光は全てピンホールを通ってくる • ボケの無い完全な結像が得られる イメージセンサー ピンホール
Slide 27
Slide 27 text
ピンホールカメラによるレイの生成 • 単純に画素からピンホールに向けてレイを飛ばせば良い イメージセンサー ピンホール
Slide 28
Slide 28 text
余談: Camera Obscura • Camera Obscuraというピンホールカメラが古い時代から使われていた • Cameraは部屋, Obscuraは暗いを意味するラテン語 • ピンホールを通過する光量はとても少ないので撮影には時間がかかる De Radio Astronomica et Geometrica, Frisius, 1544
Slide 29
Slide 29 text
実装編 実装のための準備
Slide 30
Slide 30 text
実装方法について • 実装はhttps://github.com/TUS-OSK/lecture-classical-raytracer-2020をforkし, srcの中に実 装していきます • refにはリファレンス実装が入っています • プロジェクトはCMakeで管理しているため, それについて少し見ていきます • ヘッダオンリーで実装していきます(後述)
Slide 31
Slide 31 text
CMakeとは? • C/C++の開発でよく使われるビルド自動化ツール • プラットフォームの差異の吸収, 依存ライブラリのビルド管理が楽になる • 今回はCMakeを使ったビルド方法だけを紹介します
Slide 32
Slide 32 text
CMakeを使ったビルドの流れ プロジェクトルートにCMakeLists.txtがある場合, 以下でbuildの中に実行ファイルが生成される
Slide 33
Slide 33 text
演習: CMakeによるビルド • forkしたリポジトリをCMakeでビルドし, 生成された実行ファイルを実行してみ ましょう
Slide 34
Slide 34 text
分割コンパイル • C/C++ではヘッダーファイル(.h, .hpp)に宣言, ソースファイル(.c, .cpp)に実装を書くという書 き方をする • 各ソースファイルはオブジェクトファイルにコンパイルされる • 実行ファイルは各オブジェクトファイルをリンクすることで生成される func.h func.cpp
Slide 35
Slide 35 text
ヘッダオンリー • ヘッダファイルに実装を全て書くこともでき, そのようなライブラリはヘッダオンリーライブラ リと呼ばれる • #includeしたソースファイルに全ての実装が集約されるので, コンパイル時間が長くなる • ヘッダファイル中での関数の実装にはinlineをつける func.h
Slide 36
Slide 36 text
ヘッダファイルの作成 • インクルードガードと呼ばれるものを最初に必ず書く • これは同じヘッダファイルが1つのファイルに2回以上インクルードされるのを防ぐ インクルードガード
Slide 37
Slide 37 text
実装編 古典的レイトレーサーに必要な部品の実装
Slide 38
Slide 38 text
古典的レイトレーサーの実装に必要なもの 3次元ベクトル(Vec3) 球(Sphere) レイ(Ray) ピンホールカメラ(PinholeCamera) 画像(Image) 以下の要素をクラスとして実装していく シーン(Scene) 交差情報 (IntersectInfo)
Slide 39
Slide 39 text
Vec3クラス • 3次元ベクトルを表現するクラス • 位置や方向ベクトル, RGBを表現するのに使用する
Slide 40
Slide 40 text
演習: Vec3クラスの作成 • vec3.hを作成し, 以下のような機能を持つ3次元ベクトルクラスを作成する • ベクトル同士の加算, 減算 • スカラー倍 • ベクトル同士のアダマール積 • ノルム • 内積 • 外積 • 正規化
Slide 41
Slide 41 text
演習: Vec3クラスのテスト • Vec3クラスを作成できたら各種演算が正しいかテストしてみる
Slide 42
Slide 42 text
Imageクラス • 画像を表現するクラス • 画像はfloatの配列で各画素のRGBを表現することにする • RGBは[0,1]の範囲で表現する 1 2 3 … R G B R G B R G B … 画素1 画素2 画素3
Slide 43
Slide 43 text
画像の出力 • 画像はPPM画像で出力することにする • PPM画像は以下のようにテキストファイルで記述される 横, 縦の画素数 階調数 RGBデータ PPM画像であることを示すヘッダー
Slide 44
Slide 44 text
ガンマ補正 • 生RGBをそのまま画像にすると見た目があんまり良くない • そこで以下のような補正をかける(ガンマ補正)
Slide 45
Slide 45 text
演習: Imageクラスの作成 • 以下のような機能を持つImageクラスを作成する • 横, 縦の画素数を返す • 画素(i, j)にRGBをセットする • PPM画像の出力 • ガンマ補正
Slide 46
Slide 46 text
演習: テスト画像の出力 • 画像クラスを実装できたら, 以下のようなコードで画像を出力してみる
Slide 47
Slide 47 text
演習: テスト画像の出力 • 次のような画像が出たらOK
Slide 48
Slide 48 text
演習: Rayクラスの作成 • ray.hを作成し, 以下のような機能を持つRayクラスを作成する • 許容最小交差距離 • 始点から距離𝑡のレイ上の点を返す機能
Slide 49
Slide 49 text
PinholeCameraの実装 • レイの生成は画素上の点の位置と, ピンホールの位置が分かれば簡単に計算でき る 方向: 始点: ピンホール イメージセンサー
Slide 50
Slide 50 text
ピンホールの位置 • イメージセンサーの中心位置とカメラの前方向から計算できる • ピンホールまでの距離は今はとりあえず適当に1とする イメージセンサー ピンホール
Slide 51
Slide 51 text
画素上の点の位置 • イメージセンサーの横, 縦方向の長さが必要になる. ここではとりあえず(2, 2)としておく • 中心位置 + 相対位置を計算すれば良い イメージセンサー
Slide 52
Slide 52 text
画素上の点の位置 カメラの横方向: カメラの上方向: ここで(𝑢, 𝑣)は(𝑖, 𝑗)を −1,1 × [−1,1]の領域に含まれるように変換したもの
Slide 53
Slide 53 text
カメラの横方向, 上方向の生成 • カメラ指定はカメラ位置(イメージセンサー中心)とカメラの前方向だけで行いたい • カメラの横方向, 上方向は前方向だけから以下の式で生成する(正規直交基底の生成)
Slide 54
Slide 54 text
演習: PinholeCameraクラスの作成 • pinhole-camera.hを作成し, 以下のような機能を持つPinholeCameraクラスを実 装する • 与えられた(𝑢, 𝑣)からレイを生成する
Slide 55
Slide 55 text
演習: レイの生成のテスト • PinholeCameraが実装できたら以下のようなコードを試してみる
Slide 56
Slide 56 text
演習: レイの生成のテスト • 次のような画像が出たらOK
Slide 57
Slide 57 text
IntersectInfo シーンとの交差計算では以下のものもセットで計算しておくと良い • 交差位置までの距離 • 交差位置 • 交差位置における法線 これらをIntersectInfoという構造体にまとめることにする
Slide 58
Slide 58 text
演習: IntersectInfoの作成 • intersect-info.hを作成する
Slide 59
Slide 59 text
Sphereクラス • Sphereクラスには球の中心位置, 半径だけではなく, その球がどのような材質なのかを表すイン デックスを持たせる • 材質はミラー, ガラス, その他の3種類 • 材質の色も持たせる
Slide 60
Slide 60 text
演習: Sphereクラスの作成 • sphere.hを作成し, 以下のような機能を持つSphereクラスを実装する • 与えられたレイとの交差判定・交差計算を行う
Slide 61
Slide 61 text
演習: 交差計算のテスト • Sphereが実装できたら球の法線を表示してみる
Slide 62
Slide 62 text
演習: 交差計算のテスト • 次のような結果が出たらOK
Slide 63
Slide 63 text
Sceneクラス • Sceneクラスはシーン全体(今回の場合球の集合)を表現するクラス • シーン全体とレイの交差計算を行う
Slide 64
Slide 64 text
演習: Sceneクラスの作成 • scene.hを作成し, 以下のような機能を持つSceneクラスを実装する • 球の追加 • 与えられたレイとの交差判定・交差計算を行う
Slide 65
Slide 65 text
演習: シーンとの交差計算のテスト • Sceneクラスが実装できたら, 複数の球を追加した状態で法線を表示してみる
Slide 66
Slide 66 text
演習: シーンとの交差計算のテスト • 次のような画像が出たらOK(手前にあるものがちゃんと手前に来ているか注意する)
Slide 67
Slide 67 text
実装編 古典的レイトレーサーの実装
Slide 68
Slide 68 text
古典的レイトレーサーの実装 • これで古典的レイトレーサーを実装するための部品が全て揃った • ここからは理論編で紹介したアルゴリズムを実装していく
Slide 69
Slide 69 text
注意: 古典的レイトレーサーの実装 • 古典的レイトレーサーの実装には交差した球のマテリアル情報が必要になる • 1つの解決策: IntersectInfoに交差した球へのポインタを追加 Mirror?
Slide 70
Slide 70 text
演習: IntersectInfoの拡張 • IntersectInfoを拡張し, 交差した物体のマテリアル情報を取得できるようにする
Slide 71
Slide 71 text
反射ベクトルの計算 • 反射: 法線に関して入射角と出射角が等しい
Slide 72
Slide 72 text
演習: 反射ベクトルの計算 • 入射方向 Ԧ 𝑣, 法線𝑛から反射方向Ԧ 𝑟を計算する式を導出する • ベクトルを面と水平な方向・直交する方向に分割して考え、入射角と出射角の関係を適用してみ る
Slide 73
Slide 73 text
屈折ベクトルの計算 • 屈折は以下のスネルの法則によって記述される • 𝑛1 , 𝑛2 は屈折率 • 入射角によっては全反射があることに注意
Slide 74
Slide 74 text
演習: 屈折ベクトルの計算 • 反射ベクトルの場合と同様に水平方向, 垂直方向に分けてスネルの法則を適用
Slide 75
Slide 75 text
演習: 反射・屈折ベクトルの計算 • 反射・屈折ベクトルを計算する関数をvec3.hに追加する
Slide 76
Slide 76 text
演習: 古典的レイトレーサーの実装 • 理論編のスライドを参考に, 古典的レイトレーサーの処理を実装する • 光源の方向は適当に与える • 再帰またはfor文で処理を実装できる. 好きな方で実装してみよう
Slide 77
Slide 77 text
演習: 古典的レイトレーサーのテスト • 実装できたら複数の球を追加して画像を生成してみる
Slide 78
Slide 78 text
演習: 古典的レイトレーサーのテスト • 次のような画像が生成される
Slide 79
Slide 79 text
課題: 古典的レイトレーサーの拡張 • カメラの視野角を設定できるようにする • 球だけではなく, 複数の形状に対応させる(e.g. 平面) • 点光源を試してみる(光の強さは距離の2乗に反比例) • 複数の光源に対応させる • 物体の色をテクスチャで表現してみる
Slide 80
Slide 80 text
実装編 並列化・AA・AO
Slide 81
Slide 81 text
エイリアシング • 物体や影のエッジに注目するとギザギザが見える • これはエイリアシングと呼ばれる • 連続的な光の分布を画素で離散化しているために発生する
Slide 82
Slide 82 text
アンチエイリアス • エイリアシングを防ぐために、アンチエイリアス(AA)という処理を施す • ここではSSAAといって、画素内で場所を変えながら複数回色を評価し、その平均を取る処理を施す イメージセンサー ピンホール 平均を書き込む
Slide 83
Slide 83 text
乱数の生成 • SSAAを実装するためには画素内でランダムに点を取りたい → 乱数生成が必要 • PCG32というアルゴリズムを用いて乱数生成を行う
Slide 84
Slide 84 text
PCG32による乱数生成 • pcg32_random_tが乱数生成器の状態を表す • pcg32_random_rを呼ぶことでuint32_tの一様乱数が返ってくる • uint32_tのままでは扱いにくいので, [0, 1]のfloatの一様乱数に変換したい • RNGクラスでfloatの一様乱数を生成させる
Slide 85
Slide 85 text
演習: RNGの実装 • rng.hを作成し, 以下のような機能を持つRNGクラスを実装する • シード値の設定 • [0,1]の範囲のfloatの一様乱数の生成
Slide 86
Slide 86 text
演習: 乱数生成のテスト • 実装したRNGクラスを用いて乱数を生成し, std::coutしてみる
Slide 87
Slide 87 text
演習: SSAAの実装 • 作成したRNGクラスを用いてSSAAを実装してみる
Slide 88
Slide 88 text
演習: SSAAのテスト • SSAAによってエイリアシングが軽減されているか確かめる
Slide 89
Slide 89 text
並列化 • SSAAのサンプル数 or 画像の大きさを増やしていくと計算時間が増えてくる • 計算時間を削減するために並列化が重要 CPU1 CPU2 CPU3 CPU4 CPU1 CPU2 CPU3 CPU4 並列化 未並列化 Task2 Task1 Task3 Task1 Task2 Task3
Slide 90
Slide 90 text
並列化 • レイトレは各画素で同じ処理を行うため、並列化との相性が良い • ここではOpenMPを使って手軽に並列化を行ってみる 並列化できる
Slide 91
Slide 91 text
OpenMPによる並列化 • OpenMPでfor文を並列化するには以下のようにする • #pragmaの部分が並列化を指定している部分
Slide 92
Slide 92 text
並列化の注意点 • ある変数を複数のスレッドが同時に読み書きすると値がおかしくなる場合がある(読み込みだけな ら問題ない) • そのため乱数生成器は各画素ごとに用意する(各スレッドごとでも良い) x CPU1 CPU2 1 2 1 or 2?
Slide 93
Slide 93 text
演習: OpenMPによる並列化 • 実装したレイトレーサーをOpenMPで並列化し, 計算スピードが上がったことを確認する • を用いて計算時間を計測し, 比較すると良い
Slide 94
Slide 94 text
Ambient Occlusion(AO) • 周辺から入ってくる光が周りの物体によってどれだけ遮蔽されるかを計算すること • 遮蔽割合に応じて環境光の強さを変化させれば, よりリアルに見える • SSAO, HBAOなど様々な計算方法があるが、今回はレイトレによる計算方法を実装してみる
Slide 95
Slide 95 text
RTAO(Ray Traced Ambient Occlusion) • 半球面上で一様に点をサンプリングし, その方向にレイを飛ばす • レイが物体に衝突せずに一定距離以上まで進めるかどうかの割合を計算 45%
Slide 96
Slide 96 text
半球面上の点の一様サンプリング • ワールド座標系で考えると法線の向きを考慮する必要があり, 話がややこしくなる • そこで, 注目している点の接空間で方向のサンプリングを行う • 最後にサンプリングした方向を接空間からワールド空間に変換すれば良い ワールド空間 接空間
Slide 97
Slide 97 text
接空間 • 基底が接線(Tangent), 法線(Normal), 従法線(Binormal)で与えられるベクトル空間を接空間 (Tangent Space)という • 物体上の点での方向サンプリングを考える際には接空間で考えると法線の向きを考慮しなくて良 くなるので楽になる
Slide 98
Slide 98 text
接線, 従法線の計算 • カメラの横方向・上方向の計算と同じことをすれば良い(当該スライド参照) • 法線が(0, 1, 0)に近い場合は計算が破綻するので, 別のベクトルを代わりに使う
Slide 99
Slide 99 text
補足: 接線, 従法線の計算 • 物体表面の位置が𝑓(𝑢, 𝑣)で表現される場合, 𝜕𝑓 𝜕𝑢 が接線, 𝜕𝑓 𝜕𝑣 が従法線に対応する • 球の場合はこれを解析的に計算できるので, 興味ある人はやってみよう
Slide 100
Slide 100 text
半球面一様サンプリング • 接空間上で半球面上の点の一様サンプリングは以下の式で行える(導出は今回は 省略) • 𝑢, 𝑣は[0,1]の一様乱数
Slide 101
Slide 101 text
接空間からワールド空間への変換 ワールド空間から接空間への変換行列は次で与えられる 接空間からワールド空間への変換は逆行列を考えれば良いが, 𝑇が直交行列であることに注意すると 具体的には接空間上のベクトル Ԧ 𝑣のワールド座標系への変換は以下のように書ける
Slide 102
Slide 102 text
演習: RTAOの実装 • RTAOを実装し, 1.0-遮蔽割合を画像にしてみる
Slide 103
Slide 103 text
演習: RTAOの活用 • 実装した古典的レイトレーサーにRTAOを組み込み, 環境光の強さを遮蔽割合に応 じて変化させてみる
Slide 104
Slide 104 text
補足: 超一様乱数列(準乱数) • 一様乱数はサンプル数が少ないと, 一様に分布しているようには見えない • Halton列, Sobol列などの超一様分布列(準乱数)を用いることで少ないサンプル数でも一様に分布 させることができる • RTAOの計算でこれを使うとノイズを低減できる 一様乱数 Sobol列
Slide 105
Slide 105 text
古典的レイトレーサー再考 AOを導入しても写実的には見えない. 何が足りないのだろうか? 足りないと考えられる要素 • 本来であれば光源から直接来る光(直接照明)だけではなく, 隣接する他の物体から反射した光の 影響も受ける(間接照明) • 物体上での光の強さの計算が物理的に正しくない • 本来であれば空全体から光が入ってくるはず 次回からはこれらの要素を古典的レイトレーサーに取り込んでいく事を行います
Slide 106
Slide 106 text
次回以降(予定) • 物理ベースレンダリング理論 • 確率論の基礎 • レンダリング方程式のモンテカルロ積分 理論が中心のお話になります パストレーシングは今回+次回以降の内容ですぐに実装できます!