如何判断三维空间中直线是否与立方体相交(不含顶点触碰)
三维直线与立方体相交判断实现方案
1. 先将立方体转换为轴对齐边界框(AABB)
从给定的8个立方体顶点中,提取三个坐标轴方向的极值,得到简化的边界框:
- 遍历所有顶点的x坐标,计算最小值
x_min和最大值x_max - 同理得到
y_min/y_max、z_min/z_max
转换为AABB后,无需处理复杂的顶点-面关系,大幅简化计算逻辑。
2. 用Slab方法(轴分离定理)判断相交性
Slab方法是高效判断直线与AABB相交的经典算法,核心是将直线投影到三个坐标轴,检查投影区间与AABB的投影区间是否重叠。
具体步骤
设直线端点为P0(x0,y0,z0)、P1(x1,y1,z1),方向向量D = (dx, dy, dz) = P1 - P0,参数t的取值范围t∈[0,1]对应线段部分,超出范围对应直线延长线:
- 初始化
t_min = 0,t_max = 1 - 依次处理x、y、z三个轴:
- 若方向向量分量
d ≈ 0(浮点精度用1e-8判断):- 若
P0的对应坐标不在[min, max]区间内,直线平行于该轴且在AABB外,直接返回不相交
- 若
- 否则:
- 计算
t1 = (min - P0对应坐标) / d,t2 = (max - P0对应坐标) / d - 确保
t1 ≤ t2,否则交换两者 - 更新
t_min = max(t_min, t1),t_max = min(t_max, t2) - 若
t_min > t_max,说明该轴投影无重叠,返回不相交
- 计算
- 若方向向量分量
- 处理完三轴后,若
t_min ≤ t_max,需进一步排除仅触碰顶点的情况:- 计算
t_min和t_max对应的交点P_min = P0 + t_min*D、P_max = P0 + t_max*D - 判断两个交点是否均为立方体顶点(用
1e-8阈值判断坐标相等):- 若两个交点都是顶点,且
t_min/t_max对应线段端点(即t∈{0,1}),或两个交点重合为单个顶点,返回不相交 - 否则返回相交
- 若两个交点都是顶点,且
- 计算
3. 浮点精度处理要点
所有坐标比较、相等判断必须引入极小epsilon值(如1e-8),避免因浮点计算误差导致的误判。
示例伪代码
def line_intersects_cube(cube_vertices, p0, p1): # 计算AABB边界 x_coords = [v[0] for v in cube_vertices] y_coords = [v[1] for v in cube_vertices] z_coords = [v[2] for v in cube_vertices] x_min, x_max = min(x_coords), max(x_coords) y_min, y_max = min(y_coords), max(y_coords) z_min, z_max = min(z_coords), max(z_coords) dx = p1[0] - p0[0] dy = p1[1] - p0[1] dz = p1[2] - p0[2] epsilon = 1e-8 t_min = 0.0 t_max = 1.0 # 处理x轴 if abs(dx) < epsilon: if p0[0] < x_min - epsilon or p0[0] > x_max + epsilon: return False else: t1 = (x_min - p0[0]) / dx t2 = (x_max - p0[0]) / dx if t1 > t2: t1, t2 = t2, t1 t_min = max(t_min, t1) t_max = min(t_max, t2) if t_min > t_max + epsilon: return False # 处理y轴 if abs(dy) < epsilon: if p0[1] < y_min - epsilon or p0[1] > y_max + epsilon: return False else: t1 = (y_min - p0[1]) / dy t2 = (y_max - p0[1]) / dy if t1 > t2: t1, t2 = t2, t1 t_min = max(t_min, t1) t_max = min(t_max, t2) if t_min > t_max + epsilon: return False # 处理z轴 if abs(dz) < epsilon: if p0[2] < z_min - epsilon or p0[2] > z_max + epsilon: return False else: t1 = (z_min - p0[2]) / dz t2 = (z_max - p0[2]) / dz if t1 > t2: t1, t2 = t2, t1 t_min = max(t_min, t1) t_max = min(t_max, t2) if t_min > t_max + epsilon: return False # 判断点是否为立方体顶点 def is_vertex(point): for v in cube_vertices: if (abs(point[0]-v[0]) < epsilon and abs(point[1]-v[1]) < epsilon and abs(point[2]-v[2]) < epsilon): return True return False p_min = (p0[0] + t_min*dx, p0[1] + t_min*dy, p0[2] + t_min*dz) p_max = (p0[0] + t_max*dx, p0[1] + t_max*dy, p0[2] + t_max*dz) min_is_vtx = is_vertex(p_min) max_is_vtx = is_vertex(p_max) # 排除仅触碰顶点的情况 if min_is_vtx and max_is_vtx: # 两个交点为同一顶点,或线段连接两个顶点 if abs(t_min - t_max) < epsilon or \ ((t_min < epsilon or abs(t_min-1) < epsilon) and (t_max < epsilon or abs(t_max-1) < epsilon)): return False # 排除线段端点仅触碰单个顶点的情况 if (t_min < epsilon and min_is_vtx) and (t_max > 1 - epsilon and max_is_vtx): return False return True
特殊情况处理
如果立方体不是轴对齐的,需要先对直线和立方体做相同的旋转变换,将立方体转换为轴对齐状态后,再使用上述方法判断。
内容的提问来源于stack exchange,提问作者Jelly Qwerty
相关产品推荐
相关产品推荐

