SAT(分离轴定理)

翻译自: SAT(Separating Axis Theorem)

2010年1月1日发表

目录

  • SAT(分离轴定理)
    • 引言
    • 凸特性
    • 投影
    • 算法
      • 无交叉
      • 相交
    • 获取分离轴
    • 将多边形投影到轴
    • 寻找最小平移向量(MTV)
    • 曲线形状
    • 包含
    • 其他注意事项

  这是我一直想写的一篇文章,但我一直没有抽出时间来做。首先我要说的是,网络上有大量关于这种特殊碰撞检测算法的资料。但是,在解释一些实现细节时,这些资源通常是模糊的(针对我们的目的)。
  我计划解释算法,并填补我自己实现时的一些空白。
  该教程具有交互式flash示例,可供参考。

引言

  分离轴定理,简称SAT,是一种确定两个凸多边形是否相交的方法。该算法还可用于寻找最小穿透矢量,这对于物理仿真和许多其他应用是有用的。SAT是一种快速通用算法,无需对每一对形状类型进行碰撞检测,从而降低代码量和对代码的维护。

凸特性

  如前所述,SAT是一种确定两个凸多边形是否相交的方法。如果某个形状与任何穿过该形状的直线只交叉两次,则该形状被称为凸多边形。如果某个形状与穿过该形状的直线交叉两次以上,则该形状为非凸(或凹)。更正式的定义参见维基的定义和数学世界的定义。让我们来看一些例子:

图1: 凸多边形

图2: 非凸多边形

  第一个形状被认为是凸的,因为不存在一条可以通过该形状绘制的线,该线将交叉两次以上。第二种形状是非凸的,因为存在一条相交两次以上的线。
  SAT只能处理凸多边形,不过,非凸形状可以由凸形状的组合来表示(凸分解)。因此,如果我们需要采用图2中的非凸形状时,可以执行凸分解,这样便得到了两个凸形状。这样我们可以测试每个凸面形状,以确定整个形状的碰撞。

图3: 凸分解

投影

  SAT使用的下一个概念是投影。假设你有一个光源,它的光线都是平行的。如果您将灯光照射到对象上,它将在平面上形成阴影。该阴影是三维物体的二维投影。二维物体的投影是一个一维“阴影”。

图4: 投影(阴影)

算法

  SAT指出:“如果两个凸面物体没有穿透,则存在一根轴,使得这两个物体在该轴上的投影不重叠。

无交叉

  首先,让我们讨论SAT如何确定两个形状不相交。图5中的两个形状不相交。可以通过在它们之间画一条线来说明。

图5: 两个分开的凸多边形

  做辅助线垂直于图5中分隔两个形状的线,并将形状投影到该辅助线上,则它们的投影不重叠。形状的投影(阴影)不重叠的辅助线称为分离轴。在图6中,深灰色线是分离轴,相应的彩色线是形状在分离轴上的投影,其中,投影不重叠,因此根据SAT,形状不相交。

图6: 两个不相交的凸多边形以及相应的投影

  SAT可以测试多个轴判断是否重叠。只要投影不重叠,算法可以立即确定形状不相交,从而退出循环。基于这样的特性,SAT非常适合具有许多对象但很少碰撞的应用程序(如游戏、模拟等)。
  为了进一步解释,请检查以下伪代码。

Axis[] axes = // get the axes to test;
// loop over the axes
for (int i = 0; i < axes.length; i++) {Axis axis = axes[i];// project both shapes onto the axisProjection p1 = shape1.project(axis);Projection p2 = shape2.project(axis);// do the projections overlap?if (!p1.overlap(p2)) {// then we can guarantee that the shapes do not overlapreturn false;}
}

相交

  如果对于所有轴,形状的投影都重叠,则我们可以得出形状相交的结论。图7中,在多个轴上测试了两个凸多边形的投影。这些投影都重叠,因此我们可以断定这两个形状是相交的。

图7: 两个相交的凸多边形

  必须测试所有轴是否重叠,以确定交叉点。上述代码修改为:

Axis[] axes = // get the axes to test;
// loop over the axes
for (int i = 0; i < axes.length; i++) {Axis axis = axes[i];// project both shapes onto the axisProjection p1 = shape1.project(axis);Projection p2 = shape2.project(axis);// do the projections overlap?if (!p1.overlap(p2)) {// then we can guarantee that the shapes do not overlapreturn false;}
}
// if we get here then we know that every axis had overlap on it
// so we can guarantee an intersection
return true;

获取分离轴

  实现该算法时的第一个问题是:如何知道要测试哪些轴?事实上,这非常简单:
  只要测试每个多边形的边的法线即可

图8: 边的法线

  边的法线可以通过翻转坐标并取反来获得。例如:

Vector[] axes = new Vector[shape.vertices.length];
// loop over the vertices
for (int i = 0; i < shape.vertices.length; i++) {// get the current vertexVector p1 = shape.vertices[i];// get the next vertexVector p2 = shape.vertices[i + 1 == shape.vertices.length ? 0 : i + 1];// subtract the two to get the edge vectorVector edge = p1.subtract(p2);// get either perpendicular vectorVector normal = edge.perp();// the perp method is just (x, y) =&gt; (-y, x) or (y, -x)axes[i] = normal;
}

在上面的代码中,我们返回形状每个边的垂直向量。这些向量称为“法向量”。然而,这些向量归一化(不是单位长度)。如果您只需要SAT算法的布尔结果,这就足够了,但如果您需要碰撞信息(稍后在MTV部分中讨论),则这些向量需要归一化(请参见将形状投影到轴的部分)

  对每个形状执行此操作,则可获得两个要测试的轴的列表。伪代码修改为:

Axis[] axes1 = shape1.getAxes();
Axis[] axes2 = shape2.getAxes();
// loop over the axes1
for (int i = 0; i < axes1.length; i++) {Axis axis = axes1[i];// project both shapes onto the axisProjection p1 = shape1.project(axis);Projection p2 = shape2.project(axis);// do the projections overlap?if (!p1.overlap(p2)) {// then we can guarantee that the shapes do not overlapreturn false;}
}
// loop over the axes2
for (int i = 0; i < axes2.length; i++) {Axis axis = axes2[i];// project both shapes onto the axisProjection p1 = shape1.project(axis);Projection p2 = shape2.project(axis);// do the projections overlap?if (!p1.overlap(p2)) {// then we can guarantee that the shapes do not overlapreturn false;}
}
// if we get here then we know that every axis had overlap on it
// so we can guarantee an intersection
return true;

将多边形投影到轴

  另一个问题是如何将形状投影到轴上。将多边形投影到轴上相对简单;在所有顶点上循环执行与轴的点积,并存储最小值和最大值即可。

double min = axis.dot(shape.vertices[0]);
double max = min;
for (int i = 1; i < shape.vertices.length; i++) {// NOTE: the axis must be normalized to get accurate projectionsdouble p = axis.dot(shape.vertices[i]);if (p < min) {min = p;} else if (p > max) {max = p;}
}
Projection proj = new Projection(min, max);
return proj;

寻找最小平移向量(MTV)

  到目前为止,我们只在两个形状是否相交时返回真或假。除此之外,SAT还可以返回最小平移向量(MTV)。MTV是可推导出形状发生碰撞的最小幅值向量。回到图7,我们可以看到轴C的重叠部分最小。该轴和重叠便是MTV,其中,轴是矢量部分,重叠是幅度部分。
  为了确定形状是否相交,我们必须在两个形状的所有轴上循环,以便同时跟踪最小重叠和轴。修改伪代码,在形状相交时返回一个MTV。

double overlap = // really large value;
Axis smallest = null;
Axis[] axes1 = shape1.getAxes();
Axis[] axes2 = shape2.getAxes();
// loop over the axes1
for (int i = 0; i < axes1.length; i++) {Axis axis = axes1[i];// project both shapes onto the axisProjection p1 = shape1.project(axis);Projection p2 = shape2.project(axis);// do the projections overlap?if (!p1.overlap(p2)) {// then we can guarantee that the shapes do not overlapreturn false;} else {// get the overlapdouble o = p1.getOverlap(p2);// check for minimumif (o < overlap) {// then set this one as the smallestoverlap = o;smallest = axis;}}
}
// loop over the axes2
for (int i = 0; i < axes2.length; i++) {Axis axis = axes2[i];// project both shapes onto the axisProjection p1 = shape1.project(axis);Projection p2 = shape2.project(axis);// do the projections overlap?if (!p1.overlap(p2)) {// then we can guarantee that the shapes do not overlapreturn false;} else {// get the overlapdouble o = p1.getOverlap(p2);// check for minimumif (o < overlap) {// then set this one as the smallestoverlap = o;smallest = axis;}}
}
MTV mtv = new MTV(smallest, overlap);
// if we get here then we know that every axis had overlap on it
// so we can guarantee an intersection
return mtv;

曲线形状

  我们已经看到了如何使用SAT测试多边形,但是像圆形这样的曲线形状呢?曲线形状会给SAT带来问题,因为曲线形状有无限多的分离轴要测试。解决这个问题的方法通常是分解为圆vs圆和圆vs多边形的测试,并做一些更具体的工作。另一种方法是完全不使用曲线形状,使用足够多的顶点数多边形来替换。第二种选择不需要对上面的伪代码进行更改,此处介绍第一种方法。
  让我们先看看圆vs圆。通常,首先会执行以下操作:

Vector c1 = circle1.getCenter();
Vector c2 = circle2.getCenter();
Vector v = c1.subtract(c2);
if (v.getMagnitude() < circle1.getRadius() + circle2.getRadius()) {// then there is an intersection
}
// else there isnt

  我们知道,如果圆心距比圆半径之和小,则两个圆就会发生碰撞。这个测试实际上是一个类似SAT的测试。要在SAT中实现这一点,我们可以执行以下操作:

Vector[] axes = new Vector[1];
if (shape1.isCircle() && shape2.isCircle()) {// for two circles there is only one axis testaxes[0] = shape1.getCenter().subtract(shape2.getCenter);
}
// then all the SAT code from above</pre>

  圆形vs多边形带来了更多的问题。沿多边形轴的中心到中心测试是不够的(事实上,可以省略中心到中心的测试)。在这种情况下,必须包含另一个轴:从多边形上最近顶点到圆中心的轴。要找到多边形上最近的顶点有多种方法,如Voronoi区域,该文中将不会讨论。
  其他曲线形状将会是更大的问题,必须以自己的处理方式。例如,胶囊形状可以分解为一个矩形和两个圆形。

包含

  许多开发人员选择忽略的问题之一是包含关系。当一个形状包含另一个形状时会发生什么?这个问题通常不会是一个大问题,因为大多数应用程序都不会出现这种情况。首先,让我解释一下这个问题以及如何处理它。然后我会解释为什么应该考虑。

图9: 包含关系

  如果一个形状包含在另一个形状中,则根据目前的伪代码,SAT将返回不正确的MTV。矢量和幅度部分可能都不正确。图9显示返回的重叠不足以将形状移出交叉点。所以我们需要做的是检查重叠测试中的包容度。修改SAT代码中的if语句:

if (!p1.overlap(p2)) {// then we can guarantee that the shapes do not overlapreturn false;
} else {// get the overlapdouble o = p1.getOverlap(p2);// check for containmentif (p1.contains(p2) || p2.contains(p1)) {// get the overlap plus the distance from the minimum end pointsdouble mins = abs(p1.min - p2.min);double maxs = abs(p1.max - p2.max);// NOTE: depending on which is smaller you may need to// negate the separating axis!!if (mins < maxs) {o += mins;} else {o += maxs;}}// check for minimumif (o < overlap) {// then set this one as the smallestoverlap = o;smallest = axis;}
}

  原因1:形状可能会存在这样的类型。不处理将需要两次或多次SAT迭代来解决是否碰撞,这取决于形状的相对大小。
  原因2:如果计划使算法支持线段与其他形状之间的碰撞检测,则必须这样做,因为在某些情况下重叠可能为零(这是因为线段是无限薄的形状)。

其他注意事项

  一些其他注意事项:

  • 平行轴可以减少测试轴的数量。例如矩形只有两个轴要测试。

  • 一些形状,如矩形,如果有自己的投影和getAxes函数,可以执行得更快,因为矩形只需要测试2个轴。

  • 最后一个分离轴可用于触发SAT的下一次迭代,使得算法在非相交情况下可以是0(1)。

  • 3D中的SAT最终可以测试很多轴。

  • 我不是专家,请原谅我糟糕的图形。

SAT(Separating Axis Theorem)翻译相关推荐

  1. Separating axis theorem Polygon Collision

     Separating axis theorem: http://en.wikipedia.org/wiki/Separating_axis_theorem Polygon Collision: ht ...

  2. 主轴定理(Principal axis theorem)

    1,补充知识 1.1 欧式空间(Euclidean space) 直观感受:二维平面,三维立体,拓展到高维空间就对应着超平面.我们在初高中以及大学中的高等数学.线性代数遇到的都是欧几里得空间. 为了在 ...

  3. python碰撞检测算法_GJK碰撞检测算法

    现实世界里我们对于是否碰撞的判断可以说极其容易而且准确,比如下图.在二进制的世界里,一切就没这么直观了. GJK(Gilbert-Johnson-Keerthi Distance Algorithm) ...

  4. Hybrid Astar 算法剖析和实现(七)

    在学习资料满天飞的大环境下,知识变得非常零散,体系化的知识并不多,这就导致很多人每天都努力学习到感动自己,最终却收效甚微,甚至放弃学习.我的使命就是过滤掉大量的无效信息,将知识体系化,以短平快的方式直 ...

  5. “等一下,我碰!”——常见的2D碰撞检测

    "碰乜鬼嘢啊,碰走晒我滴靓牌".想到"碰"就自然联想到了"麻将"这一伟大发明.当然除了"碰",洗牌的时候也充满了各种『碰 ...

  6. 第6-8课:分离轴算法(SAT)与碰撞检测(图文篇)

    物体的碰撞检测是游戏软件中的关键算法之一,两个角色是否能够对话.子弹是否击中了物体,以及是否出现人物穿墙的 bug,都依赖于一套可靠的碰撞检测算法.有很多算法可以实现碰撞检测,基于算法几何的方法有轴对 ...

  7. 分离轴定理SAT凸多边形精确碰撞检测

    分离轴定理SAT凸多边形精确碰撞检测 定理 Separating Axis Theorem,缩写SAT,中文为分离轴理论或分离轴定理,通过判断凸多边形在分离轴上的投影是否重叠来判断是否发生碰撞. 所谓 ...

  8. 碰撞检测之分离轴定理算法讲解

    本文翻译自@sevenson的文章Separating Axis Theorem (SAT) Explanation .原文作者用的是ActionScript 3来编写算法,不过文中主要讲述的还是算法 ...

  9. 碰撞检测之分离轴定理算法

    本文转载自 https://blog.csdn.net/yorhomwang/article/details/54869018,感谢博主分享 本文翻译自@sevenson的文章Separating A ...

  10. 游戏编程里面有哪些经典或者很酷的算法?

    光栅化 Bresenham's line algorithm [1]:经典的绘画直线算法,后来还可以稍作修改用于绘画圆弧[2],都不用三角函数或除数,只需用整数加法.减法和乘法. Perspectiv ...

最新文章

  1. 在Ubuntu上安装Snort入侵检测系统。
  2. 3D 激光雷达地图相对精度自动评价算法
  3. 电脑服务器怎么打开网页,电脑的iis服务器打开网页的方法
  4. 问:Linux下Chrome标题栏中文乱码
  5. aspx页面引用html页面
  6. 卫星导航技术的源起很有戏剧性
  7. 浅谈postMessage跨域通信与localStorage实现跨域共享
  8. 二维绘图引擎:圆、半圆、弧线绘制
  9. grasshop 犀牛5.0下载_新安|原创剑尊下载新版本 2020最新安卓版剑尊下载地址整理...
  10. MySQL 笔记9 -- PyMySQL
  11. Java线程池示例:并行计算200000以内的质数个数
  12. python手册中文版pdf-python中文官方文档 PDF 下载
  13. 基于Linux系统开发在线词典
  14. h5+vue+php仿微信源码-泡泡IM
  15. BI系统的分布式部署原理和技术实现
  16. 安装mysql8避坑指南_2019 MySQL 8 安全安装避坑指南-Go语言中文社区
  17. 芭蕉树上第十三根芭蕉--opencv第一个测试程序遇到问题
  18. (转载)消息队列详解
  19. 爬虫 第六讲 Scrapy框架
  20. Acrobat XI Pro安装教程

热门文章

  1. AGV机器人核心部件——驱动轮
  2. 如何将图片压缩到200K以内,有什么好方法吗?
  3. Matlab imcrop 与 opencv ROI
  4. python计算无穷级数求和常用公式_幂级数n=0到∞∑ x^n/的和函数怎么求 级数求和问题:求:∑1/...
  5. SpringCloud微服务之学生管理
  6. matlab 矩阵 黑白,各位好 请问怎么用0 1矩阵画出黑白的矩阵如下图所示
  7. Windows安装和设置教程
  8. 昆仑固件涉密专用计算机,存储处理国家秘密的计算机信息系统按照涉密程序实行...
  9. cv曲线面积的意义_浅谈圆锥曲线中的高级技巧
  10. 【源码开放】Hexo+Github+Coding 博客butterfly 和 matery 主题 搭建完全教程【整理】