5

教育目的で単純なレイトレーサーを作成していて、オブジェクトに屈折を追加したいと思います。スネルの法則を使用して、交点で再帰的に新しい光線を作成することができます。レイトレーサーは現在球のみをサポートしており、屈折率が異なる複数の球が互いに入れ子になっているシーンを使用しています。

球の外側から光線を開始すると、すべてが単純に見えます。シーンの屈折率から始め、最初の球に当たるとすぐに、前の屈折率と球のマテリアルの屈折率を使用して光線を屈折させ、次の球に当たるまで続けます。交差点の法線を使用して、球に入るのか出るのかを判断できます。

ただし、球葉をどのように処理する必要があるのか​​、シーンの外側で光線が始まらない場合はどうすればよいのかわかりません。

  • 屈折率のスタックを取得して、球を離れたらすぐに1つ上に移動できますか?
  • 球の内側から開始する場合、どの屈折率で開始する必要があるかをどのように判断できますか?

外側から内側への屈折率が0.9、1.1、0.8の3つの球があります。空気指数は1.0です

  • カメラは球の外側にあり、球の中心を指しています。

    • 開始インデックスが1.0の場合、最初にインデックス0.9で外側の球にヒットし、1.0から0.9に屈折し、光線が0.9のマテリアルになったことを保存します。
    • 真ん中の球に当たると、材料定数が1.1になります。これは、0.9を保存したので、0.9から1.1に屈折し、0.9に加えて1.1を保存する必要があることを知っています。
    • 内側の球に当たって1.1から0.8に屈折し、今まで0.9、1.1、0.8を保存しました
    • 内側の球をもう一度叩きます(今回は球を終了するので、保存した値を確認して、1.1に戻す必要があることを確認します)
    • ...あなたが外に出るまで
  • カメラが球の中にあるとき、今問題があります。どの屈折率に切り替える必要があるかわかりません。

4

2 に答える 2

9

私は同様のレイトレーサー(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)-光線が当たったサーフェスのサーフェス法線ベクトルを返します(これはフレネルの反射と屈折に役立ちます)

光学計算の場合、オブジェクトには屈折率も必要です。

インスタンス変数:

  • refractive_index

境界問題

私たちが解決したい問題:境界の内側(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で入手可能な私のレイトレーサーに実装されたこのスクリーンショット 代替テキスト

于 2011-01-03T12:25:03.977 に答える
1

レイトレーシングの実装の観点ではなく、物理学の観点からこれを投稿する:P。

スネルの法則によれば、入射角と屈折角の正弦の比は、境界の両側にある2つの媒体の屈折率の比の逆数に等しくなります。

したがって、光線が新しいマテリアルに近づいていて、新しいマテリアルの角度を知りたい場合は、光線が新しいマテリアルに当たる角度、新しいマテリアルの屈折率、およびインデックスを知る必要があります。光線が現在入っている材料の屈折率。

屈折は球に移動するとうまく機能すると言うように、各球とシーンの屈折率をすでに知っている必要があります。

屈折率のスタックを作成することは、ネストされたマテリアルの束に入るのに対処するための良い方法だと思います。移動するときにスタックにもう一度押すすべての屈折率に触れる必要があるからです。ネストされた球のセットの。

球を離れるときにどの屈折率から始めなければならないかを決定することに関して、あなたは常にsin(theta1)/ sin(theta2)=[屈折率2]/[屈折率1]と言っています。したがって、現在使用しているマテリアルの屈折率と、移動しようとしているマテリアルの屈折率が必要です。

あなたの質問を誤解した場合はお詫びしますが、それがお役に立てば幸いです。

于 2010-10-04T20:12:30.167 に答える