初めてのレイトレ

そんなわけで、無茶を覚悟でレイトレをやってみることにします。

数学関数群を作ったり、フレームバッファを作ったり、下準備は結構掛かりましたが、肝となっている部分は簡単です。

まずはカメラとか射影変換とか3Dっぽいことは何も考えずに、適当に配置したオブジェクトとレイの当たり判定を取って、交差していたら色を塗るってことだけをやってみることにします。

まずは、レイトレの主役ともいえる、レイ(光線)の表現を考えておきます。

レイは空間上に存在する一点と、光線の進む方向のベクトルで示します。

まぁ、こんな感じ

ray(Q,V)

Qがレイの基準位置、Vがレイの進む方向を示すベクトルです。

次に、レイ上にある一点を表現してみます。

P(t)=Q+tV

レイは、途切れることなくずっと一直線につながっているわけですから、レイ上の点をtというパラメーターを用いて表現しています。

レイの方向を示す、Vが単位ベクトルの場合、tってのはすなわち、レイの基準位置Qからの(進行方向に沿った)距離となります。

例の表現がわかったところで、今回表示しようとしている球の表現についても考えておきます。

こいつは簡単で、球の中心点と半径で示すことができます。

sphere(S,r)

Sが球の中心点の座標、rが半径です。

レイと球の表現がわかったところで、この二つの交差判定を考えることにします。

...でも、ここまでで出てきた表現だけでは、うまいこと交差判定を取ることが難しいので球の数学的な表現を出しておくことにします。

まずは復習もかねて、二次元上で数学的な円の表現を思い出しておきましょう。

二次元上の数学的な円の表現

x^2+y^2=r^2

ここで、rは円の半径を示しています。

ピタゴラスの定理を考えてあげれば、この式は納得できますよね。

そいでは、三次元上の数学的な球の表現をしておきます。

三次元上の数学的な球の表現

x^2+y^2+z^2=r^2

何も難しいことをやっているわけじゃなくって、円の表現を拡張しているだけです。
ま、こいつもピタゴラスの定理を考えてあげれば、納得できますね。

さて、球の表面上にある一点Xを考えた場合、次のような式が成り立ちます。

(X - S)^2=r^2

Xは、球の表面上にある点の座標、Sは球の中心点、rは球の半径です。

...ありゃ、スカラーがベクトルになっちゃって、わけわからなくなっちゃったって人でも大丈夫、ベクトルのノムルってのを思い出しときましょう。

|V|=\sqrt{V^2_x + V^2_y + V^2_z}

つまり、ベクトルの長さ|V|をrに置き換えてあげればほら簡単♪
ついでに、この先の話を読むためにもう一つ復習しとくと、ベクトルの二乗ってベクトルの長さの長さの二乗、つまり同じベクトルどうしの内積になります。

V^2=|V|^2=VV=V^2_x + V^2_y + V^2_z

閑話休題

ここで、レイと球が交差する点を考えたいのですから、球面上の点を用いた式のXにレイ上の一点を示す式を代入してみます。

X=P(t)=Q+tV
とおいて
(X - S)^2=r^2
(Q + tV - S)^2=r^2

ここで、Q-Sの意味を考えてみれば、Qってのがレイの基準点、Sってのが球の中心点だったので、球の中心点を原点としたときの球表面上の一転の座標を示していることがわかります。

よってこの先は、球の中心点が原点(0,0,0)にあるものとして考えてしまっても問題ありません。(計算前に、引いてあげればよいだけですから)

(Q + tV)^2=r^2
|V|^2t^2 + 2QVt + |Q|^2 = r^2
|V|^2t^2 + 2QVt + |Q|^2 - r^2 = 0

そんなこんなで、tの二次方程式が得られました。

この方程式を解けば、レイと球が交差するときのtがわかる、すなわち交差する点の座標が判るわけですよね。

こいつは、二次方程式の解の公式を使ってあげればさっくり解けます。

二次方程式の解の公式ってのは、こんな形をしていました。

ax^2+bx+c=0の時の解の公式

x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}

ax^2+2bx+c=0の時の解の公式

x=\frac{-b\pm\sqrt{b^2-ac}}{a}

ここまで理解できれば後は、コードに落とすだけですね。

/// レイとの交差判定
bool Sphere::Intersect(
					   const Ray& i_ray
					   )
{
	math::Vec3f a_vQ = i_ray.GetPos() - m_vPos;
	const math::Vec3f& a_vV = i_ray.GetDir();

	f32 a_fQVdot = math::Vec3f::Dot( a_vQ, a_vV );
	f32 a_a = a_vV.NormSq();
	f32 a_b = 2.0f * a_fQVdot;
	f32 a_c = a_vQ.NormSq() - (m_fRadius * m_fRadius);

	f32 a_fD = a_b * a_b - 4.0f * a_a * a_c; // 判定式
	if( a_fD < 0.0f ){
		return false;
	}

	f32 a_pt = a_fQVdot * -1.0f + math::Sqrt( a_fD );
	f32 a_mt = a_fQVdot * -1.0f - math::Sqrt( a_fD );

	if( a_pt < 0.0f && a_mt < 0.0f ){
		return false;
	}

	return true;
}

あとは、適当に球を配置して、フレームバッファの点毎にレイを飛ばして交差判定を取るだけです。

交差していた場合には青い点、交差していなかった場合には黒いてんを書き込むようにしました。


情けない画面ですが、実際にやってみると結構感動するものです