三维线段与平面交点

kbi*_*irk 4 c++ algorithm math vector collision-detection

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

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

在此输入图像描述

该功能的参数如下:

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)
Run Code Online (Sandbox Code Playgroud)

这是我使用的代码:

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
}
Run Code Online (Sandbox Code Playgroud)

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

小智 7

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

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

if (Dot(normal, ray)) {
   return false; // avoid divide by zero
}
Run Code Online (Sandbox Code Playgroud)

如果向量是垂直的,那么点积只有零,这是你想要避免的情况(没有交点),而非零数字在C中是真的.
因此解决方案是否定(!)或做Dot(. ..)== 0.
在所有其他情况下,将有一个交叉点.

在交点计算上:平面的所有点X都遵循等式

点(N,X)= d

其中N是法线,d可以通过将平面的已知点放在等式中找到.

float d = Dot(normal, coord);
Run Code Online (Sandbox Code Playgroud)

在光线上,线的所有点s可以表示为点p和给出方向D的矢量:

s = p + x*D.

因此,如果我们搜索飞机中的xs,我们就有了

点(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))/点(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被归一化(长度= 1),那么Dot(a,b)就是ba的投影长度.在我们的平面的情况下,d给出了平面在垂直于原点的方向上的所有点的方向距离(a是法线).然后,我们可以通过比较法线上的投影长度(点积)来确定点是否在此平面上.

除了2:
如何检查光线是否与三角形相交?(用于光线追踪)
为了测试光线是否进入由3个顶点给出的三角形,首先必须执行此处显示的内容,得到与三角形形成的平面的交点.
下一步是看这个点是否在三角形中.这可以使用重心坐标来实现,该重心坐标将平面中的点表示为其中三个点的组合.请参阅重心坐标并从笛卡尔坐标转换


tem*_*def 2

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

// calculate plane
float d = Dot(normal, coord);
Run Code Online (Sandbox Code Playgroud)

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

d = Dot(normal, (0, 0, 0)) = 0
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

希望这可以帮助!

  • @user785259:纠正错误后,测试是否有效? (2认同)