Thibaut Lagoda
Thibaut Lagoda

Reputation: 11

Why does my Separating Axis Theorem fail near world axes? Unity

Problem Description: I am implementing collision detection between an OBB and an AABB using the Separating Axis Theorem (SAT) in Unity. The algorithm works perfectly in most cases, but I have noticed an issue:

When the AABB is close to a world axis (e.g., X ≈ 0, Y ≈ 0, or Z ≈ 0), collisions are not detected properly. Objects that should be colliding pass through each other.

=> Expected behavior: The SAT should detect collisions reliably regardless of world position. => Actual behavior: Collisions fail when the AABB is near Unity's world axes.

Here is my code:

public bool IntersectAABB(AABBObstacle aabb)
    {
        Vector3[] aabbCorners = aabb.GetCorners();

        Vector3[] aabbAxes = new Vector3[] { Vector3.right, Vector3.up, Vector3.forward };

        Vector3[] obbAxes = new Vector3[]
        {
            rotation * Vector3.right,
            rotation * Vector3.up,
            rotation * Vector3.forward
        };

        List<Vector3> axesToTest = new();

        axesToTest.AddRange(aabbAxes);

        axesToTest.AddRange(obbAxes);

        for (int i = 0; i < 3; i++)
        {
            for (int j = 0; j < 3; j++)
            {
                Vector3 axis = Vector3.Cross(obbAxes[i], aabbAxes[j]);
                if (axis != Vector3.zero)
                    axesToTest.Add(axis.normalized);
            }
        }

        foreach (Vector3 axis in axesToTest)
        {
            if (IsSeparatingAxis(axis, corners, aabbCorners))
            {
                return false;
            }
        }

        return true;
    }

    private bool IsSeparatingAxis(Vector3 axis, Vector3[] obbCorners, Vector3[] aabbCorners)
    {
        if (axis == Vector3.zero)
            return false;

        float obbMin = float.MaxValue,
            obbMax = float.MinValue;
        float aabbMin = float.MaxValue,
            aabbMax = float.MinValue;

        foreach (Vector3 corner in obbCorners)
        {
            float projection = Vector3.Dot(corner, axis);
            obbMin = Mathf.Min(obbMin, projection);
            obbMax = Mathf.Max(obbMax, projection);
        }

        foreach (Vector3 corner in aabbCorners)
        {
            float projection = Vector3.Dot(corner, axis);
            aabbMin = Mathf.Min(aabbMin, projection);
            aabbMax = Mathf.Max(aabbMax, projection);
        }

        return obbMax < aabbMin || aabbMax < obbMin;
    }

I've logged all projection values, min/max intervals, and separating axis tests, and everything seems coherent. The projections of the OBB and AABB corners onto each axis appear correct, but some values are very close. I added a small tolerance (1e-4f) to the interval checks, which reduced false negatives but didn’t fully solve the issue.

I also discarded near-zero cross-product axes (sqrMagnitude < 1e-6f), but it had no effect.

Thank you for your help!

Upvotes: 1

Views: 16

Answers (0)

Related Questions