レイトレーシングに挑戦しましたというお話。

f:id:rakiyama0229:20220214192819p:plain

f:id:rakiyama0229:20220214192832p:plain

はじめに

レイトレーシング法を使って空間上の物体を描画するプログラムを作りました.
言語: C
グラフィックツール: X-Window
ライブラリ: MinilibX
その他: ペアで開発

描画する物体など, 空間上の情報はrtファイルというファイルから受け取ります.

  • rtファイルの形式例
A 0.2 255,255,255                     //環境光, 光の強さ(0~1.0), 色(rgb)
L 20,21,-20 0.7 255,255,255           //光源, 位置(xyz座標), 光の強さ, 色
C 20,10,20 -1,-0.5,-1 70              //カメラ, 位置, 向き, 視野角度
pl 0,0,0 0,1,0 0,255,255              //平面, 平面上の点位置, 法線の向き, 色
sp 0,0,0 10 255,0,0                   //球体, 中心点位置, 直径, 色
cy 0,0,0 1,0,0 0.5 10000 255,120,120  //円筒, 中心点位置, 中心線向き, 直径, 高さ, 色
  • レボジトリ
    github.com

  • 参考資料
    丁寧に解説されていて本当に助けられました, ありがとうございました.
    knzw.tech

本記事では考え方の概要と,
レイトレースの知識を活用して螺旋状に球体を並べる(本記事トップの画像のような)
通称螺旋丸の作り方も紹介したいと思います.
何か間違っている部分がありましたら, ご指摘頂けると嬉しいです.

概要

下図は球体をレイトレースで表現する際のイメージ図です.

f:id:rakiyama0229:20220216133422p:plain

カメラ, 球体, 床, 光源があり,
カメラ前方には最終的な出力画像と対応した仮想的なスクリーンがあります.
画像基板の各ピクセル(に対応するスクリーン上の点)に対して
以下の処理をすることで色を決定し画像を生成します.
また, 半直線や光線のことをレイといいます.

  1. カメラからスクリーン上のあらゆる点に対してレイ(カメラレイと呼ぶ)を飛ばす
  2. カメラレイが物体と交差する場合は, その交点から光源に向けてレイ(ライトレイと呼ぶ)を飛ばす
  3. 各点の色を決定する
    点A: カメラレイが物体と交差しないので背景色
    点B: カメラレイが球と交差, ライトレイを飛ばす, ライトレイの角度的に明るめの球の色
    点C: カメラレイが球と交差, ライトレイを飛ばす, ライトレイの角度的に暗めの球の色
    点D: カメラレイが球と交差, ライトレイが球自体に遮られる, 陰の色
    点E: カメラレイが床と交差, ライトレイが他の物体(球)に遮られる, 影の色

現実世界では, 光源からの光が物体に反射してカメラに届くことで物体を映せるわけですが,
レイトレーシング法では逆にカメラから光線を追っていきます.
カメラに届く光のみを考えた方が楽だからです(だと思います).
レイをトレース(追跡)してシュミレーションするのでレイトレースと言います.
上記では「ライトレイの角度的に明るめの色」など抽象的な説明をしていましたが,
実際はPhongの反射モデルを用いて色を算出しました.

レイと物体の交差判定

例として, レイと球の交差判定について考察したものを下図に載せます.

f:id:rakiyama0229:20220215182743p:plain
半直線と球体の交差判定

図中の上半分は, レイの式①と球体の式②について連立方程式を解いています.
下半分は, 得た式からレイと球の交差する条件について考えています.
他の物体に関しても同じように, レイの式と物体を表す式をもとに交差する条件について考えます.

(カメラから飛ばす)レイの導き方

やること

スクリーン上の点Pにレイを飛ばす場合を考えます.
点Pcam(カメラ位置)から点C(スクリーン中心), 点Cから点Pへのベクトルを追っていくと,
レイを表す式を導き出せます.

f:id:rakiyama0229:20220216133611p:plain
図中のdx, dyは平面上でそれぞれx軸正方向, y軸正方向の単位ベクトルです.
目標として表したレイの式について,
既にわかっているベクトルVcam(カメラの向き)以外を求めていきます.

スクリーン上の座標(xs, ys)の求め方

画像上のあるピクセルについてレイを考えているとして,
スクリーン上の座標(xs, ys)を画像上の座標(xi, yi)で表すのが目標です.
下図のように画像とスクリーンの縦横の幅をこちらで決めてしまえば式を導き出すことができます.

f:id:rakiyama0229:20220219000215p:plain

画像とスクリーンは全く同じ縦横の幅, 座標で考えると簡単ですが
画像の大きさを変えられなくなってしまうので縦横の相似関係のみ維持するように考えます.
画像を大きく設定するとピクセルの量が増え, 解像度が変化します.
また, スクリーンの大小はカメラからの距離が変化するだけでレイの方向は変わりません.
(次の項目の図を見ると想像しやすいかもしれません).

スクリーンまで距離tの求め方

ここでカメラの視野角θ, カメラの向きベクトルVcam, スクリーンの幅Ws(前項により)は既知です.
三角関数から以下のように求められます.

f:id:rakiyama0229:20220215192658p:plain

スクリーン上の単位ベクトルdx, dyの求め方

dx, dyはカメラの向きベクトルVcamに垂直な面において, 互いに垂直に交わるベクトルです.
つまりVcamとdx, dyの3つのベクトルはそれぞれが垂直に交わることになります.
垂直に交わるベクトルは外積なるものを使って求めます.
流れは以下です.

  1. (とりあえず)y軸正方向の単位ベクトルeyを用意
  2. Vcamとeyの外積から得られたベクトルをdxとする
  3. Vcamとdxの外積から得られたベクトルをdyとする f:id:rakiyama0229:20220215192842p:plain

dxを求めるために用意したベクトル(ey)をy軸正方向のベクトルにしたので,
スクリーンの上方向は常にy軸正方向になります(と思います).
今回のカメラはその辺の角度が決まっていないのでこれでいいかな程度で実装を終えています.
※カメラの向きベクトルがeyと同じ場合については考慮しなければいけません

螺旋丸の作り方

螺旋状に変化する球体の中心座標の導き方をご紹介します.
ここまでで, 互いに垂直なレイ・dx・dyベクトルを使って3次元空間上で平面座標を扱えることがわかりました.
また, 平面上で円周上に球体を移動させて, さらに平面自体も移動させると螺旋状に球体が動きます.
下図は左半分が平面上で移動させる球体の様子, 右半分がさらに平面自体も移動させた様子をイメージしたものです.

f:id:rakiyama0229:20220216133729p:plain
平面上での球体の位置はrとθを使って表せます.
平面の中心位置は, レイの開始位置とレイの方向ベクトルにtをかけることで表せます.
つまり処理の流れは以下です.

  1. レイ(螺旋の中心線)の開始位置と方向ベクトル, r・θ・tの初期値, 球体の直径などを決める
  2. 球体の中心座標を求める
  3. θとtを一定の変化率で変化させる
  4. 2と3を繰り返す

処理3でθとtのみではなく, rなど他のパラメータを変えるとまた面白い動きが見れそうです.

プログラムの流れ

おまけで載せさせてください. f:id:rakiyama0229:20220214181204p:plain

さいごに

現実世界の現象をコンピュータ上でシュミレーションできた実感がとても面白かったです!
もちろんその仕組みを知れたことも面白かったです.
今回の知識が何に役に立つかはわかりませんが, 来たるXRの世界を少しでも理解できたらいいなと思います.
一緒に実装してくださったfyutaさんありがとうございました.