Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

SAT算法

SAT 是处理凸多边形 (Convex Polygons)凸多面体 (Convex Polyhedra) 之间碰撞检测的强大工具。其核心思想非常优雅:

如果能找到一条轴,使得两个凸体在该轴上的投影(一维区间)不发生重叠,那么这两个凸体就没有发生碰撞。反之,如果对于所有需要测试的候选轴,两个凸体的投影都发生重叠,那么它们必然发生了碰撞。

这条能将两个凸体投影分开的轴,就被称为分离轴 (Separating Axis)

1. 候选轴 (Candidate Axes)

对于两个凸多面体 A 和 B,需要测试的候选轴是:

  • 物体 A 的所有面的法线。
  • 物体 B 的所有面的法线。
  • 物体 A 的每一条边与物体 B 的每一条边的叉积 (Cross Product)。

2. 投影与测试

对于每一个候选轴 ,我们需要:

  1. 计算物体 A 的所有顶点在轴 上的投影,找到投影区间的最小值 和最大值
  2. 同样计算物体 B 在轴 上的投影区间
  3. 检查这两个区间是否重叠。如果不重叠(即 ),则我们找到了一个分离轴,可以立即判定两个物体没有碰撞,并终止算法。

如果测试完所有候选轴,它们的投影都发生重叠,那么物体就发生了碰撞。

3. 计算穿透深度和法线

在所有候选轴的投影中,那个重叠量最小的投影,其对应的轴就是最小穿透轴 (Axis of Minimum Penetration)。这个最小的重叠量就是穿透深度 (Penetration Depth),该轴的方向就是接触法线 (Contact Normal)

代码示例 (2D SAT 伪代码):

bool checkSATCollision(const Polygon& A, const Polygon& B, ContactInfo& out_contact) {
    float minOverlap = FLT_MAX;
    Vector2 mtvAxis; // Minimum Translation Vector Axis

    // 1. 获取所有候选轴 (对于2D凸多边形,就是边的法线)
    std::vector<Vector2> axes = getCandidateAxes(A, B);

    for (const auto& axis : axes) {
        // 2. 计算在轴上的投影
        Projection pA = A.project(axis);
        Projection pB = B.project(axis);

        // 3. 检查投影是否重叠
        float overlap = pA.getOverlap(pB);
        if (overlap <= 0) {
            // 找到了分离轴,没有碰撞
            return false;
        }

        // 4. 记录最小重叠量
        if (overlap < minOverlap) {
            minOverlap = overlap;
            mtvAxis = axis;
        }
    }

    // 如果所有轴都重叠,则发生碰撞
    out_contact.penetrationDepth = minOverlap;
    out_contact.normal = mtvAxis.normalized();
    // ... (需要额外逻辑来确保法线方向正确,并计算接触点)
    return true;
}
  • 优点: 原理清晰,对于边和面数量不多的凸体非常高效。能同时得到碰撞结果、穿透深度和法线。
  • 缺点: 随着顶点数的增加,候选轴的数量会急剧增长(特别是边-边叉积部分),导致性能下降。不适用于非凸体或曲面。