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

发布于 2024-09-26 09:39:49 字数 837 浏览 6 评论 0 原文

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

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

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

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

示例

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

I am building a simple raytracer for educational purposes and want to add refraction to objects. Using Snells Law, I am able to create a new ray recursively at the intersection points. The raytracer supports currently only spheres and I use a scene where I have multiple spheres nested inside of each other with different refraction indices.

If I start a ray from outside of the spheres, everything seems simple. You start with the refraction index of the scene, and as soon as you hit the first sphere, refract the ray using the refraction index before and the refraction index of the material of the sphere until you hit the next sphere and so on. Using the normals of the intersection I can determine whether I enter or leave the sphere.

However, I don't understand how I should handle sphere leaves and what to do if the ray doesn't start in the outer part of the scene.

  • Can I just take a stack of the refraction indices and go one layer up as soon as I leave a sphere?
  • How can I determine with what refraction index I have to start if I start inside of the spheres?

Example

You have three spheres, with refraction indices 0.9, 1.1 and 0.8 from outer to inner. Air index is 1.0

  • Your camera is outside of the sphere and points at the center of the sphere:

    • start index is 1.0, you first hit the outer sphere with index 0.9 and refract from 1.0 to 0.9 and save that your ray is now in 0.9 material
    • you hit the middle sphere and notice the material constant of 1.1, since you have saved the 0.9, you know that you have to refract from 0.9 to 1.1 and save the 1.1 in addition to the 0.9
    • you hit the inner sphere and refract from 1.1 to 0.8 and you have save until now 0.9, 1.1 and 0.8
    • you hit the inner sphere again (this time you exit it, so you check your saved values and know that you have to switch back to 1.1)
    • ... until you are outside
  • Problem now, when the camera is inside the sphere. You won't know to what refraction index you have to switch.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

大海や 2024-10-03 09:39:49

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

设计

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

方法:

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

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

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

方法

  • contains(ray) - 如果光线原点位于对象内部,则返回 True;如果在表面,则返回 False;如果在
  • ray_is_on_surface(ray) 外部,则返回 False code> - 如果光线仅在表面上,则返回 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) 通过在 iobject 列表中向前查找一个对象来找到 n2,例如用伪代码:

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/ 获取丹尼尔法雷尔/pvtrace
替代文本

I have a similar ray tracer (written in Python) and stumbled over the same problem: in order to correctly work out the physics one must know the refractive index at each side of the intersection boundary. This took quite a while to solve elegantly, but in the end I went with this solution/design:

Design

1) Scene -- I have a master scene object (basically an array of all objects in the scene), you will probably have something similar. It stores geometrical objects.

Methods:

  • intersection_points(ray) - returns a list of all intersection points, sorted by distance from the ray.
  • intersection_objects(ray) - returns a list of all intersection objects, sorted by distance from the ray.
  • containing_object(ray) - returns the object that contains the ray.
  • objects() - returns a list of all the objects in arbitrary order.

Note: The scene adds an extra object to the list: the Scene_Boundary. This is a giant box (or Sphere) that encapsulate the whole scene i.e. EVERYTHING is inside this boundary.

2) Objects -- Make the geometric objects (e.g. your sphere) implement these methods.

Methods:

  • contains(ray) - returns True is the ray origin is inside the object, False if on the surface and False if outside
  • ray_is_on_surface(ray) - returns True is the ray is on the surface only, otherwise False.
  • intersection_points(ray) - returns the intersection point(s) that the ray makes with the object
  • surface_normal(ray) - returns the surface normal vector of the surface which the ray struck (this will help with Fresnel reflection and refraction)

For optical calculations the objects must also have a refractive index.

Instance variables:

  • refractive_index

Boundary Problem

The problem we want to solve: what is the refractive index inside (n1) and outside (n2) of the boundary? To do this we follow this procedure:

1) Trace the ray through the whole scene:

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]

Remember these are sorted by distance from the ray origin. The last item in ipoints and iobjects is the intersection the ray makes with the scene boundary. We will use this later!

2) n1 is found simply by finding the containing object, e.g.:

obj1 = scene.containing_object(ray) # Scene_Boundary
n1 = obj1.refractive_index() # n1 = 1. Scene_Boundary always has refractive index of Air

3) n2 is found by looking one object ahead in the iobject list, e.g. in pseudocode:

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

4) Get the surface normal for later use:

normal = obj1.surface_normal(ray)

You have all the information you need to calculate the correct reflection and refraction. This is general enough to work even if the ray is outside the object, but occasionally I did need to implement some logical filtering to make the algorithm more robust, but that's basically it!

Reflection and Refraction

You can reflect a vector just by knowing the surface normal. In Python using numpy I do it like this,

def reflect_vector(normal, vector):
   d = numpy.dot(normal, vector)
   return vector - 2 * d * normal

Refraction (as discussed) needs n1 and n2 values:

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)

Finally you will need to calculate the reflection coefficient for the ray where angle is the angle between the ray direction and the surface normal (this assumed your ray is 'unpolarised'). Compare this with a random number between 0 and 1 to decide if reflection occurs.

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)

Final comments

This all comes from my Python optical ray tracing project which is not yet released(!), but you can check here for some detail: http://daniel.farrell.name/freebies/pvtrace. I like Python! There are a number of Python ray tracing projects listed here, http://groups.google.com/group/python-ray-tracing-community/web/list-of-python-statistical-ray-tracers . Finally, be careful with fractional refractive indices in your example, the equation will breakdown.

Update

Screenshot of this implemented in my ray tracer, available at http://github.com/danieljfarrell/pvtrace
alt text

往事风中埋 2024-10-03 09:39:49

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

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

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

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

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

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

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

Posting this from a physics point of view and not a raytracing implementation point of view :P.

Snell's law states that the ratio of the sines of the incident angle and the refractive angle are equal to the inverse of the ratio of the refractive index of the two mediums on either side of the boundary.

Thus, when you have a ray approaching a new material and you want to know the angle in the new material, you need to know the angle that the ray is hitting the new material, the index of refraction of the new material, and the index of refraction of the material the ray is currently in.

As you say refraction works fine moving into the sphere, you must know the index of refraction of each sphere and your scene already.

I'd say creating a stack of refractive indices would be a good way to deal with going into a bunch of nested materials, as you're going to have to touch all the refractive indexes that you push onto the stack again as you move out of the nested set of spheres.

As to determining what refractive index you have to start with as you leave the spheres, you're always saying sin(theta1)/sin(theta2) = [refractive index of 2]/[refractive index of 1]. Thus, you need the refractive index of the material that you're currently in and the index of the material that you're going to be moving towards.

Apologies if I misunderstood your question, but I hope that helps!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文