三维线段与平面交点

时间:2011-08-23 22:52:44

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

我正在尝试实现一个线段和平面相交测试,它将返回true或false,具体取决于它是否与平面相交。它还将返回线相交的平面上的接触点,如果线不相交,则如果线段已经是光线,则该函数仍应返回交点。我使用了Christer Ericson的实时碰撞检测中的信息和代码,但我认为我没有正确实现它。

使用的平面是从三角形的法线和顶点导出的。在平面上找到交叉点的位置是我想要的,无论它是否位于我用来导出平面的三角形上。

enter image description here

该功能的参数如下:

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
}

在我的测试中,它永远不会真实......任何想法?

3 个答案:

答案 0 :(得分:7)

我正在回答这个问题,因为当被要求提供光线交叉的c ++示例时,它首先出现在Google上。)

代码总是返回false,因为您输入if here:

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 :< / p>

  

s = p + x * D

因此,如果我们搜索飞机中的 x s ,我们就有

  

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

点积 ab 转置(a)* b
转置(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 值是什么意思?
对于两个向量 a b ,点积实际上返回一个向量的正交投影的长度,而另一个向量的另一个向量。
但是,如果 a 被归一化(长度= 1),那么点(a,b)就是 b 投影的长度即可。在我们的平面的情况下, d 给出了平面在垂直于原点的方向上的所有点的方向距离( a 是正常的)。然后我们可以通过比较法线上的投影长度(点积)得出点是否在这个平面上。

除了2:
如何检查光线是否与三角形相交? (用于光线追踪)
为了测试光线是否进入由3个顶点给出的三角形,首先必须做这里显示的内容,得到与三角形形成的平面的交点。
下一步是看这个点是否在三角形中。这可以使用重心坐标来实现,该重心坐标将平面中的点表示为其中三个点的组合。请参阅Barycentric Coordinates and converting from Cartesian coordinates

答案 1 :(得分: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);

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

希望这有帮助!

答案 2 :(得分:1)

这一切对我来说都很好。我已经独立检查了代数,这对我来说很好。

作为示例测试用例:

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运算符。 / p>