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

Graphics 当光线从嵌套对象内部开始时,如何处理折射,graphics,raytracing,Graphics,Raytracing,我正在构建一个简单的光线跟踪器,用于教育目的,并希望向对象添加折射。使用斯奈尔定律,我能够在交点处递归地创建一条新光线。光线跟踪器当前仅支持球体,我使用的场景中,多个球体相互嵌套,折射率不同 如果我从球体外部开始一条光线,一切看起来都很简单。从场景的折射率开始,一旦击中第一个球体,使用之前的折射率和球体材质的折射率折射光线,直到击中下一个球体,依此类推。使用交点的法线,我可以确定是否进入或离开球体 但是,我不明白如何处理球体叶子,以及如果光线没有从场景的外部开始,该怎么办 我能用一堆折射率,一

我正在构建一个简单的光线跟踪器,用于教育目的,并希望向对象添加折射。使用斯奈尔定律,我能够在交点处递归地创建一条新光线。光线跟踪器当前仅支持球体,我使用的场景中,多个球体相互嵌套,折射率不同

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

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

  • 我能用一堆折射率,一离开球体就往上一层吗
  • 如果从球体内部开始,我如何确定折射率
示例

有三个球体,从外到内折射率分别为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)
    • 。。。直到你在外面
  • 现在的问题是,当摄影机位于球体内部时。你不知道你必须切换到什么折射率


    • 从物理角度而非光线跟踪实现角度发布此消息:p

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

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

      正如您所说,在移动到球体中时折射效果良好,您必须已经知道每个球体和场景的折射率

      我想说,创建一个折射率堆栈将是一个很好的方法来处理进入一堆嵌套材质的问题,因为当你移出嵌套的球体集时,你将不得不再次接触你推到堆栈上的所有折射率

      当你离开球体时,首先要确定折射率,你总是说sin(θ1)/sin(θ2)=[折射率2]/[折射率1]。因此,您需要当前所在材质的折射率和将要移动的材质的折射率


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

      我有一个类似的光线跟踪器(用Python编写),但遇到了同样的问题:为了正确计算物理,必须知道相交边界两侧的折射率。这花了相当长的时间才优雅地解决,但最终我采用了以下解决方案/设计:

      设计

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

      方法:

      • 交点(光线)
        -返回所有交点的列表,按与光线的距离排序
      • 相交对象(光线)
        -返回所有相交对象的列表,按与光线的距离排序
      • 包含对象(光线)
        -返回包含光线的对象
      • objects()
        -以任意顺序返回所有对象的列表
      注意:场景会向列表中添加一个额外的对象:场景\u边界。这是一个巨大的盒子(或球体),封装了整个场景,即所有东西都在这个边界内

      2) 对象--使几何对象(例如球体)实现这些方法

      方法:

      • 包含(光线)
        -如果光线原点位于对象内部,则返回True;如果光线原点位于曲面上,则返回False;如果光线原点位于对象外部,则返回False
      • 光线在曲面上(光线)
        -如果光线仅在曲面上,则返回True,否则返回False
      • 交点(光线)
        -返回光线与对象的交点
      • 曲面\u法线(光线)
        -返回光线照射的曲面的曲面法线向量(这将有助于菲涅耳反射和折射)
      对于光学计算,对象还必须具有折射率

      实例变量:

      • 折射率
      边界问题

      我们要解决的问题是:边界内部(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中的最后一项是光线与t的交点
      obj1 = scene.containing_object(ray) # Scene_Boundary
      n1 = obj1.refractive_index() # n1 = 1. Scene_Boundary always has refractive index of Air
      
      index = iobjects.index_of_object(obj1)
      obj2 = iobjects[index+1]
      n2 = obj2.refractive_index() # n2 = 1.5 e.g. Glass
      
      normal = obj1.surface_normal(ray)
      
      def reflect_vector(normal, vector):
         d = numpy.dot(normal, vector)
         return vector - 2 * d * normal
      
      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)
      
      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)