3D碰撞检测:凸包与凸包,需要位置和法线

时间:2019-07-06 08:16:16

标签: c++ algorithm collision-detection game-physics convex

我想知道两个3D凸包(AB)之间碰撞位置的大约3D位置 3D法线。

括号中的CPU显示了我完成的程序所需的相对CPU时间。

第1部分:提早使用(CPU 1%)

第一步,我使用一种非常便宜的算法-separation axis theorem
例如,我将15 axis用于2个多维数据集。 (在实际情况下,形状会更复杂。)
如果至少有1个可分离的轴,则return "no-collide"
否则,请执行下一部分。

第2部分:顶点与体积(CPU 10%)

  • 检查A的每个顶点-是否在B内。
  • 检查B的每个顶点-是否在A内。

enter image description here

第3部分:Edge vs Edge(CPU> 20%)

有一个奇怪的情况,例如https://gamedev.stackexchange.com/questions/75437/collision-detection-3d-rectangles-using-sat。我从那里偷了图像:-

enter image description here

因此,我还需要 edge edge

  • 对于每对A和B边(12 * 12 = 144对),在A的边上找到最接近B边的点。检查顶点是否在B内部。
  • (反之亦然)对于每对B&A边,检查该顶点是否在A内部。

哇,这是很多计算。
但是,还没有结束。

问题

  1. 报告的碰撞位置不太准确(左:当前,右:希望):-

    enter image description here

    为解决这个问题,我考虑过要生成一个新的凸形= A intersect B
    有一些免费的C ++库(例如OpenMesh),但我认为它过于占用CPU资源。
    请注意,我不需要准确无误。

  2. 有时它还会报告错误的正常信息(左:当前,右:希望):-

    enter image description here

    ^通过添加(A)的 edge 和B的 face 可以解决此问题,但这会使整个碰撞检测更加昂贵。

问题

看起来(从中复制)互联网上的常见算法只能识别微特征。
恕我直言,顶点体积/边缘算法专注于拓扑结构,而不是两个形状都是 solid 体积的事实。

哪种算法更准确(第一优先)并且可能更便宜?
我的方法在基础级别上可能是错误的。

为了加快速度,我已经进行了一些修剪,例如仅选择一对靠近的边A和B。

参考文献:-

编辑(10天后)

现在,我可以找到所有相交的凸点(该凸点被描绘为粉红色三角形/矩形): enter image description here

这是我找到法线的方法。

对于每个分离轴(全部= 15轴),我投影该轴上的粉红色凸起。
产生最短投影距离(粉红色箭头)的轴应为法线方向。

我的上述假设通常是正确的(例如,左),但有时是错误的(例如,右)。
如何便宜地改善它?

2 个答案:

答案 0 :(得分:1)

游戏引擎通常会模拟一系列离散步骤的时间。结果,由于相互渗透(您的情况)或事物快速移动,碰撞系统可能陷入困难的(通常是昂贵的)情况下-在步骤N中,A位于B的一侧,而完全位于B的另一侧,因此隧道发生碰撞在步骤N + 1。如果您需要处理多体接触或连续接触或非凸形,接缝或软物体,则更加困难。 kes!我们正在模拟整个世界。

您想进行“游戏物理学”并使用近似值来回购帧速率... 最后,您可以用一堆烟雾或火光掩盖很多错误。 :-)

有一类算法,明确考虑了模拟时间以帮助碰撞系统。有很多方法可以实现“连续碰撞检测”系统。您可以从这里开始,但是在提交代码之前,应该先广泛阅读。幸运的是,关于碰撞的文献很多。 这是一个很好的起点 https://docs.unity3d.com/Manual/ContinuousCollisionDetection.html https://pybullet.org/Bullet/phpBB3/viewtopic.php?t=20

这是一种建议的启发式方法,可能在您现有的系统中起作用。 这种启发式技术可能会在诸如星体3d这样的游戏中起作用,其中物体可以在太空中自由漂浮。这可能足以满足您的需求。

每个对象的图像都存储其当前状态向量(位置,方向,速度,加速度,旋转...)以及上一个时间步的先前状态向量。

假设您在时间=当前检测到对象A和B之间潜在的碰撞。

对于time = previous,假设A和B没有接触。

使用A和B的先前状态向量分别在time = prev处计算A和B的表面上的最接近点。 (closestA,closestB)。

线段(closestA,closestB)在time = previous时将具有非零长度。 您可以只使用closestB作为位置和法线,但是它会与线段的长度成比例。

通过查找A任意接近B的时间,也可以及时进行二进制搜索,并最大程度地减少错误。 在每次搜索时,将搜索时间步长减小一半。 0.5、0.25、0.125 ..直到(closestA,最近的B)的长度低于错误阈值或您放弃。

对于简单情况,这应该为您提供可接受的近似解决方案...

您还说过,您正在使用分离轴定理作为“第一检查”。如果这真的是“第一眼检查”,那对我来说确实听起来很昂贵。

最快的计算是您不需要的计算,因此快速的碰撞意味着大量的便宜的预测试并避免了昂贵的情况。

您可以考虑使用诸如粗糙的空间网格之类的空间技术,并且仅检查已经知道的彼此靠近的对象。

此外,球面测试是一种非常快速的预测试,可以查看两个凸物体的边界球是否均匀重叠。

答案 1 :(得分:0)

我会这样做:

  1. 定义

    让2个三角3D凸包A,B。它们每个都定义了中心pc和表面点列表p0,p1,p2,...和三角形列表t0,t1,t2,...。每个三角形还应该计算出其法线n0,n1,n2,...指出的位置。

  2. 计算最多的内部生命值q

    因此,让我们测试A是否击中B。只需遍历所有点A.p[i],并记住位于B内且最接近B.pc的最里面的点(如果有多个)。假设您的模拟有足够小的时间步长dt,因此插值位置不会错过命中率。如果没有,则必须使用另一种方法。

  3. 计算位移d

    为此,我们需要知道相对于A的运动方向B。可以这样计算:

    dir = (A.pc-B.pc) - (A'.pc-B'.pc)
    

    其中A'.pc,B'.pc是最后一次迭代的位置...这里是相对热值的图像(因为B会静止不动):

    overview

    现在要计算位移,只需从q-dir方向投射光线,并测试命中B的哪个三角形。交点q'是您要寻找的命中点。排量:

     d = q'-q
    

    您需要翻译A以便仅触摸B而不是相交。

    由此,您可以计算反射r,因为B的命中三角形具有自己的法线:

    reflect

    所以现在只需将A转换为r,这样您的A就可以在反射后找到位置(因此,在撞击过程中不会浪费时间)...也来自{{1} }大小和d之间的相对速度,您可以估算撞击时间并通过降低速度和A,B来增加撞击摩擦力。不要忘记也反映r的速度。您还可以对A进行碰撞物理。

    Bq'还可用于在两个d上创建旋转扭矩,但是模拟3D旋转非常困难,通常只是被欧拉角所伪造...我理解是刚体可以具有任意数量的旋转,而不仅仅是3 ...但是其中一些旋转可以组合或抵消,因此,为了模拟一个物体,需要动态列出所涉及的旋转,这将是缓慢而困难的在上面实现东西。

看看:

您可以在A,B测试中找到我对point的实现,甚至在convex_mesh中也可以找到更多...在较新的版本中,我实现了更多的基元和凸面壳,但可以t将其增长到35 KB(已超过30 KB的限制)后再发布。

为加快测试速度,您还可以在每个AABB,OBB之前为每个convex_hull添加外切球体,并测试它们是否相交...您知道A,B线性路径之间的垂直距离必须小于convex_hull ...适用于简单的线/线最近点测试,该测试也在上面的链接中实现。