c++ - 3D线段与平面相交

标签 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
}

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

最佳答案

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

代码总是返回 false 因为你在这里输入了 if :

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

只有当 vector 垂直时点积才为零,这是您要避免的情况(无交集),非零数在 C 中为真。
因此解决方案是取反 ( ! ) 或做 Dot(...) == 0。
在所有其他情况下都会有一个交叉点。

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

Dot(N, X) = d

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

float d = Dot(normal, coord);

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

s = p + x*D

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

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

点积a.btranspose(a)*b
transpose(N)Nt .

Nt*(p + x*D) = d
Nt*p + Nt*D*x = d (x scalar)
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);<br/>
    if (Dot(normal, ray) == 0) {
        return false; // No intersection, the line is parallel to the plane
    }<br/>
    // Compute the X value for the directed line ray intersecting the plane
    float x = (d - Dot(normal, rayOrigin)) / Dot(normal, ray);<br/>
    // output contact point
    *contact = rayOrigin + normalize(ray)*x; //Make sure your ray vector is normalized
    return true;
}

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

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

关于c++ - 3D线段与平面相交,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7168484/

相关文章:

c++ - 使用与以下子句相同的关键字的可选子句的 Yacc 扩展

c++ - 如何创建不区分大小写的正则表达式来匹配文件扩展名?

algorithm - 分配不同值(value)对象的算法建议

c++ - 迭代任意范围的 n 维数组的最快方法?

math - 从带有 x 轴的向量计算角度(梯度)

c++ - 为什么一元运算符返回的类型与其操作数不同?

C++:std::visit 不能在 gcc 下编译

java - resize() 方法被调用了多少次?

math - 如何使用mathematica隐式求解单变量的微分方程?

c# - 在 C# 中计算 NxN 矩阵行列式