gjk算法(gjk算法代码)

gjk算法(gjk算法代码)翻译自 http www dyn4j org 2010 04 gjk gilbert johnson keerthi 今天 我将讨论 dyn4j 项目随附的其他碰撞检测算法 您可以找到很多 GJK 文档 但是其中很多实际上是技术性的 主要是因为它们是研究论文 我强烈推荐该视频教程 老实说 看完之后 您甚至不需要进一步阅读 但是 如果您在观看视频后觉得需要更多信息 请继续阅读 介绍 凸包 闵可夫斯基和 单纯形 Support 函数 创建单纯形 确定碰撞 迭代 检查单纯形 简介



翻译自: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

编程小号
上一篇 2025-02-15 09:17
下一篇 2025-02-19 15:01

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ri-ji/41246.html