3D RENDERING HISTORY
CHAPTER 05 · TEXTBOOK

第5章・先取り編 レイトレーシングとレイマーチングの基礎

目的:画素からレイを飛ばす方法、球への解析的交差判定、SDFに基づくsphere tracing、法線と拡散反射を理解する。 左右比較の実験 / OpenGLとWebGL 本編第3章(ラスタライズ・Z-buffer)は未公開。ここはレイによる描画を先に知るための先取り編。

1. 画素から3D空間へ

ラスタライズでは3Dの三角形を画面へ投影して、覆われる画素を探す。レイを使う方式では反対に、画素から3Dへレイを飛ばして最初に当たる面を探す。

左のデモは、球と平面について数学的に交点を求める解析的な一次レイのray casting。反射・屈折や経路を繰り返し追跡するレイトレーシング、パストレーシングとは区別する。

右のデモは、距離関数の値を使って進むsphere tracing。レイマーチングはもっと広い総称であり、たとえばボリュームを固定間隔で積分する方法も含む。レイマーチング全般がSDFを必要とするわけではない。

カメラの原点を \(\mathbf{o}\)、単位方向を \(\mathbf{d}\) とすると、レイ上の位置は

\[ \mathbf{p}(t)=\mathbf{o}+t\mathbf{d},\qquad t\ge 0,\quad\|\mathbf{d}\|=1 \]

方向を正規化すれば \(t\) は実距離。画素 \((x,y)\)、幅 \(W\)、高さ \(H\)、垂直画角 \(\theta\) のとき、

\[ u=\left(2\frac{x+0.5}{W}-1\right)\frac{W}{H}\tan\frac{\theta}{2}, \quad v=\left(1-2\frac{y+0.5}{H}\right)\tan\frac{\theta}{2} \]
\[ \mathbf{d}=\operatorname{normalize}(u,v,1) \]

この教材はカメラが+Zを見る座標系(第1章と同じ)。OpenGLで慣例的な−Z方向とは混同しない。

2. レイトレーシング:球の交点を解く

球の中心 \(\mathbf{c}\)、半径 \(r\) なら表面は

\[ \|\mathbf{p}-\mathbf{c}\|^2=r^2 \]

レイの式を代入し、\(\mathbf{q}=\mathbf{o}-\mathbf{c}\) と置く。方向は単位ベクトルなので、

\[ t^2+2bt+k=0,\quad b=\mathbf{q}\cdot\mathbf{d},\quad k=\mathbf{q}\cdot\mathbf{q}-r^2 \]
\[ t=-b\pm\sqrt{b^2-k} \]

判別式が負なら交点はない。実数解があれば正の最小の解が最初の表面(球の内部からなら出る側)。実装は次の考え方。

const q = subtract(origin, center);
const b = dot(q, direction);  // directionの長さは1
const k = dot(q,q) - radius*radius;
const delta = b*b-k;
if (delta < 0) return Infinity;
const t0 = -b-Math.sqrt(delta), t1 = -b+Math.sqrt(delta);
return t0>1e-4 ? t0 : t1>1e-4 ? t1 : Infinity;

床を水平面 \(y=h\) とすると、方向のy成分が0でない場合、

\[ t=\frac{h-o_y}{d_y} \]

で求まる。複数の物体について交点を求め、最も手前の正の \(t\) を選ぶ。左のデモでは球2個+床で各画素3回の交差テストを行う。

複雑な三角形メッシュは全三角形を総当たりせず、BVHなどの加速構造で交差候補を絞ることが多い。本章はそこを省略している。

3. レイマーチング:SDFで安全に前進

SDF(Signed Distance Function)は表面までの距離を返し、形状の外側で正、内側で負。球と水平面は以下のように書ける。

\[ d_{\mathrm{sphere}}(\mathbf{p})=\|\mathbf{p}-\mathbf{c}\|-r \]
\[ d_{\mathrm{plane}}(\mathbf{p})=p_y-h \]

複数の形状の和集合なら、外側にいるとき

\[ d_{\mathrm{scene}}(\mathbf{p})=\min(d_1,d_2,d_{\mathrm{plane}}) \]

は表面まで進んでよい距離の下限になる。その距離だけ進めば表面を飛び越えないというのがsphere tracingの基本。

\[ t_{n+1}=t_n+d_{\mathrm{scene}}(\mathbf{o}+t_n\mathbf{d}) \]
let t = 0;
for (let i=0; i<maxSteps && t<=maxDist; i++) {
  const p = addScaled(origin,direction,t);
  const distance = sceneDistance(p);
  if (distance<epsilon) return {hit:true,t,steps:i+1};
  t += distance;
}
return {hit:false};

重要な終了条件は3つ。

ここでの t += distance は、距離の下限が保証される関数を使うことが前提。sin(x)+sin(y)+sin(z)、一般のmetaball、gyroidなどの暗黙関数を、根拠なくSDFとしてそのまま加算してはいけない。安全係数、固定刻み、符号変化検出、二分法などの別設計が必要になる。

また本章は形状の外にカメラを配置している。内部から出るレイや、交差・変形させた距離場では符号や終了条件に注意。

4. 表面法線と簡単な光

球の交点 \(\mathbf{p}\) での法線は

\[ \mathbf{n}=\frac{\mathbf{p}-\mathbf{c}}{\|\mathbf{p}-\mathbf{c}\|} \]

SDFでは勾配から \(\mathbf{n}\approx\operatorname{normalize}(\nabla d)\) と求めることもできる。本章では交点を探す方法だけを比較するため、両方式とも球の解析的法線を共用し、同じLambert型の簡易照明を適用する。

\[ I=0.22+0.78\max(0,\mathbf{n}\cdot\mathbf{l}) \]

\(\mathbf{l}\) は単位光方向。影レイ、反射、屈折、間接照明は未実装であり、物理ベースの光輸送ではない。

5. 左右の画像を使った実験

デモを開く。CPUのJavaScriptとCanvas 2Dで同じ2球+床を描き、WebGL・WebGPUには依存しない。

  1. 左(解析法)と右(SDF)で同じ形が出ることを見る。
  2. 最大ステップ数を8にする。右だけ到達前に打ち切られ、欠ける領域が増える。
  3. ステップ数を96に戻し、εを0.04から0.004へ変える。必要ステップ数と境界の違いを調べる。
  4. 左右に視線を回転する。斜めに接近するレイは、進み方が変わる。
  5. 左右いずれかの画像をクリックする。選択画素の方向、解析的交差距離、SDFの t と距離の履歴が下に表示される。

表示する処理時間は、小さいCPU実装の参考値。「3回の交差テスト」と「N回のSDF評価」は演算の重さが異なるため、単純に数字だけで高速性を比較できない。

6. どこが得意で、何がまだない?

いずれもCPU/GPUに実装できる。レイトレーシング=WebGPU、レイマーチング=WebGLという1対1の対応はない。

7. 演習と参考資料

ソース:ray-math.js(方程式・SDF・照明)、app.js(左右描画と操作)、index.html(UI)。

参考文献:John C. Hart, Sphere Tracing: A Geometric Method for the Antialiased Ray Tracing of Implicit Surfaces (1996)。Inigo Quilez, Distance functions。

次はSDFの合成、ソフトシャドウ、反射や屈折、BVH、GPUでの並列化へ進める。

実際に立方体を操作するMarkdown原本