如何在光线从嵌套对象内部开始时处理折射

Eta*_*tan 5 graphics raytracing

我正在为教育目的构建一个简单的光线跟踪器,并希望为对象添加折射.使用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然后保存1.1除了0.9之外
    • 你击中内部球体并从1.1折射到0.8并且你已经保存到现在0.9,1.1和0.8
    • 你再次击中内部球体(这次你退出它,所以你检查你保存的值,并知道你必须切换回1.1)
    • ......直到你在外面
  • 现在问题,当相机在球体内时.您不知道必须切换的折射率.

Dan*_*ell 9

我有一个类似的光线追踪器(用Python编写)并偶然发现了同样的问题:为了正确地计算出物理学,人们必须知道交叉点边界两侧的折射率.这花了很长时间才能优雅地解决,但最后我选择了这个解决方案/设计:

设计

1)场景 - 我有一个主场景对象(基本上是场景中所有对象的数组),你可能会有类似的东西.它存储几何对象.

方法:

  • intersection_points(ray) - 返回所有交叉点的列表,按照与光线的距离排序.
  • intersection_objects(ray) - 返回所有交叉点对象的列表,按照与光线的距离排序.
  • containing_object(ray) - 返回包含光线的对象.
  • objects() - 以任意顺序返回所有对象的列表.

注意:场景向列表添加了一个额外的对象:Scene_Boundary.这是一个封装整个场景的巨型盒子(或Sphere),即一切都在这个边界内.

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]
Run Code Online (Sandbox Code Playgroud)

请记住,这些是按照距射线原点的距离排序的.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
Run Code Online (Sandbox Code Playgroud)

3)通过在iobject列表中查找前面的一个对象来找到n2,例如在伪代码中:

index = iobjects.index_of_object(obj1)
obj2 = iobjects[index+1]
n2 = obj2.refractive_index() # n2 = 1.5 e.g. Glass
Run Code Online (Sandbox Code Playgroud)

4)获得表面法线供以后使用:

normal = obj1.surface_normal(ray)
Run Code Online (Sandbox Code Playgroud)

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

反射和折射

您可以通过了解曲面法线来反射矢量.在使用numpy的Python中,我这样做,

def reflect_vector(normal, vector):
   d = numpy.dot(normal, vector)
   return vector - 2 * d * normal
Run Code Online (Sandbox Code Playgroud)

折射(如上所述)需要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)
Run Code Online (Sandbox Code Playgroud)

最后,您需要计算光线的反射系数,其中角度是光线方向和曲面法线之间的角度(假设您的光线是"未散射的").将其与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)
Run Code Online (Sandbox Code Playgroud)

最后的评论

这一切都来自我尚未发布的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上找到 替代文字


joh*_*188 1

从物理角度而不是光线追踪实现角度发布此内容:P。

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

因此,当光线接近新材料并且想要知道新材料中的角度时,您需要知道光线撞击新材料的角度、新材料的折射率以及折射率射线当前所在材料的折射。

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

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

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

如果我误解了你的问题,我很抱歉,但希望能有所帮助!