GJK 算法

GJK算法是一种用于检测凸包之间碰撞的迭代方法,基于闵科夫斯基和的概念。它不需要计算完整的闵科夫斯基差,而是通过构建包含原点的单纯形来判断碰撞。算法的关键包括支持函数和单纯形迭代,适用于具有支持函数的任何形状。GJK在3D环境中比SAT更快,特别适合确定两个凸形之间的距离和小距离穿透情况下的碰撞信息。
摘要由CSDN通过智能技术生成

GJK 算法(Gilbert–Johnson–Keerthi)

翻译自:http://www.dyn4j.org/2010/04/gjk-gilbert-johnson-keerthi/

今天,我将讨论dyn4j项目随附的其他碰撞检测算法。您可以找到很多GJK文档,但是其中很多实际上是技术性的,主要是因为它们是研究论文。我强烈推荐该视频教程,老实说,看完之后,您甚至不需要进一步阅读。但是,如果您在观看视频后觉得需要更多信息,请继续阅读。

  1. 介绍
  2. 凸包
  3. 闵可夫斯基和
  4. 单纯形
  5. Support函数
  6. 创建单纯形
  7. 确定碰撞
  8. 迭代
  9. 检查单纯形

简介
GJK与SAT一样,仅在凸包上运行。GJK更具吸引力的功能之一是,它可以支持实现“
support ”函数的任何形状(我们将在后面讨论)。因此,与SAT不同,您不需要增加特殊的代码或算法来处理弯曲的形状。

GJK是一种迭代方法,但是收敛速度非常快,如果使用最终的穿透/分离向量进行约束,则可以在几乎恒定的时间内运行。在3D环境中,它是SAT的更好替代方案,因为SAT必须测试轴数。

GJK的初衷是确定两个凸包之间的距离。GJK还可以用于在小距离穿透情况下获取碰撞信息,并可以通过其他算法进行补充以实现更大距离的穿透。

凸包
正如我之前说的,GJK是只能用于凸包的算法。请参阅我在SAT上的帖子以获取有关凸包的解释。

闵科夫斯基和
GJK算法在很大程度上依赖于称为闵科夫斯基和的概念。闵科夫斯基和在概念上非常容易理解。假设您有两个形状,这些形状的闵科夫斯基和就是shape1中的所有点加到shape2中的所有点上{得到的是另外一个更大更复杂的形状}:

A + B = {a + b |a∈A,b∈B}

如果两个形状均为凸包,则所得形状为凸包{如果需要深入了解,可以参考wiki百科}。

您可能会想,“好,那很好,但是这有什么关系呢?”重要性不在于加法,而是在于减法:

A – B = {a – b |a∈A,b∈B}

作为补充,在继续操作之前,请注意,即使我们使用的是“减法”运算符,然而事实上它不该被称为闵科夫斯基差,因为其仍然是闵科夫斯基和{相当于先将B中的所有点相对于原点进行镜像,使用得到的形状与A计算闵科夫斯基和}。不过,在本文的其余部分中,为清楚起见,我将其称为闵科夫斯基差。

继续,在闵科夫斯基和中执行减法操作的关键是:

如果两个形状重叠/相交,则闵科夫斯基差将包含原点。

图1:两个凸形状相交

图1:两个凸形状相交

让我们看一个例子,取图1中的两个形状并对它们执行闵科夫斯基差,您将得到图2中的结果形状。请注意,结果形状包含原点,因为图1中的两个形状是相交的。

执行此操作需要shape1.vertices.size * shape2.vertices.size * 2个减法。这是很重要的,因为形状是由无穷多个点组成的。由于这两个形状都是凸包,且由最外面的顶点定义,所以我们只需要对这些顶点执行此操作。关于GJK伟大的事情是,你并不真正需要计算闵科夫斯基差。

图2:Minkowski差异

图2:闵科夫斯基差

单纯形(Simplex)
我们不想计算闵科夫斯基差,而只是想知道闵科夫斯基差是否包含原点。如果包含,那么两个形体就是是相交的;如果不包含,则它们不相交。

我们可以做的是,在闵科夫斯基差形体内迭代构建一个试图包含原点的多边形。如果我们构建的多边形包含原点(也形体包含在闵科夫斯基差形体内),那么我们可以说闵科夫斯基差包含原点。我们要构建的多边形称为“单纯形(Simplex)”。

Support函数
那么下一个问题是我们如何构建单纯形?Simplex是使用所谓的Support函数构建的。给定两个形体,Support函数应在闵科夫斯基差内返回一个点。我们已经知道我们可以从shape1中获取一个点,并从shape2中获取一个点,并将其相减以获得闵科夫斯基差中的一个点,但是我们不希望每次都得到相同的点。

如果使Support函数依赖于方向,我们可以确保每次调用Support函数时都不会得到相同的点。换句话说,如果使Support函数在某个方向上返回最远的点,则可以后续选择其他方向来获取其他点。

选择方向上最远的点很重要,因为它会创建一个最大包含面积的单纯形,因此增加了算法快速退出的机会。另外,我们可以利用这样的事实,即以这种方式返回的所有点都在闵科夫斯基差的边缘,因此,如果我们不能沿某个方向添加一个超出原点的点,我们就知道闵科夫斯基差不包含原点。这增加了算法在非相交情况下快速退出的机会。稍后对此进行更多讨论。

 

1

2

3

4

5

6

7

8

9

10

public Point support(Shape shape1, Shape shape2, Vector d) {

  // d is a vector direction (doesn't have to be normalized)

  // get points on the edge of the shapes in opposite directions

  Point p1 = shape1.getFarthestPointInDirection(d);

  Point p2 = shape2.getFarthestPointInDirection(d.negative());

  // perform the Minkowski Difference

  Point p3 = p1.subtract(p2);

  // p3 is now a point in Minkowski space on the edge of the Minkowski Difference

  return p3;

}

创建单纯形

让我们从一个例子开始。使用图2中的形状并执行3次Support函数:
首先让我们开始使用d =(1,0)

 

1

2

3

p1 = (9, 9);

p2 = (5, 7);

p3 = p1 - p2 = (4, 2);

接下来让我们使用d =(-1,0)

 

1

2

3

p1 = (4, 5);

p2 = (12, 7);

p3 = p1 - p2 = (-8, -2);

请注意,p1可能是(4,5)或(4,11)。两者都会在闵可夫斯基差的边缘产生一个点。
接下来让我们使用d =(0,1)

 

1

2

3

p1 = (4, 11);

p2 = (10, 2);

p3 = p1 - p2 = (-6, 9);

我们获得图3中所示的Simplex。

图3:示例单纯形

图3:示例单纯形

确定碰撞

前面我们说过,如果闵可夫斯基差中的单纯形包含原点,则我们知道这两个形状是相交的。在图3中,单纯形不包含原点,但是我们知道这两个形状是相交的。这里的问题是,我们的第一个猜测(在选择方向时)没有产生包含原点的单纯形。

如果相反,我为第三个闵科夫斯基差方向选择d =(0,-1):

 

1

2

3

p1 = (4, 5);

p2 = (5, 7);

p3 = p1 - p2 = (-1, -2);

这样就产生了图4所示的单纯形,现在我们包含了原点并可以确定存在碰撞。

图4:包含原点的示例单纯形

图4:包含原点的示例单纯形

因此,正如我们所看到的,方向的选择会影响结果。我们还可以看到,如果获得不包含原点的单纯形,则可以计算另一个点,并使用它。

这就是算法的迭代部分出现的地方。我们不能保证我们选择的前3个点将包含原点,也不能保证Minkowski差值包含原点。我们可以通过仅沿原点方向选择点来修改选择点的方式。如果我们将选择第三个Minkowski差异点的方式更改为下方,则可以将原点围起来。

 

1

2

3

4

5

6

7

8

d = ...

a

GJK计算碰撞代码的应用 //----------------------------------------------------------------------------- // Torque 3D // Copyright (C) GarageGames.com, Inc. // // The core algorithms in this file are based on code written // by G. van den Bergen for his interference detection library, // "SOLID 2.0" //----------------------------------------------------------------------------- #include "core/dataChunker.h" #include "collision/collision.h" #include "sceneGraph/sceneObject.h" #include "collision/convex.h" #include "collision/gjk.h" //---------------------------------------------------------------------------- static F32 rel_error = 1E-5f; // relative error in the computed distance static F32 sTolerance = 1E-3f; // Distance tolerance static F32 sEpsilon2 = 1E-20f; // Zero length vector static U32 sIteration = 15; // Stuck in a loop? S32 num_iterations = 0; S32 num_irregularities = 0; //---------------------------------------------------------------------------- GjkCollisionState::GjkCollisionState() { a = b = 0; } GjkCollisionState::~GjkCollisionState() { } //---------------------------------------------------------------------------- void GjkCollisionState::swap() { Convex* t = a; a = b; b = t; CollisionStateList* l = mLista; mLista = mListb; mListb = l; v.neg(); } //---------------------------------------------------------------------------- void GjkCollisionState::compute_det() { // Dot new point with current set for (int i = 0, bit = 1; i < 4; ++i, bit <<=1) if (bits & bit) dp[i][last] = dp[last][i] = mDot(y[i], y[last]); dp[last][last] = mDot(y[last], y[last]); // Calulate the determinent det[last_bit][last] = 1; for (int j = 0, sj = 1; j < 4; ++j, sj <<= 1) { if (bits & sj) { int s2 = sj | last_bit; det[s2][j] = dp[last][last] - dp[last][j]; det[s2][last] = dp[j][j] - dp[j][last]; for (int k = 0, sk = 1; k < j; ++k, sk <<= 1) { if (bits
GJK算法(Gilbert-Johnson-Keerthi算法)是一种用于求解凸体相交的算法,其用途广泛应用在物理引擎、碰撞检测和计算机图形学等领域。 GJK算法的基本思想是通过迭代逼近的方式,在高维空间中找到两个凸体是否相交的最近点对,从而确定它们是否相交。具体来说,GJK算法分为三个主要步骤: 1. 初始化:选择两个初始点,一个位于第一个凸体上,一个位于第二个凸体上。这两个点可以是凸体的顶点、边界上的任意一点。 2. 迭代逼近:根据Minkowski差集(两个凸体的差集)形成的新凸体,找到离原点最近的点。该最近点必然位于Minkowski差集的边界上,可以通过求解凸包或者线段相交等方法来寻找。 3. 更新凸体:如果最近点距离原点足够小,说明两个凸体相交;否则,将最近点加入到Minkowski差集,进行下一轮迭代逼近。 由于GJK算法通过在高维空间中进行求解,虽然只需要两个凸体的形状信息,但能够得到相交的最近点对,进而支持接触深度、碰撞法向量等额外的信息。此外,GJK算法具有高效、可扩展性好等优点,因此被广泛应用于各种实时计算几何问题中。 总之,GJK算法是一种高效的求解凸体相交的算法,通过迭代逼近的方式找到相交的最近点对,其具有应用广泛的优势,可用于物理引擎、碰撞检测和计算机图形学等领域。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值