Upgrade to Pro — share decks privately, control downloads, hide ads and more …

OSK#p-ray2020年度講義: 古典的レイトレーサーの実装

A7a5efde86e0137d32a93d8609b7a022?s=47 yumcyawiz
February 21, 2021

OSK#p-ray2020年度講義: 古典的レイトレーサーの実装

OSK#p-rayの2020年度講義: 古典的レイトレーサーの実装 の講義スライドです。
講義用リポジトリ: https://github.com/TUS-OSK/classical-raytracer-lecture-2020
p-ray公式サイト: https://p-ray.oskt.us/

A7a5efde86e0137d32a93d8609b7a022?s=128

yumcyawiz

February 21, 2021
Tweet

Transcript

  1. 古典的レイトレーサーの実装 2021/01/24 ~ 2021/02/21 OSK#p-ray Lecture02 ~ Lecture05

  2. 内容 • 画像が出来る仕組み • 古典的レイトレーサーについて • レイの表現 • レイと球体との交差計算 •

    レイとシーンとの交差計算 • ピンホールカメラ 理論編 実装編 • CMakeの使い方 • 分割コンパイルとヘッダオンリー • 古典的レイトレーサーの実装 • SSAA • OpenMPによる並列化 • Ambient Occlusion(AO)
  3. 表記と仮定 • Ԧ 𝑥でベクトルを表現し, 主に位置や方向を表すのに使用する • 方向ベクトルは長さ1に正規化されていると仮定する • ベクトルは縦ベクトルで表現する

  4. 理論編 画像が出来る仕組み

  5. 画像が出来る仕組み • カメラが3次元空間上に置かれているとする • カメラの各画素に届く光を計算すれば良い イメージセンサー レンズ

  6. 画像が出来る仕組み • 光は光源から画素に流れていくが、それを逆にして考える • 画素からシーン上の点にレイを飛ばし, その点から来る光の強さを計算する イメージセンサー ? シーン上の点からどれくらいの光が反射して画素に向かうのかを計算したい

  7. 補足: 光の強さとは? • 物理的には放射輝度に対応するが, それについては次回見ていく • CGでは光の強さはRGB(Red, Green, Blue)の3次元ベクトルで表されることが多い •

    RGBはそのまま画像として書き込める利点がある
  8. 補足: 現実世界はRGB…? • 現実世界では色の実体は波長ごとの光の強さの分布(Spectral Power Distribution) • 人間はそれを錯体細胞によってRGBのように認識していると考えられる • 波長を考慮したレンダリングをSpectral

    Renderingという https://en.wikipedia.org/wiki/Spectral_power_distribution
  9. 理論編 古典的レイトレーサー

  10. 古典的レイトレーサー • コンピューターグラフィクス黎明期の物理ベースではないレイトレーサー(e.g. Whitted Ray Tracing) An improved illumination model

    for shaded display, Whitted, 1979
  11. 古典的レイトレーサーの仕組み • 鏡, ガラス以外の面: まず光源から見えるかどうか確かめる ?

  12. 古典的レイトレーサーの仕組み 光源から見えた場合 𝜔𝑖 を入射方向ベクトル, 𝑛を法線とすると 光源から見えない場合 𝑎の部分は環境光と呼ばれる これは物理ベースではない, アドホックな計算方法 光の強さ𝐼は以下のように計算する

  13. 古典的レイトレーサーの仕組み • 鏡, ガラスの面: 反射, 屈折した先の光を持ってくる(再帰的) ?

  14. レイの表現 • レイは始点位置と方向ベクトルの組( Ԧ 𝑜, Ԧ 𝑑)で表現できる 始点位置 方向ベクトル

  15. レイとシーンの交差計算 • レイトレーサーを実装するにはレイとシーンの交差位置の計算が必要 • まずは球とレイの交差位置の計算を見ていく ? 交差位置の計算 交差判定

  16. レイと球の交差計算 レイ上の点(𝑡はパラメーター) 半径𝑟, 中心Ԧ 𝑐の球の方程式( Ԧ 𝑝は球面上の点) レイ上の点が球面上にあると仮定し

  17. レイと球の交差計算 を球の方程式に代入すると ノルムの2乗は内積で表せるので 展開して整理していくと

  18. レイと球の交差計算 これは𝑡に関する2次方程式になっているので, 解の公式を用いて解を計算できる として 交差判定: 交差位置: 求めた𝑡を に代入 これで交差位置が計算できた

  19. 演習: レイと平面の交差計算 先程の流れを参考にし, 平面とレイの交差計算の式を導出してみましょう 平面は中心位置Ԧ 𝑐と法線𝑛を用いて と表せる( Ԧ 𝑝は平面上の点)

  20. レイとシーンの交差計算 • シーンが複数の球で構成されているとする • レイとシーンの交差計算は, 全ての球と交差計算を行って最も距離が近いものを返せば良い hit? hit? hit?

  21. 注意: レイとシーンの交差計算 • 下図のような場合だと, 数値計算誤差によりレイが床と交差してしまう場合がある • このようなケースを防ぐために, レイに許容最小交差距離𝑡𝑚𝑖𝑛 を導入する

  22. 補足: 空間分割法 • 先ほど説明した方法は全探索で𝑂(𝑛) • Kd-treeやBVHといった空間分割法を用いると𝑂(log 𝑛) ポリゴンを含むようなシーンでは空間分割法を用いないと計算時間がかかりすぎる

  23. カメラモデル • これまでの流れでレイとシーンの交差計算が分かった • 最初のレイはどうやって作れば良いのか? ?

  24. カメラモデル • 画素からレンズ内の屈折をシミュレーションし, シーンにレイを飛ばせば良い • ここでは最も簡単なピンホールカメラモデルを考える イメージセンサー レンズ

  25. 補足: Realistic Camera Model • 現実の写真レンズ内の屈折をシミュレーションすれば, そのレンズで撮った画像 を表現できる

  26. ピンホールカメラ • ピンホールカメラ: 画素に入ってくる光は全てピンホールを通ってくる • ボケの無い完全な結像が得られる イメージセンサー ピンホール

  27. ピンホールカメラによるレイの生成 • 単純に画素からピンホールに向けてレイを飛ばせば良い イメージセンサー ピンホール

  28. 余談: Camera Obscura • Camera Obscuraというピンホールカメラが古い時代から使われていた • Cameraは部屋, Obscuraは暗いを意味するラテン語 •

    ピンホールを通過する光量はとても少ないので撮影には時間がかかる De Radio Astronomica et Geometrica, Frisius, 1544
  29. 実装編 実装のための準備

  30. 実装方法について • 実装はhttps://github.com/TUS-OSK/lecture-classical-raytracer-2020をforkし, srcの中に実 装していきます • refにはリファレンス実装が入っています • プロジェクトはCMakeで管理しているため, それについて少し見ていきます

    • ヘッダオンリーで実装していきます(後述)
  31. CMakeとは? • C/C++の開発でよく使われるビルド自動化ツール • プラットフォームの差異の吸収, 依存ライブラリのビルド管理が楽になる • 今回はCMakeを使ったビルド方法だけを紹介します

  32. CMakeを使ったビルドの流れ プロジェクトルートにCMakeLists.txtがある場合, 以下でbuildの中に実行ファイルが生成される

  33. 演習: CMakeによるビルド • forkしたリポジトリをCMakeでビルドし, 生成された実行ファイルを実行してみ ましょう

  34. 分割コンパイル • C/C++ではヘッダーファイル(.h, .hpp)に宣言, ソースファイル(.c, .cpp)に実装を書くという書 き方をする • 各ソースファイルはオブジェクトファイルにコンパイルされる •

    実行ファイルは各オブジェクトファイルをリンクすることで生成される func.h func.cpp
  35. ヘッダオンリー • ヘッダファイルに実装を全て書くこともでき, そのようなライブラリはヘッダオンリーライブラ リと呼ばれる • #includeしたソースファイルに全ての実装が集約されるので, コンパイル時間が長くなる • ヘッダファイル中での関数の実装にはinlineをつける

    func.h
  36. ヘッダファイルの作成 • インクルードガードと呼ばれるものを最初に必ず書く • これは同じヘッダファイルが1つのファイルに2回以上インクルードされるのを防ぐ インクルードガード

  37. 実装編 古典的レイトレーサーに必要な部品の実装

  38. 古典的レイトレーサーの実装に必要なもの 3次元ベクトル(Vec3) 球(Sphere) レイ(Ray) ピンホールカメラ(PinholeCamera) 画像(Image) 以下の要素をクラスとして実装していく シーン(Scene) 交差情報 (IntersectInfo)

  39. Vec3クラス • 3次元ベクトルを表現するクラス • 位置や方向ベクトル, RGBを表現するのに使用する

  40. 演習: Vec3クラスの作成 • vec3.hを作成し, 以下のような機能を持つ3次元ベクトルクラスを作成する • ベクトル同士の加算, 減算 • スカラー倍

    • ベクトル同士のアダマール積 • ノルム • 内積 • 外積 • 正規化
  41. 演習: Vec3クラスのテスト • Vec3クラスを作成できたら各種演算が正しいかテストしてみる

  42. Imageクラス • 画像を表現するクラス • 画像はfloatの配列で各画素のRGBを表現することにする • RGBは[0,1]の範囲で表現する 1 2 3

    … R G B R G B R G B … 画素1 画素2 画素3
  43. 画像の出力 • 画像はPPM画像で出力することにする • PPM画像は以下のようにテキストファイルで記述される 横, 縦の画素数 階調数 RGBデータ PPM画像であることを示すヘッダー

  44. ガンマ補正 • 生RGBをそのまま画像にすると見た目があんまり良くない • そこで以下のような補正をかける(ガンマ補正)

  45. 演習: Imageクラスの作成 • 以下のような機能を持つImageクラスを作成する • 横, 縦の画素数を返す • 画素(i, j)にRGBをセットする

    • PPM画像の出力 • ガンマ補正
  46. 演習: テスト画像の出力 • 画像クラスを実装できたら, 以下のようなコードで画像を出力してみる

  47. 演習: テスト画像の出力 • 次のような画像が出たらOK

  48. 演習: Rayクラスの作成 • ray.hを作成し, 以下のような機能を持つRayクラスを作成する • 許容最小交差距離 • 始点から距離𝑡のレイ上の点を返す機能

  49. PinholeCameraの実装 • レイの生成は画素上の点の位置と, ピンホールの位置が分かれば簡単に計算でき る 方向: 始点: ピンホール イメージセンサー

  50. ピンホールの位置 • イメージセンサーの中心位置とカメラの前方向から計算できる • ピンホールまでの距離は今はとりあえず適当に1とする イメージセンサー ピンホール

  51. 画素上の点の位置 • イメージセンサーの横, 縦方向の長さが必要になる. ここではとりあえず(2, 2)としておく • 中心位置 + 相対位置を計算すれば良い

    イメージセンサー
  52. 画素上の点の位置 カメラの横方向: カメラの上方向: ここで(𝑢, 𝑣)は(𝑖, 𝑗)を −1,1 × [−1,1]の領域に含まれるように変換したもの

  53. カメラの横方向, 上方向の生成 • カメラ指定はカメラ位置(イメージセンサー中心)とカメラの前方向だけで行いたい • カメラの横方向, 上方向は前方向だけから以下の式で生成する(正規直交基底の生成)

  54. 演習: PinholeCameraクラスの作成 • pinhole-camera.hを作成し, 以下のような機能を持つPinholeCameraクラスを実 装する • 与えられた(𝑢, 𝑣)からレイを生成する

  55. 演習: レイの生成のテスト • PinholeCameraが実装できたら以下のようなコードを試してみる

  56. 演習: レイの生成のテスト • 次のような画像が出たらOK

  57. IntersectInfo シーンとの交差計算では以下のものもセットで計算しておくと良い • 交差位置までの距離 • 交差位置 • 交差位置における法線 これらをIntersectInfoという構造体にまとめることにする

  58. 演習: IntersectInfoの作成 • intersect-info.hを作成する

  59. Sphereクラス • Sphereクラスには球の中心位置, 半径だけではなく, その球がどのような材質なのかを表すイン デックスを持たせる • 材質はミラー, ガラス, その他の3種類

    • 材質の色も持たせる
  60. 演習: Sphereクラスの作成 • sphere.hを作成し, 以下のような機能を持つSphereクラスを実装する • 与えられたレイとの交差判定・交差計算を行う

  61. 演習: 交差計算のテスト • Sphereが実装できたら球の法線を表示してみる

  62. 演習: 交差計算のテスト • 次のような結果が出たらOK

  63. Sceneクラス • Sceneクラスはシーン全体(今回の場合球の集合)を表現するクラス • シーン全体とレイの交差計算を行う

  64. 演習: Sceneクラスの作成 • scene.hを作成し, 以下のような機能を持つSceneクラスを実装する • 球の追加 • 与えられたレイとの交差判定・交差計算を行う

  65. 演習: シーンとの交差計算のテスト • Sceneクラスが実装できたら, 複数の球を追加した状態で法線を表示してみる

  66. 演習: シーンとの交差計算のテスト • 次のような画像が出たらOK(手前にあるものがちゃんと手前に来ているか注意する)

  67. 実装編 古典的レイトレーサーの実装

  68. 古典的レイトレーサーの実装 • これで古典的レイトレーサーを実装するための部品が全て揃った • ここからは理論編で紹介したアルゴリズムを実装していく

  69. 注意: 古典的レイトレーサーの実装 • 古典的レイトレーサーの実装には交差した球のマテリアル情報が必要になる • 1つの解決策: IntersectInfoに交差した球へのポインタを追加 Mirror?

  70. 演習: IntersectInfoの拡張 • IntersectInfoを拡張し, 交差した物体のマテリアル情報を取得できるようにする

  71. 反射ベクトルの計算 • 反射: 法線に関して入射角と出射角が等しい

  72. 演習: 反射ベクトルの計算 • 入射方向 Ԧ 𝑣, 法線𝑛から反射方向Ԧ 𝑟を計算する式を導出する • ベクトルを面と水平な方向・直交する方向に分割して考え、入射角と出射角の関係を適用してみ

  73. 屈折ベクトルの計算 • 屈折は以下のスネルの法則によって記述される • 𝑛1 , 𝑛2 は屈折率 • 入射角によっては全反射があることに注意

  74. 演習: 屈折ベクトルの計算 • 反射ベクトルの場合と同様に水平方向, 垂直方向に分けてスネルの法則を適用

  75. 演習: 反射・屈折ベクトルの計算 • 反射・屈折ベクトルを計算する関数をvec3.hに追加する

  76. 演習: 古典的レイトレーサーの実装 • 理論編のスライドを参考に, 古典的レイトレーサーの処理を実装する • 光源の方向は適当に与える • 再帰またはfor文で処理を実装できる. 好きな方で実装してみよう

  77. 演習: 古典的レイトレーサーのテスト • 実装できたら複数の球を追加して画像を生成してみる

  78. 演習: 古典的レイトレーサーのテスト • 次のような画像が生成される

  79. 課題: 古典的レイトレーサーの拡張 • カメラの視野角を設定できるようにする • 球だけではなく, 複数の形状に対応させる(e.g. 平面) • 点光源を試してみる(光の強さは距離の2乗に反比例)

    • 複数の光源に対応させる • 物体の色をテクスチャで表現してみる
  80. 実装編 並列化・AA・AO

  81. エイリアシング • 物体や影のエッジに注目するとギザギザが見える • これはエイリアシングと呼ばれる • 連続的な光の分布を画素で離散化しているために発生する

  82. アンチエイリアス • エイリアシングを防ぐために、アンチエイリアス(AA)という処理を施す • ここではSSAAといって、画素内で場所を変えながら複数回色を評価し、その平均を取る処理を施す イメージセンサー ピンホール 平均を書き込む

  83. 乱数の生成 • SSAAを実装するためには画素内でランダムに点を取りたい → 乱数生成が必要 • PCG32というアルゴリズムを用いて乱数生成を行う

  84. PCG32による乱数生成 • pcg32_random_tが乱数生成器の状態を表す • pcg32_random_rを呼ぶことでuint32_tの一様乱数が返ってくる • uint32_tのままでは扱いにくいので, [0, 1]のfloatの一様乱数に変換したい •

    RNGクラスでfloatの一様乱数を生成させる
  85. 演習: RNGの実装 • rng.hを作成し, 以下のような機能を持つRNGクラスを実装する • シード値の設定 • [0,1]の範囲のfloatの一様乱数の生成

  86. 演習: 乱数生成のテスト • 実装したRNGクラスを用いて乱数を生成し, std::coutしてみる

  87. 演習: SSAAの実装 • 作成したRNGクラスを用いてSSAAを実装してみる

  88. 演習: SSAAのテスト • SSAAによってエイリアシングが軽減されているか確かめる

  89. 並列化 • SSAAのサンプル数 or 画像の大きさを増やしていくと計算時間が増えてくる • 計算時間を削減するために並列化が重要 CPU1 CPU2 CPU3

    CPU4 CPU1 CPU2 CPU3 CPU4 並列化 未並列化 Task2 Task1 Task3 Task1 Task2 Task3
  90. 並列化 • レイトレは各画素で同じ処理を行うため、並列化との相性が良い • ここではOpenMPを使って手軽に並列化を行ってみる 並列化できる

  91. OpenMPによる並列化 • OpenMPでfor文を並列化するには以下のようにする • #pragmaの部分が並列化を指定している部分

  92. 並列化の注意点 • ある変数を複数のスレッドが同時に読み書きすると値がおかしくなる場合がある(読み込みだけな ら問題ない) • そのため乱数生成器は各画素ごとに用意する(各スレッドごとでも良い) x CPU1 CPU2 1

    2 1 or 2?
  93. 演習: OpenMPによる並列化 • 実装したレイトレーサーをOpenMPで並列化し, 計算スピードが上がったことを確認する • <chrono>を用いて計算時間を計測し, 比較すると良い

  94. Ambient Occlusion(AO) • 周辺から入ってくる光が周りの物体によってどれだけ遮蔽されるかを計算すること • 遮蔽割合に応じて環境光の強さを変化させれば, よりリアルに見える • SSAO, HBAOなど様々な計算方法があるが、今回はレイトレによる計算方法を実装してみる

  95. RTAO(Ray Traced Ambient Occlusion) • 半球面上で一様に点をサンプリングし, その方向にレイを飛ばす • レイが物体に衝突せずに一定距離以上まで進めるかどうかの割合を計算 45%

  96. 半球面上の点の一様サンプリング • ワールド座標系で考えると法線の向きを考慮する必要があり, 話がややこしくなる • そこで, 注目している点の接空間で方向のサンプリングを行う • 最後にサンプリングした方向を接空間からワールド空間に変換すれば良い ワールド空間

    接空間
  97. 接空間 • 基底が接線(Tangent), 法線(Normal), 従法線(Binormal)で与えられるベクトル空間を接空間 (Tangent Space)という • 物体上の点での方向サンプリングを考える際には接空間で考えると法線の向きを考慮しなくて良 くなるので楽になる

  98. 接線, 従法線の計算 • カメラの横方向・上方向の計算と同じことをすれば良い(当該スライド参照) • 法線が(0, 1, 0)に近い場合は計算が破綻するので, 別のベクトルを代わりに使う

  99. 補足: 接線, 従法線の計算 • 物体表面の位置が𝑓(𝑢, 𝑣)で表現される場合, 𝜕𝑓 𝜕𝑢 が接線, 𝜕𝑓

    𝜕𝑣 が従法線に対応する • 球の場合はこれを解析的に計算できるので, 興味ある人はやってみよう
  100. 半球面一様サンプリング • 接空間上で半球面上の点の一様サンプリングは以下の式で行える(導出は今回は 省略) • 𝑢, 𝑣は[0,1]の一様乱数

  101. 接空間からワールド空間への変換 ワールド空間から接空間への変換行列は次で与えられる 接空間からワールド空間への変換は逆行列を考えれば良いが, 𝑇が直交行列であることに注意すると 具体的には接空間上のベクトル Ԧ 𝑣のワールド座標系への変換は以下のように書ける

  102. 演習: RTAOの実装 • RTAOを実装し, 1.0-遮蔽割合を画像にしてみる

  103. 演習: RTAOの活用 • 実装した古典的レイトレーサーにRTAOを組み込み, 環境光の強さを遮蔽割合に応 じて変化させてみる

  104. 補足: 超一様乱数列(準乱数) • 一様乱数はサンプル数が少ないと, 一様に分布しているようには見えない • Halton列, Sobol列などの超一様分布列(準乱数)を用いることで少ないサンプル数でも一様に分布 させることができる •

    RTAOの計算でこれを使うとノイズを低減できる 一様乱数 Sobol列
  105. 古典的レイトレーサー再考 AOを導入しても写実的には見えない. 何が足りないのだろうか? 足りないと考えられる要素 • 本来であれば光源から直接来る光(直接照明)だけではなく, 隣接する他の物体から反射した光の 影響も受ける(間接照明) • 物体上での光の強さの計算が物理的に正しくない

    • 本来であれば空全体から光が入ってくるはず 次回からはこれらの要素を古典的レイトレーサーに取り込んでいく事を行います
  106. 次回以降(予定) • 物理ベースレンダリング理論 • 確率論の基礎 • レンダリング方程式のモンテカルロ積分 理論が中心のお話になります パストレーシングは今回+次回以降の内容ですぐに実装できます!