私は同様のレイトレーサー(Pythonで記述)を持っていて、同じ問題に遭遇しました。物理学を正しく理解するには、交差境界の両側の屈折率を知る必要があります。これはエレガントに解決するのにかなりの時間がかかりましたが、最終的に私はこの解決策/設計を採用しました:
設計
1)シーン-マスターシーンオブジェクト(基本的にはシーン内のすべてのオブジェクトの配列)がありますが、おそらく似たようなものがあります。幾何学的オブジェクトを格納します。
方法:
intersection_points(ray)
-光線からの距離でソートされた、すべての交点のリストを返します。
intersection_objects(ray)
-光線からの距離でソートされた、すべての交差オブジェクトのリストを返します。
containing_object(ray)
-光線を含むオブジェクトを返します。
objects()
-すべてのオブジェクトのリストを任意の順序で返します。
注:シーンは、リストに追加のオブジェクトScene_Boundaryを追加します。これは、シーン全体をカプセル化する巨大なボックス(または球)です。つまり、すべてがこの境界の内側にあります。
2)オブジェクト-ジオメトリックオブジェクト(球など)にこれらのメソッドを実装させます。
方法:
contains(ray)
-光線の原点がオブジェクトの内側にある場合はTrueを返し、表面にある場合はFalseを返し、外側にある場合はFalseを返します。
ray_is_on_surface(ray)
-光線が表面のみにある場合はTrueを返し、それ以外の場合はFalseを返します。
intersection_points(ray)
-光線がオブジェクトと交わる交点を返します
surface_normal(ray)
-光線が当たったサーフェスのサーフェス法線ベクトルを返します(これはフレネルの反射と屈折に役立ちます)
光学計算の場合、オブジェクトには屈折率も必要です。
インスタンス変数:
境界問題
私たちが解決したい問題:境界の内側(n1)と外側(n2)の屈折率は何ですか?これを行うには、次の手順に従います。
1)シーン全体で光線をトレースします。
sphere # origin = (0,0,0), radius = 1
ray # origin = (0,0,0), direction = (0,0,1) Note: the ray is inside the sphere
scene.add_object(sphere)
ipoints = scene.intersection_points(ray) # [ (0,0,1), (0,0,10) ]
iobjects = scene.intersection_objects(ray) # [ Sphere, Scene_Boundary]
これらは光線の原点からの距離でソートされていることを忘れないでください。ipointsとiobjectsの最後の項目は、光線がシーンの境界と交差することです。これは後で使用します!
2)n1は、含まれているオブジェクトを見つけるだけで見つかります。例:
obj1 = scene.containing_object(ray) # Scene_Boundary
n1 = obj1.refractive_index() # n1 = 1. Scene_Boundary always has refractive index of Air
3)n2は、iobjectリストで1つのオブジェクトを先読みすることによって検出されます(例:擬似コード)。
index = iobjects.index_of_object(obj1)
obj2 = iobjects[index+1]
n2 = obj2.refractive_index() # n2 = 1.5 e.g. Glass
4)後で使用するために、サーフェス法線を取得します。
normal = obj1.surface_normal(ray)
正しい反射と屈折を計算するために必要なすべての情報があります。これは、光線がオブジェクトの外側にある場合でも機能するのに十分一般的ですが、アルゴリズムをより堅牢にするために論理フィルタリングを実装する必要がある場合もありましたが、基本的にはそれだけです。
反射と屈折
表面の法線を知るだけでベクトルを反映できます。numpyを使用するPythonでは、次のようにします。
def reflect_vector(normal, vector):
d = numpy.dot(normal, vector)
return vector - 2 * d * normal
(説明したように)屈折にはn1とn2の値が必要です。
def fresnel_refraction(normal, vector, n1, n2):
n = n1/n2
dot = np.dot(norm(vector), norm(normal))
c = np.sqrt(1 - n**2 * (1 - dot**2))
sign = 1
if dot < 0.0:
sign = -1
refraction = n * vector + sign*(c - sign*n*dot) * normal
return norm(refraction)
最後に、光線の反射係数を計算する必要があります。ここで、角度は光線の方向と表面の法線の間の角度です(これは光線が「無偏光」であると仮定しています)。これを0から1までの乱数と比較して、反射が発生するかどうかを判断します。
def fresnel_reflection(angle, n1, n2):
assert 0.0 <= angle <= 0.5*np.pi, "The incident angle must be between 0 and 90 degrees to calculate Fresnel reflection."
# Catch TIR case
if n2 < n1:
if angle > np.arcsin(n2/n1):
return 1.0
Rs1 = n1 * np.cos(angle) - n2 * np.sqrt(1 - (n1/n2 * np.sin(angle))**2)
Rs2 = n1 * np.cos(angle) + n2 * np.sqrt(1 - (n1/n2 * np.sin(angle))**2)
Rs = (Rs1/Rs2)**2
Rp1 = n1 * np.sqrt(1 - (n1/n2 * np.sin(angle))**2) - n2 * np.cos(angle)
Rp2 = n1 * np.sqrt(1 - (n1/n2 * np.sin(angle))**2) + n2 * np.cos(angle)
Rp = (Rp1/Rp2)**2
return 0.5 * (Rs + Rp)
最終コメント
これはすべて、まだリリースされていないPythonオプティカルレイトレーシングプロジェクトからのものです(!)が、詳細については、http://daniel.farrell.name/freebies/pvtraceで確認できます。私はPythonが好きです!ここにリストされているPythonレイトレーシングプロジェクトがいくつかあります。http: //groups.google.com/group/python-ray-tracing-community/web/list-of-python-statistical-ray-tracers。最後に、あなたの例では屈折率の分数に注意してください。方程式は崩壊します。
アップデート
http://github.com/danieljfarrell/pvtraceで入手可能な私のレイトレーサーに実装されたこのスクリーンショット
