Unity体素化算法开发:立方体与三角形相交检测代码解析求助
Unity体素化中AABB与三角形相交检测的解析与简洁实现
Hey there! 我知道在体素化开发里,AABB(轴对齐包围盒)和三角形的相交检测是个基础但关键的环节——毕竟体素化本质就是把三维模型拆成一个个小立方体,得逐个判断哪些立方体和模型的三角面相交对吧?下面我就给你拆解核心原理,再给你一套简洁好懂的Unity适配实现。
核心原理:分离轴定理(SAT)
这是当前检测凸体相交的主流算法,对于AABB和三角形这种凸形状完全适用,逻辑特别清晰:
两个凸体不相交的充要条件是,存在一个分离轴,使得两个物体在这个轴上的投影区间没有重叠。反过来,如果所有可能的分离轴上投影都重叠,那两个物体一定相交。
针对AABB和三角形的组合,我们只需要测试13个潜在分离轴(优化后可以更少,先讲全量逻辑):
- AABB自身的三个坐标轴(X、Y、Z轴):因为AABB是轴对齐的,这三个轴是最容易计算的快速检测轴
- 三角形的法线轴:如果三角形完全在AABB的某一侧,法线轴就能把它们分开
- 三角形的三条边分别与AABB的三条边做叉乘得到的9个轴:这些轴用来检测三角形的边是否“穿透”AABB的面,或者反过来
现有代码的通用解析(假设你用的是SAT实现)
如果你的现有代码是基于SAT的,那流程大概是这样的:
- 快速排斥预检测:先计算三角形的包围盒,和目标AABB做快速重叠判断——如果两个包围盒都不碰,直接返回
false,节省后续计算 - 遍历所有分离轴:对每个潜在分离轴,分别计算AABB和三角形在该轴上的投影区间
- 判断投影重叠:只要有任意一个轴上的投影不重叠,立刻返回
false;所有轴都重叠的话,返回true
更简洁的Unity适配实现
下面给你一套用C#写的、适配Unity的简化版代码,去掉了冗余计算,注释也很清楚:
using UnityEngine; public static class IntersectionChecker { // 检测AABB与三角形是否相交 public static bool AABBIntersectsTriangle(Bounds aabb, Vector3 v0, Vector3 v1, Vector3 v2) { // 步骤1:快速排斥预检测 Bounds triBounds = new Bounds((v0 + v1 + v2)/3f, Vector3.zero); triBounds.Encapsulate(v0); triBounds.Encapsulate(v1); triBounds.Encapsulate(v2); if (!aabb.Intersects(triBounds)) return false; // 步骤2:分离轴测试 Vector3[] axes = new Vector3[13]; // AABB的三个坐标轴 axes[0] = Vector3.right; axes[1] = Vector3.up; axes[2] = Vector3.forward; // 三角形的法线 axes[3] = Vector3.Cross(v1 - v0, v2 - v0).normalized; // 三角形边与AABB边的叉乘轴 axes[4] = Vector3.Cross(v1 - v0, Vector3.right); axes[5] = Vector3.Cross(v1 - v0, Vector3.up); axes[6] = Vector3.Cross(v1 - v0, Vector3.forward); axes[7] = Vector3.Cross(v2 - v0, Vector3.right); axes[8] = Vector3.Cross(v2 - v0, Vector3.up); axes[9] = Vector3.Cross(v2 - v0, Vector3.forward); axes[10] = Vector3.Cross(v2 - v1, Vector3.right); axes[11] = Vector3.Cross(v2 - v1, Vector3.up); axes[12] = Vector3.Cross(v2 - v1, Vector3.forward); foreach (Vector3 axis in axes) { // 跳过零向量轴(叉乘可能得到零向量,说明边平行) if (axis.sqrMagnitude < 0.0001f) continue; // 计算AABB在该轴上的投影区间 float aabbMin = Vector3.Dot(aabb.min, axis); float aabbMax = Vector3.Dot(aabb.max, axis); if (aabbMin > aabbMax) (aabbMin, aabbMax) = (aabbMax, aabbMin); // 计算三角形在该轴上的投影区间 float triMin = Vector3.Dot(v0, axis); float triMax = triMin; float dot = Vector3.Dot(v1, axis); triMin = Mathf.Min(triMin, dot); triMax = Mathf.Max(triMax, dot); dot = Vector3.Dot(v2, axis); triMin = Mathf.Min(triMin, dot); triMax = Mathf.Max(triMax, dot); // 判断投影是否不重叠(加入浮点误差容忍) if (triMax < aabbMin - 0.0001f || triMin > aabbMax + 0.0001f) return false; } // 所有分离轴都重叠,说明相交 return true; } }
代码优化说明
- 加入了0.0001f的误差容忍:避免因为浮点精度问题导致的误判
- 跳过了零向量轴:当三角形边和AABB轴平行时,叉乘结果是零向量,这种轴不需要测试(因为已经被AABB的坐标轴覆盖了)
- 用Unity内置的
Bounds类做快速排斥:省去了自己计算三角形包围盒的冗余代码
如果你觉得13个轴还是太多,其实可以进一步优化——比如三角形边和AABB轴叉乘的轴,很多情况下可以通过提前判断方向来跳过,但上面的版本已经足够简洁且稳定,适合体素化场景的性能需求。
内容的提问来源于stack exchange,提问作者KIM CHANGJUN
相关产品推荐
相关产品推荐

