Skip to content

碰撞点求解(二):V-clip 算法 #42

Description

@phenomLi

上一篇我们介绍了一种简单的碰撞点求解算法:最近内部顶点法,还提到了算法的几个小缺陷。今天我们来看一种新的碰撞点求解算法 -- V-clip 算法。(我也不是很懂里面的 V 是什么意思)。

V-clip 是 box2d 里面使用的碰撞点求解算法,相比最近内部顶点法,V-clip 的求解更准确性能更好,而且弥补了最近内部顶点法的几个缺陷。

在这里不得不说一下,box2d 的作者 Erin Catto 是真的牛逼,简直大神级的存在。Erin Catto 前身是数学家,现在在暴雪工作,担任游戏引擎开发,box2d 是他业余时间写的一个物理引擎。他的 box2d 可谓是真真正正的商用级 2d 物理引擎标杆,其中独创了 V-clip,Sequential Impulses,Bilateral Advancement 等算法。V-clip 提供了准确度更高的碰撞点求解;Sequential Impulses 算法将约束求解器的复杂度降低了好几个等级,之后的绝大多数无论是玩具还是商用级的物理引擎都有 Sequential Impulses 的身影;Bilateral Advancement 更是将精确连续碰撞检测变为可能。另外,box2d 中的动态 AABB 树,island,还有各种严谨的数学公式,都证明了 box2d 在 2d 物理引擎中的老大哥地位。


V-clip

V-clip 名字虽然有 clip(裁剪),但是其核心思想并不是裁剪,而是筛选

V-clip 有 3 个重要的概念,分别是 reference edgeincident edge (这两个我实在不知道怎么翻译)和筛选域,其中:

  • reference edge:一物体上与碰撞法线 n 垂直的那一条边,也就是 SAT 中求出 n 的那一条边
  • incident edge:另一物体上与 n 最垂直的边
  • 筛选域:由 reference edge 划分的域,落在这个域上的顶点将被排除为碰撞点

V-clip 主要工作流程如下:

  1. 确定 reference edge 和 incident edge,并将 incident edge 的两个端点加入到候选碰撞点
  2. 第一次划分筛选域,为 reference edge 左右垂线的两侧
  3. 将落在筛选域的候选碰撞点移除,且若 reference edge 左右垂线与 incident edge 有交点则将该交点加入到候选碰撞点
  4. 第二次划分筛选域,为 reference edge 的左侧(顺时针)
  5. 将落在筛选域的候选碰撞点移除

可见 V-clip 的关键是找出 reference edge,incident edge 和确定筛选域。看不懂没关系,我们举上一篇结尾的一个例子:

上一篇我提到了这种情况用内部顶点法无法解决,那现在我们来看看用 V-clip 怎么做。


Step 1

首先我们要做的是确定 reference edge 和 incident edge。现在假设我们已经用 SAT 求出了碰撞法线 n ,那么对于 reference edge 我们可以马上确定(图中黄色的线),因为 SAT 在求出 n 的同时已经求出了 reference edge,此时 reference edge 位于物体 A 。

至于 incident edge,我们可以先求出 B 距离 A 最近的一个顶点(图中蓝色的点),然后选取该点的两条邻边,分布与法线进行点乘,点乘越接近 0 的边表示与 n 越垂直,那么这条边就是 incident edge。最近点怎么求可以参考最近内部顶点法里面的方法。

现在,我们已求出 reference edge 和 incident edge ,并将 incident edge 的两个端点 v1,v2 加入到候选碰撞点里。

Step 2

接下来是进行第一次划分筛选域,我们过 reference edge 的两个端点 v3,v4 分布做一条垂直于 reference edge 的垂线。

Step 3

那么两垂线背向 reference edge 的一则即是第一次划分的筛选域,我们在下图中以灰色区域表示。

可以看到,顶点 v2 由于落在筛选域内,因此从候选碰撞点中移除。一条垂线和 incident edge 相交产生了顶点 v5 ,我们把 v5 加入到候选碰撞点中。至于如何求交点,可以看看我之前曾经介绍过一种相似三角形的方法

Step 4

我们作第二次筛选域划分,这次 reference edge 的左侧区域成为筛选域。

边的左侧或右侧是怎么确定的呢?是根据物体的顶点顺序确定的。如图中 A 的顶点顺序为顺时针(v3 -> v4),因此此时筛选域为从 v3 出发到 v4 的左侧。如果你习惯使用逆时针顶点顺序,那么此时筛选域应该是 reference edge 右侧。

Step 5

v5 落在筛选域内,将其从候选碰撞点中移除。


此时算法执行完成,我们得到唯一的一个碰撞点 v1 。


如何确定顶点是否落在筛选域内?

其实这个问题很简单,一种方法是使用向量叉乘,box2d 使用了另一种稍微复杂一点的方法。

假设黄线为 reference edge ,虚线为筛选域的垂线。使用图中的方法,可以判断出 v1 不在筛选域内而 v2 在筛选域内。这种方法有一个很大的优点(这个优点使得 V-clip 必须使用这个方法来判断点是否在筛选域内)就是计算出来的 d1,d2 有重要的意义。

还记得上一篇最后的第二个例子吗,我们提到了一种用最近内部顶点法无法确定每个碰撞点的穿透深度的情况。然而在 V-clip 中情况不一样了,我们在第二次划分筛选域中判断两个候选碰撞点(图中红点)是否位于 reference edge 左侧(顺时针)时,因为刚好这两个点都不在筛选域,因此碰撞点就是这个两个点,所以,计算出的 d1,d2 正正就是两个碰撞点各自的穿透深度

你可能暂时不知道为什么,但是当你熟悉了 SAT 和 V-clip 之后,就能想明白了。同时你应该注意到,这种情况下,V-clip 求得的碰撞点和最近内部顶点法求得的并不一样,说明了这个例子很好地反映了两种算法之间地差异。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions