java 调用fcl碰撞库 java碰撞检测算法_java计算一个多边形的重心


2D凸多边形碰撞检测算法(二) - GJK(上)

原理

在 Narrow Phase 精细碰撞检测中,除了 SAT ,另外一个就是 GJK(Gilbert–Johnson–Keerthi)算法。它足够高效,且很容易了解它是如何进行碰撞检测的。同样的,它也只适用于 凸多边形

首先,重新回顾一下:当两个物体碰撞,发生部分重叠的时候,我们是怎样让计算机知道他们发生了碰撞呢?在上一篇中,SAT 告诉我们:

如果存在一条直线,能够将两个物体分隔开,则两个物体没有发生碰撞。
反之,如果两个物体发生碰撞,则找不到这一条直线,将两个物体分隔开。

SAT 让我们从 分离

而 GJK ,则是从 重叠

为了方便理解,我们先创建两个相互重叠的多边形,并标记两个多边形重叠的部分:


java 调用fcl碰撞库 java碰撞检测算法_碰撞检测_02


目光聚焦到红色的重叠部分,并想一想,它的几何意义究竟是什么?

我们在中学就已经学过,两个直线相交,会产生一个点。这个点都在这两条直线上,这意味着,反应在坐标系上,两条直线享有一个 共同的坐标 。而对于平面,就是无数条直线构成。两个图形产生了重叠,意味着他们有着一组共同的点, 共享一组坐标

而产生碰撞的条件是,两个图形必须 至少重合一个点

“是否能从两个图形中,各自找到一个点,使得它们相减后为原点?”

这就是 GJK 算法的核心目的。当两个图形发生重叠时,他们 必然会有一个坐标,相减后为原点

计算机想要“看见”两个图形重叠的部分,可以利用自身的计算优势,把左边整个图形所包含的坐标 减去 右边图形所包含的坐标,并得到一系列的点。将所有计算后的点包围起来,如果这个新生成的图形 包含原点,则意味着两个图形发生了重叠,从而判断两个图形发生了碰撞。我们把计算后的点称为 闵可夫斯基差 ( Minkowski Difference ),将生成的三角形/线形称为 单纯形 ( Simplex

Minkowski Difference。实际上,似乎只存在 Minkowski Sum

将两个图形所有的坐标相减,显然不是我们能够手算的。但是,我们可以根据多边形的特点,只需将他们的顶点相减,就可以达成我们的目的。但是这样,排列组合后,需计算 20 个点,这显然也不是我们想要的,并且可能会出现多余的计算结果。所以,我们需要尽量得到足够大的单纯形,这样才会提升包含原点的几率。

在GJK 算法中,我们定义一个名为 support

  1. ,那么将 黄色
  2. 。然后将 蓝色
  3. 获得的两个点相减,即可得到两个图形的单纯形上的一点。

为了便于理解,我们创建两个多边形,并且规定


为蓝色图形的位置坐标,


为黄色图形的位置坐标,从


指向


为 support 函数的起始方向。我们的目标是至少能够算出三个点,构成一个包含三个顶点的单纯形。


java 调用fcl碰撞库 java碰撞检测算法_单纯形_03


第一次

依次按照上述步骤,我们得到这样的一个结果:


java 调用fcl碰撞库 java碰撞检测算法_java 调用fcl碰撞库_04


得到黄色图形在


方向上的最大投影点为来自于点


,记录坐标


;得到蓝色图形在 方向


上的最大投影点为来自于点


,记录坐标



将两个坐标相减,得到单纯形上的一个顶点 :



第二次

然后,我们将初始方向取反,作为新的初始方向,再进行如上的步骤:


java 调用fcl碰撞库 java碰撞检测算法_碰撞检测_05


得到黄色图形在


方向上的最大投影点为来自于点


,记录坐标


;得到蓝色图形在


方向上的最大投影点为来自于点


,记录坐标



将两个坐标相减,得到单纯形上的一个新的顶点 :



第三次

之后,我们取与起始向量垂直的向量作为新的方向向量,值得注意的是,这样的方向向量有两个,一个是 指向原点的


,一个是

背离原点的


java 调用fcl碰撞库 java碰撞检测算法_单纯形_06


我们不妨以先背离原点的


作为新的方向,重复 support 函数。


java 调用fcl碰撞库 java碰撞检测算法_java计算一个多边形的重心_07


得到黄色图形在


方向上的最大投影点为来自于点


,记录坐标


;得到蓝色图形在 方向


上的最大投影点为来自于点


,记录坐标



将两个坐标相减,得到单纯形上的一个新的顶点 :



构造单纯形

我们得到了三个点:


,画出单纯形:


java 调用fcl碰撞库 java碰撞检测算法_单纯形_08


我们似乎没能构成一个包含原点的单纯形。

如果我们换成


,计算后,我们将会得到一个新的点


。画出这个新的单纯形:


java 调用fcl碰撞库 java碰撞检测算法_碰撞检测_09


这一次,我们 幸运地

从以上几个步骤,不难看出, GJK 是一种 迭代算法,它需要不断地检测单纯形是否包含原点。它退出迭代的条件是:

  1. 单纯形包含原点
  2. 单纯形最后添加的顶点与搜寻方向点乘小于0

到目前为止,我们离真正实现 GJK 碰撞检测还有一段距离,以及碰撞之后,如何通过EPA算法,获得它的穿透向量。这些内容,将在稍后的篇章中详细介绍。