我正在尝试确定一条线段(即两个点之间的线段)是否与球相交。我不关心交点的位置,只是想知道线段是否与球面相交。是否有人对此有什么建议,最有效的算法是什么?(我想知道是否有比通常的光线-球相交算法更简单的算法,因为我不关心交点位置)
我正在尝试确定一条线段(即两个点之间的线段)是否与球相交。我不关心交点的位置,只是想知道线段是否与球面相交。是否有人对此有什么建议,最有效的算法是什么?(我想知道是否有比通常的光线-球相交算法更简单的算法,因为我不关心交点位置)
如果您仅对相交与否感兴趣,则基本算法如下所示...
考虑您有射线线段的向量,A->B。
您知道该向量与球心之间的最短距离出现在您的射线向量和一个垂直于此向量并穿过球心的向量的交点处。
因此,您有两个向量,它们的方程完全定义。 您可以使用线性代数计算出向量的交点,从而计算出线段的长度(或更高效地计算出线段长度的平方),并测试是否小于您的球体半径(或半径的平方)。
这个网页提供了一个准确的解决方案。基本上,您将直线方程代入球面方程中,然后计算所得二次方程的判别式。 判别式的值表示相交。
你还在寻找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
之间的夹角:
φ = arccos( dot(A,C) / (|A||C|) )
然后解出 H
:
sin(φ) = H/|A|
===> H = |A|sin(φ) = |A| sqrt(1 - (dot(A,C) / (|A||C|))^2)
我们做完了。结果是
H < r
,则线段与球相交H = r
,则线段切球H > r
,则线段不与球相交下面是Python代码:
import numpy as np
def unit_projection(v1, v2):
'''takes the dot product between v1, v2 after normalization'''
u1 = v1 / np.linalg.norm(v1)
u2 = v2 / np.linalg.norm(v2)
return np.dot(u1, u2)
def angle_between(v1, v2):
'''computes the angle between vectors v1 and v2'''
return np.arccos(np.clip(unit_projection(v1, v2), -1, 1))
def check_intersects_sphere(xa, ya, za, xb, yb, zb, xc, yc, zc, radius):
'''checks if a line segment intersects a sphere'''
v1 = np.array([xa, ya, za])
v2 = np.array([xb, yb, zb])
vc = np.array([xc, yc, zc])
A = v1 - vc
B = v2 - vc
C = v1 - v2
if(np.linalg.norm(A) < radius or np.linalg.norm(B) < radius):
return True
if(angle_between(A, B) < np.pi/2):
return False
H = np.linalg.norm(A) * np.sqrt(1 - unit_projection(A, C)**2)
if(H < radius):
return True
if(H >= radius):
return False
False
,因为这样更符合我的需求。(-1, 1, 1)
起始的几条试验线段,以及一个位于(1,1,1)
处的单位球。蓝色的线段相交,红色的线段没有相交。
这里还有另一个图,验证了止于球面表面前的线段不相交,即使它们所属的无限射线相交:
这是生成图像的代码:import matplotlib.pyplot as plt
radius = 1
xc, yc, zc = 1, 1, 1
xa, ya, za = xc-2, yc, zc
nx, ny, nz = 4, 4, 4
xx = np.linspace(xc-2, xc+2, nx)
yy = np.linspace(yc-2, yc+2, ny)
zz = np.linspace(zc-2, zc+2, nz)
n = nx * ny * nz
XX, YY, ZZ = np.meshgrid(xx, yy, zz)
xb, yb, zb = np.ravel(XX), np.ravel(YY), np.ravel(ZZ)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for i in range(n):
if(xb[i] == xa): continue
intersects = check_intersects_sphere(xa, ya, za, xb[i], yb[i], zb[i], xc, yc, zc, radius)
color = ['r', 'b'][int(intersects)]
s = [0.3, 0.7][int(intersects)]
ax.plot([xa, xb[i]], [ya, yb[i]], [za, zb[i]], '-o', color=color, ms=s, lw=s, alpha=s/0.7)
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x = np.outer(np.cos(u), np.sin(v)) + xc
y = np.outer(np.sin(u), np.sin(v)) + yc
z = np.outer(np.ones(np.size(u)), np.cos(v)) + zc
ax.plot_surface(x, y, z, rstride=4, cstride=4, color='k', linewidth=0, alpha=0.25, zorder=0)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.tight_layout()
plt.show()
如果你想要准确性,你基本上必须处理那个位置。算法上提高速度的唯一方法是从射线-球体相交改为射线-边界框相交。
或者你可以深入研究并试图提升sqrt和其他内部函数调用。