【问题标题】:3D Line Segment and Plane Intersection3D 线段和平面相交
【发布时间】:2011-11-02 08:47:56
【问题描述】:

我正在尝试实现线段和平面相交测试,该测试将根据它是否与平面相交而返回真或假。它还将返回直线相交的平面上的接触点,如果直线不相交,则如果线段是射线,该函数仍应返回相交点。我使用了 Christer Ericson 的实时碰撞检测中的信息和代码,但我认为我没有正确实现它。

im 使用的平面来源于三角形的法线和顶点。找到平面上的交点位置是我想要的,不管它是否位于我用来推导平面的三角形上。

函数的参数如下:

contact = the contact point on the plane, this is what i want calculated
ray = B - A, simply the line from A to B
rayOrigin = A, the origin of the line segement
normal = normal of the plane (normal of a triangle)
coord = a point on the plane (vertice of a triangle)

这是我使用的代码:

bool linePlaneIntersection(Vector& contact, Vector ray, Vector rayOrigin, Vector normal, Vector coord) {

    // calculate plane
    float d = Dot(normal, coord);

    if (Dot(normal, ray)) {
        return false; // avoid divide by zero
    }

    // Compute the t value for the directed line ray intersecting the plane
    float t = (d - Dot(normal, rayOrigin)) / Dot(normal, ray);

    // scale the ray by t
    Vector newRay = ray * t;

    // calc contact point
    contact = rayOrigin + newRay;

    if (t >= 0.0f && t <= 1.0f) {
        return true; // line intersects plane
    }
    return false; // line does not
}

在我的测试中,它永远不会返回 true...有什么想法吗?

【问题讨论】:

  • 你最后解决了吗?

标签: c++ algorithm math vector collision-detection


【解决方案1】:

我之所以回答这个问题,是因为当被问及光线相交的 c++ 示例时,它首先出现在 Google 上:)

代码总是返回 false,因为您在此处输入 if:

if (Dot(normal, ray)) {
   return false; // avoid divide by zero
}

只有当向量垂直时,点积才为零,这是您要避免的情况(没有交集),并且非零数字在 C 中为真。
因此,解决方案是否定 ( ! ) 或 Dot(...) == 0。
在所有其他情况下,都会有一个交叉路口。

关于交集计算: 平面上的所有点X都遵循方程

点(N, X) = d

其中 N 是法线,d 可以通过将平面的已知点放入方程中来找到。

float d = Dot(normal, coord);

在射线上,一条线的所有点s都可以表示为一个点p和一个给出方向D的向量:

s = p + x*D

所以如果我们搜索哪个x s在平面上,我们有

点(N, s) = d
点(N, p + x*D) = d

点积 abtranspose(a)*b
transpose(N)Nt。

Nt*(p + x*D) = d
Nt*p + Nt*D*x = d (x 标量)
x = (d - Nt*p) / (Nt*D)
x = (d - Dot(N, p)) / Dot(N , D)

这给了我们:

float x = (d - Dot(normal, rayOrigin)) / Dot(normal, ray);
我们现在可以通过将 x 放入直线方程来获得交点

s = p + x*D

Vector intersection = rayOrigin + x*ray;

上面的代码更新了:
bool linePlaneIntersection(Vector& contact, Vector ray, Vector rayOrigin, 
                           Vector normal, Vector coord) {
    // get d value
    float d = Dot(normal, coord);
if (Dot(normal, ray) == 0) { return false; // No intersection, the line is parallel to the plane }
// Compute the X value for the directed line ray intersecting the plane float x = (d - Dot(normal, rayOrigin)) / Dot(normal, ray);
// output contact point *contact = rayOrigin + normalize(ray)*x; //Make sure your ray vector is normalized return true; }

除 1:
d 值是什么意思?
对于两个向量 ab,点积实际上返回一个向量在另一个向量上的正交投影的长度乘以另一个向量。
但是如果a被归一化(length = 1),Dot(a, b)就是b上的投影长度>一个。对于我们的平面,d 为我们提供了平面上所有点在法线方向上到原点的方向距离(a 是法线)。然后,我们可以通过比较法线上的投影长度(点积)来得到一个点是否在这个平面上。

旁白 2:
如何检查射线是否与三角形相交? (用于光线追踪)
为了测试光线是否进入由 3 个顶点给定的三角形,您首先必须执行此处显示的操作,获取与三角形形成的平面的交点。
下一步是查看该点是否位于三角形中。这可以使用重心坐标来实现,该坐标将平面中的一个点表示为平面中三个点的组合。见Barycentric Coordinates and converting from Cartesian coordinates

【讨论】:

    【解决方案2】:

    我可能错了,但代码中有一些地方看起来非常可疑。首先,考虑这一行:

    // calculate plane
    float d = Dot(normal, coord);
    

    在这里,您的值 d 对应于平面法线(向量)和空间中的点(平面上的点)之间的点积。这似乎是错误的。特别是,如果你有任何平面通过原点并使用原点作为坐标点,你最终会计算

    d = Dot(normal, (0, 0, 0)) = 0
    

    并立即返回 false。我不确定你打算在这里做什么,但我很确定这不是你的意思。

    代码中另一个看起来可疑的地方是这一行:

    // Compute the t value for the directed line ray intersecting the plane
    float t = (d - Dot(normal, rayOrigin)) / Dot(normal, ray);
    

    请注意,您正在计算平面的法线向量(向量)和射线的原点(空间中的点)之间的点积。这看起来很奇怪,因为这意味着根据光线在空间中的起源位置,用于光线的缩放因子会发生变化。我建议再看一遍这段代码,看看这是否真的是你的意思。

    希望这会有所帮助!

    【讨论】:

    • 我的错误应该是 if (Dot(normal, ray) == 0),因为在平面上垂直于射线,因此没有相交的可能性。至于 float t = (d - Dot(normal, rayOrigin)) / Dot(normal, ray);这就是书中给出的方程式
    • 用于射线的缩放因子会根据射线原点发生变化,以补偿分母中的相应缩放,注意线向量ray不是单位向量而是整个线段。
    • @Keith- 啊,我没听懂。这是有道理的。
    • @user785259:纠正了那个错误,测试是否有效?
    【解决方案3】:

    我觉得这一切都很好。我已经独立检查了代数,这对我来说看起来不错。

    作为一个示例测试用例:

    A = (0,0,1)
    B = (0,0,-1)
    coord = (0,0,0)
    normal = (0,0,1)
    

    这给出了:

    d = Dot( (0,0,1), (0,0,0)) = 0
    Dot( (0,0,1), (0,0,-2)) = -2 // so trap for the line being in the plane passes.
    t = (0 - Dot( (0,0,1), (0,0,1) ) / Dot( (0,0,1), (0,0,-2)) = ( 0 - 1) / -2 = 1/2
    contact = (0,0,1) + 1/2 (0,0,-2) = (0,0,0) // as expected.
    

    因此,鉴于@templatetypedef 的回答之后的修正,我能看到问题的唯一领域是其他操作之一的实现,无论是Dot() 还是Vector 运算符。

    【讨论】:

      猜你喜欢
      • 2011-08-05
      • 1970-01-01
      • 1970-01-01
      • 2011-05-25
      • 2011-03-23
      • 1970-01-01
      • 1970-01-01
      • 2012-02-14
      • 2015-05-22
      相关资源
      最近更新 更多