【问题标题】:Fast query for closest point between line and AABB in 3D在 3D 中快速查询线和 AABB 之间的最近点
【发布时间】:2021-09-01 02:40:05
【问题描述】:

如何在 3D 空间中有效地找到无限线和 AABB 之间的最近点?

我有一个简单的解决方案,包括找到最接近线的 AABB 的所有 12 个边缘的点并选择最接近的对,这可行,但性能不是很好。我的用例需要更快的东西。

在我的搜索中,我发现了大量关于查找线和 AABB 之间的冲突的文献,以及一些朴素算法的实现。但是有什么更好的吗?

【问题讨论】:

  • @markasoftware 不错,但链接中的解决方案只是给出了距离。看起来这个问题要求一对点(即它们的坐标)——一个在线上,一个在盒子上——满足最小距离。由于在边缘情况下可能有许多这样的点对(即,在一般情况下,解的非唯一性,尽管通常不是在典型情况下),所以大概任何一对这样的点就足够了(?)。这可能是链接中解决方案的自然扩展,但我不太确定。
  • @markasoftware 这些是用于碰撞检测,而不是最近点。

标签: collision-detection


【解决方案1】:

我(也相当幼稚)的解决方案是选择最靠近 AABB 中心的线上的点,然后按照此处的示例从该点向外工作: https://math.stackexchange.com/questions/2133217/minimal-distance-to-a-cube-in-2d-and-3d-from-a-point-lying-outside

然后沿线迭代,直到距离不再减小(必须在每个方向上向外扫。)

在线条平行于面或边缘的情况下,这将具有非常快的好处,但我不知道它是否会比您在与线条没有平行方面的天真的解决方案更快AABB。

(当然,数学堆栈交换的解决方案需要从伪代码适应您选择的语言,并且您必须在找到后计算向量的坐标才能获得分数最短路径。)

【讨论】:

  • 如果我错了,请纠正我,您是否建议在改变点的同时计算 12 条边与在线点之间的距离?另外,如何找到边缘最近的点?
【解决方案2】:

我首先想到的是,您不需要检查所有 12 条边。例如假设你想找到离 A 线最近的点。通过一些简单的数学运算,你可以找到一条穿过 AABB 中心的垂直线 A'。你可以找到A和A'的交点,我们称那个点为P。所以点P基本上是AABB中心在A线上的投影,有数学公式可以计算。如果你能告诉我们你的无限线是如何定义的,我会添加它(它是由两个点定义的,还是由一个点和一个方向向量定义的?)。
现在您可以查看 P 的坐标以检查它相对于 AABB 的中心位于哪个八分圆,即从 P 的坐标中减去中心的坐标,这将导致中心到原点的基本平移(原点偏移)。立方体最近的顶点将是同一个八分圆中的那个。

现在,离线最近的边将是该顶点所属的 3 条边之一。

这将在您算法的 1/4 时间内起作用。

【讨论】:

  • 这听起来很有希望,但也许你可以澄清你是如何从 P(AABB 的中心到线上的投影)到 P',这似乎是 AABB 面上的一个点,它这就是为什么你提到象限——一个平面概念。
  • @Salmonstrikes 请原谅,没有点 P'。当我写 P' 时,我认为 P 是 AABB 的中心。重读这个,我意识到我从来没有命名中心,而是 P 是 AABB 中心的投影。我已经编辑了我的答案以反映这一点。
  • 知道了。最后一件事:你的意思是八分圆而不是象限,约定 AABB 以原点为中心?
  • @Salmonstrikes 是的,我又来了。谢谢指正!
  • 我认为 OP 对 12 条边中的每条边都使用了类似 this 的东西,这种方法可以减少到只有 3 条边。这种方法的开销将是计算 A' 以及 A 和 A' 的交集。找到一条与 A 垂直的线是找到最近点的解决方案的一小部分(如您在上面的链接中所见),这将减少到只有一次,而不是 OP 方法中的 8 倍。所以我想这将是一个更快的实现。
【解决方案3】:

您的线将类似于 cv + p,其中 v 是方向向量,p 是它通过的点。

从直线到盒子上最近点的方向将垂直于直线,因此获取一对v 的法线,例如nm

实际最近点将是nm 的线性组合,其中一个点位于直线上。 (你从线上的一个点开始,沿着一条垂直于这条线的线到达你的目标点,这条线的每一条垂直线都是通过该点的法线的线性组合。)

所以解决an + bm + cv + p = s,其中s 是你盒子的每个角落。

不失一般性,我们可以假设v 在其前两个组件中的任何一个都不为零。 (如果它有一个,排列索引使其成为第三个。如果它有两个,这将成为一个更简单的问题,我们可以单独解决。)

这意味着我们可以使用n = (1, 0, N)m = (0, 1, M)

由于n * v = 0m * v = 0,我们可以找到N = -v1/v3M = -v2/v3。我们不会做所有这些替换,只要知道这些是数字而不是变量就足够了。

现在我们要解决an + bm + cv = s - p。我们可以用普通代数来做。我们得到

c = (s3 - p3 - N(s1 - p1)- M(s2 - p2))/(v3 - N v1 - M v2)

然后cv + p 是您的直线上离该角最近的点。

同时我们还有:

b = s2 - p2 - v2 c
a = s1 - p1 - v1 c

线到你的角落的距离是|an + bm|,所以距离的平方是

D^2 = (an + bm) * (an + bm) = (a^2 + b^2 + (aM + bM)^2)

当您查看角落时,您可以考虑您有一个盒子这一事实。选择一个角落,去一个邻居,然后去那个让你从那个角落对角线的邻居。如果这三个值中的一个值在同一方向上比其他两个值更远,则最近的点将不在通往它的边缘上。这将删除所有涉及它的边缘。由于这条线可能会直接穿过您的盒子,因此这可能不会发生。但这不太可能。

对于构成边的每一对角(不排除),我们有一个范围为cx c0 + (1-x) c1x in [0,1],它们表示直线上距离最近点的最近点的范围在边缘。

对应的ab的值为

a = s1-p1-v2(x c0 + (1-x)c1) = s1-p1-v2-(c0-c1)v2 x
b = s2-p2-v1(x c0 + (1-x)c1) = s2-p2-v1-(c0-c1)v1 x

同样,知道常数很重要,所以我们把它们写成:

a = A0 + A1x
b = B0 + B1x

现在,从上面

D^2 = (a^2 + b^2 + (aN + bM)^2)

所以我们可以得到 x 的平方距离。

D^2 = (A0 + A1 x)^2 + (B0 + B1 x)^2 + 
      (A0 N + A1 Nx)^2 + (B0 M + B1 MX)^2

将这一切相乘,x 项将是每个正方形的中间项,x^2 项将是每个正方形的平方项。

很明显,抛物线k + rx + qx^2 的最小值是r + 2qx = 0 在'x = -1/2 r/q,通过微分。

所以最小值将是

x = -(A0 A1 + B0 B2 + (NA0 A1 + MB0 B1))/
     (A1^2 + B1^2 + (A1 N + B1 M)^2)

x 介于 0 和 1 之间时,您有一个最小值或最大值。通过比较这些结果,您应该能够丢弃最大值。

如果最小值出现在面上,而不是边上,则限定值过多。在那种情况下,答案显然是零——这条线碰到了盒子。

【讨论】:

  • 不错的尝试!快速澄清请求:您提到“对于构成边的每一对角......x>的进一步计算”。如果这意味着您要运行 12 次最小化例程,它可能无法满足提问者的要求,即比他们的 12 边检查更快。如果您能阐明您的答案在性能方面的含义,那就太好了。
  • 但是“运行最小化例程”正在评估给出的非常简单的算术。
  • @Salmonstrikes 我还添加了您不必查看所有边缘的观察结果。构成人脸的角为您提供了足够的信息,让您可以忽略它们中较远的部分。如果线穿过盒子,你最终只会看到所有的角落。但是你马上就会得到三名候选人。所以很有可能你只看到四个点和接触它们的边缘。所以我想我已经包括了所有的代数,并省略了导致这个算法不足的战略考虑。我稍后会回到它。
猜你喜欢
  • 2016-04-04
  • 2018-06-17
  • 2017-02-16
  • 2020-01-26
  • 2014-02-23
  • 2022-07-21
  • 2023-03-11
  • 2020-05-19
  • 2021-08-06
相关资源
最近更新 更多