三维点三角碰撞检测

时间:2008-09-19 04:07:32

标签: math 3d collision-detection

如何在以下物理模拟中纠正浮点错误:

  • 原点(x,y,z),
  • 施加力后所需的点(x',y',z')。
  • 两个三角形(A,B,C)和(B,C,D),共享边缘BC

我正在使用此方法进行碰撞检测:

For each Triangle
    If the original point is in front of the current triangle, and the desired point is behind the desired triangle:
        Calculate the intersection point of the ray (original-desired) and the plane (triangle's normal).
        If the intersection point is inside the triangle edges (!)
            Respond to the collision.
        End If
    End If
Next Triangle

我遇到的问题是,有时候该点落入浮点数学的灰色区域,在那里它非常靠近BC线,以至于它不能与任何一个三角形碰撞,即使在技术上它应该始终与一个或三个碰撞。另一个,因为他们有共同的优势。当发生这种情况时,该点在两个边共享三角形之间正好通过。我用(!)标记了一行代码,因为我相信这是我应该做出改变的地方。

在非常有限的情况下工作的一个想法是跳过边缘测试。有效地将三角形转换为平面。这仅适用于我的网格是凸包的情况,但我计划创建凸形。

我专门使用点积和三角法线进行所有前后测试。

5 个答案:

答案 0 :(得分:9)

对于具有边和顶点的某些几何体拍摄单个光线时,这是一个不可避免的问题。令人惊讶的是,物理模拟似乎在寻找最小的数值误差!

其他受访者提出的一些解释和解决方案将无效。特别是:

  • 数值不准确确实会导致光线“穿过间隙”。问题是我们在测试线BC之前将光线与平面ABC相交(得到点P,比方说)。然后我们在测试线BC之前将光线与平面BCD(得到点Q,相比)相交。 P和Q都用最接近的浮点近似表示;没有理由期望这些完全躺在它们应该躺在的平面上,所以你可以将BC放在BC的左边,Q放在BC的右边。

  • 使用低于或等于的测试无济于事;这是射线和飞机交叉点的不准确之处。

  • 平方根不是问题;你可以使用点积和浮点除法进行所有必要的计算。

以下是一些真正的解决方案:

  • 对于凸网格,您可以像所说的那样测试所有平面并忽略边和顶点(从而完全避免问题)。

  • 不要依次将光线与每个三角形相交。相反,请使用scalar triple product。 (当考虑每个三角形时,此方法对光线和边缘BC进行完全相同的计算序列,确保两个三角形之间的任何数值不准确性至少一致。)

  • 对于非凸网格,请为边和顶点指定一些宽度。也就是说,在网格中的每个顶点放置一个小球体,并沿网格的每个边缘放置一个细圆柱体。将光线与这些球体和圆柱体以及三角形相交。这些额外的几何图形会阻止光线穿过网格的边缘和顶点。

让我强烈推荐Christer Ericson的书Real-Time Collision Detection。第446-448页讨论了这个确切的问题,并对第184-188页上的光线与三角形相交的标量三重积方法进行了解释。

答案 1 :(得分:2)

听起来你不是在测试它是否在边缘(你正在写“内部三角形边缘”)。尝试将代码更改为“小于或等于”(内部或重叠)。

答案 2 :(得分:1)

我发现你的光线不太可能以浮点精度生效的方式落在三角形之间。你确实是个问题,你是否绝对肯定?

无论如何,一种可能的解决方案是不仅仅拍摄一张光线来拍摄三张非常接近的光线。如果一个完全落在两者之间,那么其他两个中的至少一个肯定会落在一个三角形上。

这将至少允许您测试问题是否真的是浮点错误或更有可能。

答案 3 :(得分:0)

@Statement:我的代码中确实已经使用了“大于或等于”比较,谢谢你的建议。 1

我目前的解决方案是在边缘测试中添加一个小的微移量。基本上,当测试每个三角形时,其边缘被推出非常小的量以抵消浮点中的误差。如果浮点计算的结果小于0.01而不是测试等于零的测试,则类似于测试。

这是一个合理的解决方案吗?

答案 4 :(得分:0)

如果您正在进行距离测量,请注意平方根。他们有一种讨厌你精确度一半的习惯。如果你把这些计算中的一些叠加起来,你就会很快遇到大麻烦。这是我用过的距离函数。

double Distance(double x0, double y0, double x1, double y1)
{
  double a, b, dx, dy;

  dx = abs(x1 - x0);
  dy = abs(y1 - y0);

  a = max(dx, dy));
  if (a == 0)
    return 0;
  b = min(dx, dy);

  return a * sqrt( 1 + (b*b) / (a*a) );
}

由于上一次操作不是平方根,因此不再失去精度。

我在一个正在研究的项目中发现了这一点。在研究了它并弄清楚它做了什么后,我找到了我认为有责任祝贺他的程序员,但他不知道我在说什么。