OBB3D相交检测

接上一篇 OBB2D相交检测

在2D 中是只要有一个轴、线将两个 OBB 分离到两侧就判定两个 OBB不相交

在3D 中基本类似2D,但是在3D 中要将分离线替换为分离面,就是只要是找到一个面,能将两个 OBB分离在面的两侧,就说明两个OBB不相交。看下图

面P平行于OBB的一个面 ABCD,面P可以将两个 OBB分离到两侧

像面P这样的面理论上可以找到几个?

 首先平行于每个OBB六个面的,因为一个OBB的六个面是俩俩平行的,所以一个OBB可以贡献 3 个面,两个OBB可以贡献 6个面。看下图,画的不像,可以自己脑补想象一下

除了平行于面的面还有下面这种,下面这个将两个OBB分离的面的法向量是 两个红色边叉乘得到的。

两个OBB分别命名为 A、B

则 A 有三个轴 a_axisX,a_axisY, a_axisZ

则 B 有三个轴 b_axisX,b_axisY, b_axisZ

总共有 9 中组合分别为

cross1 = a_axisX 叉乘  b_axisX

cross2 = a_axisX 叉乘  b_axisY

cross3 = a_axisX 叉乘  b_axisZ

cross4 = a_axisY 叉乘  b_axisX

cross5 = a_axisY 叉乘  b_axisY

cross6 = a_axisY 叉乘  b_axisZ

cross7 = a_axisZ 叉乘  b_axisX

cross8 = a_axisZ 叉乘  b_axisY

cross9 = a_axisZ 叉乘  b_axisZ

所以在3D轴分离轴总共有 6 + 9 = 15 中。

点到分离轴的投影方式同 2D 计算相同,只是点坐标和向量的 Vector2 调整为 Vector3

2D 中一个OBB 有两个轴,四个顶点

3D 中一个OBB 有三个轴,八个顶点

代码逻辑如下

public class OBB3D
{
    /// <summary>
    ///  X 轴方向向量
    /// </summary>
    public Vector3 _axisX;

    /// <summary>
    /// Y 轴方向向量
    /// </summary>
    public Vector3 _axisY;

    /// <summary>
    /// Z 轴方向向量
    /// </summary>
    public Vector3 _axisZ;

    /// <summary>
    /// 中心点坐标
    /// </summary>
    public Vector3 _center;

    /// <summary>
    /// 三条边长度
    /// </summary>
    public Vector3 _size;

    /// <summary>
    /// 八个顶点坐标
    /// </summary>
    public Vector3[] _vertexs;

    public OBB3D(){   }

    public void Set(Vector3 axisX, Vector3 axisY, Vector3 axisZ, Vector3 center, Vector3 size)
    {
        _axisX = axisX;
        _axisY = axisY;
        _axisZ = axisZ;
        _center = center;
        _size = size;

        _vertexs = new Vector3[8];
        _vertexs[0] = center + (axisX * size.x + axisY * size.y + axisZ * size.z) * 0.5f;
        _vertexs[1] = center + (axisX * size.x - axisY * size.y + axisZ * size.z) * 0.5f;
        _vertexs[2] = center + (axisX * size.x + axisY * size.y - axisZ * size.z) * 0.5f;
        _vertexs[3] = center + (axisX * size.x - axisY * size.y - axisZ * size.z) * 0.5f;
        _vertexs[4] = center + (-axisX * size.x + axisY * size.y - axisZ * size.z) * 0.5f;
        _vertexs[5] = center + (-axisX * size.x - axisY * size.y - axisZ * size.z) * 0.5f;
        _vertexs[6] = center + (-axisX * size.x - axisY * size.y + axisZ * size.z) * 0.5f;
        _vertexs[7] = center + (-axisX * size.x + axisY * size.y + axisZ * size.z) * 0.5f;
    }
}

public class OBB3DIntersect
{
    public bool IsIntersect3D(OBB3D a, OBB3D b)
    {
        Vector3 cross1 = Vector3.Cross(a._axisX, b._axisX);
        Vector3 cross2 = Vector3.Cross(a._axisX, b._axisY);
        Vector3 cross3 = Vector3.Cross(a._axisX, b._axisZ);
        Vector3 cross4 = Vector3.Cross(a._axisY, b._axisX);
        Vector3 cross5 = Vector3.Cross(a._axisY, b._axisY);
        Vector3 cross6 = Vector3.Cross(a._axisY, b._axisZ);
        Vector3 cross7 = Vector3.Cross(a._axisZ, b._axisX);
        Vector3 cross8 = Vector3.Cross(a._axisZ, b._axisY);
        Vector3 cross9 = Vector3.Cross(a._axisZ, b._axisZ);

        // 如果有一个分离轴上不相交,则OBB1 和 OBB2 不相交
        return !(IsNotIntersectInAxis(a._vertexs, b._vertexs, a._axisX)
            || IsNotIntersectInAxis(a._vertexs, b._vertexs, a._axisY)
            || IsNotIntersectInAxis(a._vertexs, b._vertexs, a._axisZ)
            || IsNotIntersectInAxis(a._vertexs, b._vertexs, b._axisX)
            || IsNotIntersectInAxis(a._vertexs, b._vertexs, b._axisY)
            || IsNotIntersectInAxis(a._vertexs, b._vertexs, b._axisZ)
            || IsNotIntersectInAxis(a._vertexs, b._vertexs, cross1)
            || IsNotIntersectInAxis(a._vertexs, b._vertexs, cross2)
            || IsNotIntersectInAxis(a._vertexs, b._vertexs, cross3)
            || IsNotIntersectInAxis(a._vertexs, b._vertexs, cross4)
            || IsNotIntersectInAxis(a._vertexs, b._vertexs, cross5)
            || IsNotIntersectInAxis(a._vertexs, b._vertexs, cross6)
            || IsNotIntersectInAxis(a._vertexs, b._vertexs, cross7)
            || IsNotIntersectInAxis(a._vertexs, b._vertexs, cross8)
            || IsNotIntersectInAxis(a._vertexs, b._vertexs, cross9));
    }

    /// <summary>
    /// OBB 在 axis 轴上没有相交
    /// </summary>
    /// <param name="vertexs1">OBB1 的所有顶点</param>
    /// <param name="vertexs2">OBB2 的所有顶点</param>
    /// <param name="axis">分离轴</param>
    /// <returns></returns>
    private bool IsNotIntersectInAxis(Vector3[] vertexs1, Vector3[] vertexs2, Vector3 axis)
    {
        float[] range1 = VertexProject(vertexs1, axis);
        float[] range2 = VertexProject(vertexs2, axis);
        return range1[0] > range2[1] || range2[0] > range1[1];
    }

    /// <summary>
    /// 顶点在轴上的投影的最小值和最大值
    /// </summary>
    /// <param name="vertexs">顶点</param>
    /// <param name="axis">分离轴</param>
    /// <returns></returns>
    private float[] VertexProject(Vector3[] vertexs, Vector3 axis)
    {
        float[] range = new float[] { float.MaxValue, float.MinValue };
        for (int i = 0; i < vertexs.Length; ++i)
        {
            Vector3 vertex = vertexs[i];
            float dot = Vector3.Dot(vertex, axis);
            range[0] = Math.Min(range[0], dot);
            range[1] = Math.Max(range[1], dot);
        }
        return range;
    }
}

测试代码如下

public class OBB3DCollisionController : MonoBehaviour
{
    // Cube 立方体 A
    public Transform A;
    // Cube 立方体 B
    public Transform B;

    // 定义两个 OBB3D 立方体
    private OBB3D a;
    private OBB3D b;

    // 相交检测逻辑
    private OBB3DIntersect obb3DIntersect;

    void Start()
    {
        // 实例化
        a = new OBB3D();
        b = new OBB3D();
        obb3DIntersect = new OBB3DIntersect();
    }

    private bool result = false;
    void Update()
    {
        // 将两个Cube 的数据分别赋值给 OBB3D
        SetOBB(A, a);
        SetOBB(B, b);

        // 判断两立方体是否相交
        bool intersect = obb3DIntersect.IsIntersect3D(a, b);
        if (result != intersect)
        {
            result = intersect;
            // 如果两个立方体相交,则设置为红色
            A.GetComponent<Renderer>().material.color = result ? Color.red : Color.white;
            B.GetComponent<Renderer>().material.color = result ? Color.red : Color.white;
        }
    }

    private void SetOBB(Transform tr, OBB3D obb)
    {
        // OBB3D 的三个轴分别使用 Cube.transform:right、up、forward
        // OBB3D 的坐标使用 Cube.transform.position
        // OBB3D 的size 使用 Cube.transform.localScale
        obb.Set(tr.right, tr.up, tr.forward, tr.position, tr.localScale);
    }
}

测试如下

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值