レイを飛ばして、球に当たるか調べる
レイトレーシングでは、目から画面のピクセルへ向けて、レイと呼ぶ半直線を飛ばし、何に当たるかを調べます。球に当たるかどうかは、2次方程式を解くだけで分かります。
このレッスンでは、レイを式で表し、球との交差を判定して、当たる場所までの距離を求めます。
このレッスンのゴール
メモ
レイと球が当たるかを判定し、当たる場所までの距離を求められる。
キーワード
| ことば | いみ | れい |
|---|---|---|
| レイ | 出発点から、ある向きへ伸びる半直線 | |
| 方向 | レイが進む向きを表す3つの数 | (0, 0, 1) |
| 内積 | 対応する数をかけて、足した値 | |
| 判別式 | 2次方程式の解の数を決める値 | |
| 交点 | レイと球が当たる点 |
学生が入力するセルは、このプレビューでは空白です。
レイは、出発点と方向で決まる
レイは、出発点 o から、方向 d へ伸びる半直線です。レイの上の点は、o + t × d と書けます。t は 0 以上の数で、どこまで進んだかを表します。
方向 d の長さが 2 なら、t が 1 増えるごとに、2 進みます。点は (x, y, z) の3つの数で表します。
レイの上の点の例
| t | o + t × d の計算 | 点 |
|---|---|---|
| 0 | o = (1, 0, 0)、d = (0, 1, 2) | (1, 0, 0) |
| 1 | (1, 0, 0) + 1 × (0, 1, 2) | (1, 1, 2) |
| 3 | (1, 0, 0) + 3 × (0, 1, 2) | (1, 3, 6) |
学生が入力するセルは、このプレビューでは空白です。
球は、中心から同じ距離にある点の集まり
球は、中心 m から、距離が半径 r の点の集まりです。点 p が球の上にあるのは、p と m の距離の2乗が、r の2乗に等しいときです。
レイの点 o + t × d を、この式に入れると、t についての2次方程式になります。これを解けば、レイが球に当たる t が分かります。
判別式が、当たるかどうかを決める
2次方程式は、a × t² + b × t + c = 0 の形になります。a は d と d の内積、b は d と (o − m) の内積の 2 倍、c は (o − m) と (o − m) の内積から r² を引いた値です。
判別式は b² − 4 × a × c です。負なら外れ、0 なら1点にかすり、正なら2点で当たります。
判別式と当たり方
| 判別式 | 当たり方 | 解の数 |
|---|---|---|
| 負 | 外れる | 0 |
| 0 | 1点にかする | 1 |
| 正 | 2点で当たる | 2 |
学生が入力するセルは、このプレビューでは空白です。
a、b、c と判別式を求める
o = [0, 0, 0]
d = [0, 0, 1]
m = [0, 0, 7]
r = 2
offset = [o[0] - m[0], o[1] - m[1], o[2] - m[2]]
a = d[0] * d[0] + d[1] * d[1] + d[2] * d[2]
b = 2 * (d[0] * offset[0] + d[1] * offset[1] + d[2] * offset[2])
c = offset[0] ** 2 + offset[1] ** 2 + offset[2] ** 2 - r * r
print(a, b, c)
print("判別式:", b * b - 4 * a * c)
# m を [0, 5, 7] に変えて、もう一度実行します。
# 判別式は、負になりますか。
中心 (0, 0, 7)、半径 2 の球の計算
| 値 | 計算 | 結果 |
|---|---|---|
| a | (0, 0, 1) と (0, 0, 1) の内積 | 1 |
| b | 2 × (0, 0, 1) と (0, 0, -7) の内積 | -14 |
| c | 49 − 4 | 45 |
| 判別式 | (-14)² − 4 × 1 × 45 | 16 |
| t | (14 ± 4) ÷ 2 | 5 と 9 |
学生が入力するセルは、このプレビューでは空白です。
2次方程式の解を numpy で確かめる
numpy の roots は、2次方程式の解を求めます。a、b、c の順に渡します。
次のコマンドは、a = 1、b = -14、c = 45 の解を表示します。出力は [9. 5.] で、t が 9 と 5 の2点で当たります。小さい方の t = 5 が、手前の交点 (0, 0, 5) です。
a = 1、b = -14、c = 45 の解を求める
docker exec jupyter python3 -c "import numpy as np; print(np.roots([1, -14, 45]))"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
手前の交点は、0 より大きい最小の t
2つの解のうち、レイが先に通るのは、小さい方の t です。ただし、t が負なら、その点はレイの後ろにあります。
t が両方とも負なら、球はレイの後ろにあり、当たりません。出発点が球の中なら、小さい方は負で、大きい方が出口になります。
よくある間違い
注意
判別式が正なら、必ず球に当たると考えがちです。判別式が教えるのは、レイをのばした直線との交わりです。t が負なら、当たる点はレイの後ろなので、t の符号も調べます。
レイを全ピクセルに飛ばして、形を見る
すべてのピクセルから、同じ向きにレイを飛ばしたとします。球を正面から見ると、ピクセル (x, y) のレイが、中心 (10, 5)、半径 4 の球に当たるのは、(x − 10)² + (y − 5)² が 16 以下のときです。
次のコマンドは、当たるピクセルを # で、外れるピクセルを . で、21 × 11 の枠に並べます。真ん中の行は # が9個で、上から2行目と下から2行目は1個です。丸い形が見えます。
球の輪郭を # と . で表示する
docker exec jupyter python3 -c "print(chr(10).join(''.join('#' if (x - 10) ** 2 + (y - 5) ** 2 <= 16 else '.' for x in range(21)) for y in range(11)))"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。