You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity体素化算法开发:立方体与三角形相交检测代码解析求助

Unity体素化中AABB与三角形相交检测的解析与简洁实现

Hey there! 我知道在体素化开发里,AABB(轴对齐包围盒)和三角形的相交检测是个基础但关键的环节——毕竟体素化本质就是把三维模型拆成一个个小立方体,得逐个判断哪些立方体和模型的三角面相交对吧?下面我就给你拆解核心原理,再给你一套简洁好懂的Unity适配实现。

核心原理:分离轴定理(SAT)

这是当前检测凸体相交的主流算法,对于AABB和三角形这种凸形状完全适用,逻辑特别清晰:

两个凸体不相交的充要条件是,存在一个分离轴,使得两个物体在这个轴上的投影区间没有重叠。反过来,如果所有可能的分离轴上投影都重叠,那两个物体一定相交。

针对AABB和三角形的组合,我们只需要测试13个潜在分离轴(优化后可以更少,先讲全量逻辑):

  • AABB自身的三个坐标轴(X、Y、Z轴):因为AABB是轴对齐的,这三个轴是最容易计算的快速检测轴
  • 三角形的法线轴:如果三角形完全在AABB的某一侧,法线轴就能把它们分开
  • 三角形的三条边分别与AABB的三条边做叉乘得到的9个轴:这些轴用来检测三角形的边是否“穿透”AABB的面,或者反过来

现有代码的通用解析(假设你用的是SAT实现)

如果你的现有代码是基于SAT的,那流程大概是这样的:

  1. 快速排斥预检测:先计算三角形的包围盒,和目标AABB做快速重叠判断——如果两个包围盒都不碰,直接返回false,节省后续计算
  2. 遍历所有分离轴:对每个潜在分离轴,分别计算AABB和三角形在该轴上的投影区间
  3. 判断投影重叠:只要有任意一个轴上的投影不重叠,立刻返回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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:44:05