是否存在金字塔形光束与AABB的相交检测算法?
轴对齐包围盒(AABB)与金字塔形光束的相交检测方法
核心思路
你描述的金字塔形光束本质是无限延伸的凸视锥体,可以用**分离轴定理(SAT)**实现高效的相交检测——SAT是凸体相交检测的标准方法,只需判断两个凸体在所有潜在分离轴上的投影是否重叠,若全部重叠则相交。
算法步骤
1. 构建光束的局部坐标系
首先为光束建立局部坐标系,方便后续计算:
- 原点:光束的
Origin - Z轴:归一化的
Direction向量 - X轴(宽度方向):构造一个与Z轴垂直的向量并归一化,避免与Z轴共线的情况:
glm::vec3 U; if (fabs(Direction.y) < fabs(Direction.x) && fabs(Direction.y) < fabs(Direction.z)) { U = glm::cross(glm::vec3(0, 1, 0), Direction); } else { U = glm::cross(glm::vec3(1, 0, 0), Direction); } U = glm::normalize(U); - Y轴(高度方向):由Z轴叉乘X轴得到,自动归一化:
glm::vec3 V = glm::cross(Direction, U);
2. 生成分离轴
根据SAT,需要检测7个潜在分离轴:
- 3个AABB的世界坐标轴:
(1,0,0)、(0,1,0)、(0,0,1) - 4个光束侧面的法向量:
Axis1 = U - unit_width * Direction Axis2 = -U - unit_width * Direction Axis3 = V - unit_height * Direction Axis4 = -V - unit_height * Direction
3. 投影区间重叠检测
对每个分离轴,分别计算AABB和光束的投影区间,判断是否重叠:
计算AABB的投影区间
推荐使用中心+半尺寸的AABB表示,计算更高效:
设AABB中心为C,半尺寸为H,当前分离轴为Axis,则投影区间为:
float proj_center = glm::dot(C, Axis); float proj_extent = glm::dot(glm::abs(H), glm::abs(Axis)); float aabb_min = proj_center - proj_extent; float aabb_max = proj_center + proj_extent;
计算光束的投影区间
光束是无限延伸的凸体,其投影区间取决于轴与Direction的夹角:
float proj_origin = glm::dot(Origin, Axis); float dot_dir = glm::dot(Direction, Axis); // 光束在轴上的投影区间 float frustum_min, frustum_max; if (dot_dir > 0) { // 沿轴正方向无限延伸 frustum_min = proj_origin; frustum_max = INFINITY; } else if (dot_dir < 0) { // 沿轴负方向无限延伸 frustum_min = -INFINITY; frustum_max = proj_origin; } else { // 轴与光束方向垂直,投影覆盖全范围 frustum_min = -INFINITY; frustum_max = INFINITY; }
判断区间重叠
只要满足aabb_max >= frustum_min且aabb_min <= frustum_max,则该轴上投影重叠。
4. 最终判定
若所有7个分离轴的投影区间都重叠,则AABB与光束相交;否则不相交。
优化技巧
- 快速排斥预检测:先计算AABB所有顶点在
Direction上的投影(即(P - Origin)·Direction),若所有值都小于0,说明AABB在光束后方,直接判定不相交。 - 跳过归一化:分离轴无需归一化,因为投影的相对大小不影响重叠判断,可节省计算量。
AABB表示选择
优先选择中心+半尺寸的表示,相比最小/最大坐标,它能快速计算任意轴上的投影区间,无需遍历8个顶点,大幅提升检测效率。
内容的提问来源于stack exchange,提问作者Chribit
相关产品推荐
相关产品推荐

