5

我正在为教育目的构建一个简单的光线追踪器,并希望为对象添加折射。使用 Snells Law,我能够在交叉点递归地创建一条新射线。光线追踪器目前仅支持球体,我使用了一个场景,其中多个球体相互嵌套,具有不同的折射率。

如果我从球体外部开始射线,一切似乎都很简单。你从场景的折射率开始,一旦你击中第一个球体,就使用之前的折射率和球体材料的折射率来折射光线,直到你击中下一个球体,依此类推。使用交叉点的法线,我可以确定我是进入还是离开球体。

但是,我不明白应该如何处理球体叶子以及如果光线没有从场景的外部开始该怎么办。

  • 我可以只取一堆折射率并在我离开球体后立即向上一层吗?
  • 如果我从球体内开始,我如何确定我必须从什么折射率开始?

例子

您有三个球体,从外到内的折射率分别为 0.9、1.1 和 0.8。空气指数为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 列表中向前查看一个对象来找到的,例如在伪代码中:

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)

您拥有计算正确反射和折射所需的所有信息。即使光线在对象之外,这也足够通用,但偶尔我确实需要实现一些逻辑过滤以使算法更健壮,但基本上就是这样!

反射和折射

您只需知道表面法线就可以反射矢量。在 Python 中使用 numpy 我这样做,

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。

斯涅尔定律指出,入射角和折射角的正弦之比等于边界两侧两种介质的折射率之比的倒数。

因此,当您有一条光线接近一种新材料并且您想知道新材料中的角度时,您需要知道光线撞击新材料的角度、新材料的折射率和折射率光线当前所在材料的折射系数。

正如您所说,折射进入球体时效果很好,您必须已经知道每个球体和场景的折射率。

我会说创建一堆折射率将是处理进入一堆嵌套材料的好方法,因为当你移出时你将不得不再次触摸你推入堆栈的所有折射率的嵌套球体集。

至于确定离开球体时必须从什么折射率开始,您总是说 sin(theta1)/sin(theta2) = [折射率为 2]/[折射率为 1]。因此,您需要您当前所在材料的折射率以及您将要向其移动的材料的折射率。

如果我误解了你的问题,我深表歉意,但我希望这会有所帮助!

于 2010-10-04T20:12:30.167 回答