测试线段是否与球体相交
我试图确定线段(即两点之间)是否与球体相交。我对相交的位置不感兴趣,只对线段是否与球体表面相交感兴趣。有人对最有效的算法有什么建议吗? (我想知道是否有比通常的射线球相交算法更简单的算法,因为我对相交位置不感兴趣)
I am trying to determine whether a line segment (i.e. between two points) intersects a sphere. I am not interested in the position of the intersection, just whether or not the segment intersects the sphere surface. Does anyone have any suggestions as to what the most efficient algorithm for this would be? (I'm wondering if there are any algorithms that are simpler than the usual ray-sphere intersection algorithms, since I'm not interested in the intersection position)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
如果您只对知道它是否相交感兴趣,那么您的基本算法将如下所示......
考虑您有射线线的向量,A -> > B.
您知道该矢量与球体中心之间的最短距离出现在射线矢量与穿过球体中心的与其成 90 度的矢量的交点处。
因此,你有两个向量,其方程完全定义。您可以使用线性代数计算出向量的交点,从而计算出直线的长度(或更有效地计算直线长度的平方),并测试它是否小于半径(或半径的平方) )你的领域。
If you are only interested if knowing if it intersects or not then your basic algorithm will look like this...
Consider you have the vector of your ray line, A -> B.
You know that the shortest distance between this vector and the centre of the sphere occurs at the intersection of your ray vector and a vector which is at 90 degrees to this which passes through the centre of the sphere.
You hence have two vectors, the equations of which fully completely defined. You can work out the intersection point of the vectors using linear algebra, and hence the length of the line (or more efficiently the square of the length of the line) and test if this is less than the radius (or the square of the radius) of your sphere.
我不知道这样做的标准方法是什么,但如果您只想知道它是否相交,这就是我会做的。
一般规则...避免执行 sqrt() 或其他昂贵的操作。如果可能的话,处理半径的平方。
从这里开始,你的起点就在球体之外。
从现在开始,你就进入了更加复杂的阶段。您的起点位于假想的盒子和球体之间。您可以使用微积分和几何得到简化的表达式。
您要做的要点是确定射线和球体之间的最短距离是否小于球体的半径。
让射线由 (x0 + it, y0 + jt, z0 + kt) 表示,球体中心位于 (xS, yS, zS)。因此,我们希望找到 t,使其给出 (xS - x0 - it,yS - y0 - jt,zS - z0 - kt) 中最短的一个。
设 x = xS - x0、y = yX - y0、z = zS - z0、D = 向量幅度平方
D = x^2 -2*xit + (i*t) ^2 + y^2 - 2*yjt + (j*t)^2 + z^2 - 2*zkt + (k*t) ^2
D = (i^2 + j^2 + k^2)t^2 - (xi + yj + zk)*2*t + (x^2 + y^2 + z^2)
dD/dt = 0 = 2*t*(i^2 + j^2 + k^2) - 2*(xi + yj + z*k)
t = (xi + yj + z*k) / (i^2 + j^2 + k^2)
将 t 代入方程,得到 D = ....如果结果小于或等于球体半径的平方,则有一个交集。如果更大,则不存在交集。
I don't know what the standard way of doing it is, but if you only want to know IF it intersects, here is what I would do.
General rule ... avoid doing sqrt() or other costly operations. When possible, deal with the square of the radius.
From here on, your starting point is outside the sphere.
From here on, you are in the more complicated phase. Your starting point is between the imaginary box and the sphere. You can get a simplified expression using calculus and geometry.
The gist of what you want to do is determine if the shortest distance between your ray and the sphere is less than radius of the sphere.
Let your ray be represented by (x0 + it, y0 + jt, z0 + kt), and the centre of your sphere be at (xS, yS, zS). So, we want to find t such that it would give the shortest of (xS - x0 - it, yS - y0 - jt, zS - z0 - kt).
Let x = xS - x0, y = yX - y0, z = zS - z0, D = magnitude of the vector squared
D = x^2 -2*xit + (i*t)^2 + y^2 - 2*yjt + (j*t)^2 + z^2 - 2*zkt + (k*t)^2
D = (i^2 + j^2 + k^2)t^2 - (xi + yj + zk)*2*t + (x^2 + y^2 + z^2)
dD/dt = 0 = 2*t*(i^2 + j^2 + k^2) - 2*(xi + yj + z*k)
t = (xi + yj + z*k) / (i^2 + j^2 + k^2)
Plug t back into the equation for D = .... If the result is less than or equal the square of the sphere's radius, you have an intersection. If it is greater, then there is no intersection.
此页面针对此问题提供了精确的解决方案。本质上,您是将直线方程代入球体方程,然后计算所得二次方程的判别式。判别式的值表示交集。
This page has an exact solution for this problem. Essentially, you are substituting the equation for the line into the equation for the sphere, then computes the discriminant of the resulting quadratic. The values of the discriminant indicate intersection.
13年后你还在寻找答案吗?这是一个完整且简单的解决方案
假设如下:
v1
和v2
vc
为中心> 以半径r
定义三角形
ABC
的三个边长为:A = v1-vc
B = v2-vc
C = v1-v2
如果
|A| < r
或|B| < r
,然后我们完成了;线段与球体相交完成上述检查后,如果
A
和B
之间的角度为锐角,则完成;线段不与球体相交。如果这两个条件都不满足,则线段可能会或可能不会与球体相交。为了找到答案,我们只需要找到
H
,它是以C
为底的三角形ABC
的高度。首先我们需要φ
,即A
和C
之间的角度:然后求解
H
:我们就完成了。结果是
H < r
,则线段与球体相交,H = r
,则如果H 。 r
,则线段不与球体相交这是用 Python 编写的:
请注意,我已经编写了此代码,以便当任一端点位于球体表面时返回 False,或者当线段与球体相切时,因为它更符合我的目的。
这可能本质上就是用户 Cruachan 的建议。那里的评论表明其他答案“过于复杂”。可能有一种更优雅的方法来实现这一点,即使用更紧凑的线性代数运算和恒等式,但我怀疑所需的实际计算量可以归结为这样的东西。如果有人发现可以节省一些精力的地方,请告诉我们。
这是代码的测试。下图显示了从
(-1, 1, 1)
位置出发的几条试验线段,单位球体位于(1,1,1)
处。蓝色线段已相交,红色线段未相交。这是另一幅图,它验证了在球体表面附近停止的线段不相交,即使它们所属的无限射线相交:
此处是生成图像的代码:
Are you still looking for an answer 13 years later? Here is a complete and simple solution
Assume the following:
v1
andv2
vc
with radiusr
Ne define the three side lengths of a triangle
ABC
as:A = v1-vc
B = v2-vc
C = v1-v2
If
|A| < r
or|B| < r
, then we're done; the line segment intersects the sphereAfter doing the check above, if the angle between
A
andB
is acute, then we're done; the line segment does not intersect the sphere.If neither of these conditions are met, then the line segment may or may not intersect the sphere. To find out, we just need to find
H
, which is the height of the triangleABC
takingC
as the base. First we needφ
, the angle betweenA
andC
:and then solve for
H
:and we are done. The result is
H < r
, then the line segment intersects the sphereH = r
, then the line segment is tangent to the sphereH > r
, then the line segment does not intersect the sphereHere that is in Python:
Note that I have written this so that it returns
False
when either endpoint is on the surface of the sphere, or when the line segment is tangent to the sphere, because it serves my purposes better.This might be essentially what user Cruachan suggested. A comment there suggests that other answers are "too elaborate". There might be a more elegant way to implement this that uses more compact linear algebra operations and identities, but I suspect that the amount of actual compute required boils down to something like this. If someone sees somewhere to save some effort please do let us know.
Here is a test of the code. The figure below shows several trial line segments originating from a position
(-1, 1, 1)
, with a unit sphere at(1,1,1)
. Blue line segments have intersected, red have not.And here is another figure which verifies that line segments that stop just short of the sphere's surface do not intersect, even if the infinite ray that they belong to does:
Here is the code that generates the image:
如果您只需要知道是否相交,而不需要知道相交点,有一种方法可以轻松做到这一点:
请注意,如果段完全包含在球体中。我发现这很有用,但如果你不这样做,只要确保在两个端点都在球体中时返回 false 即可。
v.sq_mag()
计算v
的平方幅值。在这种情况下,使用平方值会更有效,因为它避免了调用 sqrt。
cross(a, b)
计算a
和b
之间的叉积。有一个部门,但如果您愿意,您可以重构以轻松删除它。
我在Wolfram上找到了点线距离公式。如果您想了解它,那里有信息: https://mathworld.wolfram.com /Point-LineDistance3-Dimensional.html
最后一部分关于两个不重叠的直角三角形可以直观地理解:
If you just need to know whether it intersects, and do not need to know the intersection point, there is a way to do it without much difficulty:
Note that this algorithm as formulated above also returns true if the segment is entirely contained in the sphere. I found this useful, but if you do not, just make sure to return false if both endpoints are in the sphere.
v.sq_mag()
computes the square magnitude ofv
.Working with the square magnitude is more efficient in cases like this since it avoids the need to call sqrt.
cross(a, b)
computes the cross product betweena
andb
.There is one division, but you could refactor to remove it quite easily if you wanted to.
I found the point-line distance formula on Wolfram. There is information there if you want to understand it: https://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html
The last part about the two non-overlapping right triangles can be understood visually:
如果你想要准确性,无论如何你都必须在这个位置上工作。从算法上提高速度的唯一方法是从射线球体相交切换到射线边界框相交。
或者您可以更深入地尝试改进 sqrt 和其他内部函数调用
http://wiki.cgsociety .org/index.php/Ray_Sphere_Intersection
you sorta have to work that the position anyway if you want accuracy. The only way to improve speed algorithmically is to switch from ray-sphere intersection to ray-bounding-box intersection.
Or you could go deeper and try and improve sqrt and other inner function calls
http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection