三维线与三角形之间的交点

Kai*_*Kid 1 c++ 3d intersection line line-intersection

我在3D空间的某个地方有一条线和一个三角形.换句话说,我有三个点([x,y,z]每个)三角形,和两个点(也是[x,y,z])的线.

我需要找出一种方法,希望使用C++来确定线是否曾经过三角形.与三角形平行且具有多个共同点的线应计为"不相交".

我已经制作了一些代码,但它不起作用,即使视觉表现清楚地显示了交叉点,我也总是假的.

ofVec3f P1, P2;
P1 = ray.s;
P2 = ray.s + ray.t;

ofVec3f p1, p2, p3;
p1 = face.getVertex(0);
p2 = face.getVertex(1);
p3 = face.getVertex(2);

ofVec3f v1 = p1 - p2;
ofVec3f v2 = p3 - p2;

float a, b, c, d;

a = v1.y * v2.z - v1.z * v2.y;
b = -(v1.x * v2.z - v1.z * v2.x);
c = v1.x * v2.y - v1.y * v2.x;
d = -(a * p1.x + b * p1.y + c * p1.z);

ofVec3f O = P1;
ofVec3f V = P2 - P1;

float t;

t = -(a * O.x + b * O.y + c * O.z + d) / (a * V.x + b * V.y + c * V.z);

ofVec3f p = O + V * t;

float xmin = std::min(P1.x, P2.x);
float ymin = std::min(P1.y, P2.y);
float zmin = std::min(P1.z, P2.z);

float xmax = std::max(P1.x, P2.x);
float ymax = std::max(P1.y, P2.y);
float zmax = std::max(P1.z, P2.z);


if (inside(p, xmin, xmax, ymin, ymax, zmin, zmax)) {
    *result = p.length();
    return true;
}
return false;
Run Code Online (Sandbox Code Playgroud)

这里是inside()的定义

bool primitive3d::inside(ofVec3f p, float xmin, float xmax, float ymin, float ymax, float zmin, float zmax) const {
    if (p.x >= xmin && p.x <= xmax && p.y >= ymin && p.y <= ymax && p.z >= zmin && p.z <= zmax)
        return true;

    return false;
}
Run Code Online (Sandbox Code Playgroud)

Joc*_*pek 11

@BrunoLevi:您的算法似乎不起作用,请参阅以下 python 实现:

def intersect_line_triangle(q1,q2,p1,p2,p3):
    def signed_tetra_volume(a,b,c,d):
        return np.sign(np.dot(np.cross(b-a,c-a),d-a)/6.0)

    s1 = signed_tetra_volume(q1,p1,p2,p3)
    s2 = signed_tetra_volume(q2,p1,p2,p3)

    if s1 != s2:
        s3 = signed_tetra_volume(q1,q2,p1,p2)
        s4 = signed_tetra_volume(q1,q2,p2,p3)
        s5 = signed_tetra_volume(q1,q2,p3,p1)
        if s3 == s4 and s4 == s5:
            n = np.cross(p2-p1,p3-p1)
            t = -np.dot(q1,n-p1) / np.dot(q1,q2-q1)
            return q1 + t * (q2-q1)
    return None
Run Code Online (Sandbox Code Playgroud)

我的测试代码是:

q0 = np.array([0.0,0.0,1.0])
q1 = np.array([0.0,0.0,-1.0])
p0 = np.array([-1.0,-1.0,0.0])
p1 = np.array([1.0,-1.0,0.0])
p2 = np.array([0.0,1.0,0.0])

print(intersect_line_triangle(q0,q1,p0,p1,p2))
Run Code Online (Sandbox Code Playgroud)

给出:

[ 0.  0. -3.] 
Run Code Online (Sandbox Code Playgroud)

而不是预期的

[ 0.  0. 0.]
Run Code Online (Sandbox Code Playgroud)

看着线

t = np.dot(q1,n-p1) / np.dot(q1,q2-q1)
Run Code Online (Sandbox Code Playgroud)

从法线减去 p1 对我来说没有意义,你想从 q1 投影到三角形的平面上,所以你需要沿着法线投影,距离与 q1 到平面和 q1-q2沿法线,对吗?

以下代码修复了这个问题:

n = np.cross(p2-p1,p3-p1)
t = np.dot(p1-q1,n) / np.dot(q2-q1,n)
return q1 + t * (q2-q1)
Run Code Online (Sandbox Code Playgroud)

  • 非常感谢您的关注,我已经修复了另一个答案。 (3认同)
  • 对此答案+1。该答案中更正的数学应该合并到接受的答案中。 (2认同)

use*_*587 5

要在 3D 中查找直线和三角形之间的交点,请遵循以下方法:

  • 计算支撑三角形的平面,
  • 直线与支撑三角形的平面相交:

    • 如果没有交点,则与三角形没有交点。
    • 如果存在交点,验证交点确实位于三角形内:

      • 三角形的每条边与支撑三角形的平面的法线一起确定了包围三角形内部的半空间(可以从法线和边顶点导出相应的边界平面),
      • 验证交点是否位于所有边半空间的内部。

以下是一些示例代码,其中包含应该有效的详细计算:

// Compute the plane supporting the triangle (p1, p2, p3)
//     normal: n
//     offset: d
//
// A point P lies on the supporting plane iff n.dot(P) + d = 0
//
ofVec3f v21 = p2 - p1;
ofVec3f v31 = p3 - p1;

ofVec3f n = v21.getCrossed(v31);
float d = -n.dot(p1);

// A point P belongs to the line from P1 to P2 iff
//     P = P1 + t * (P2 - P1)
//
// Find the intersection point P(t) between the line and
// the plane supporting the triangle:
//     n.dot(P) + d = 0
//                  = n.dot(P1 + t (P2 - P1)) + d
//                  = n.dot(P1) + t n.dot(P2 - P1) + d
//
//     t = -(n.dot(P1) + d) / n.dot(P2 - P1)
//
ofVec3f P21 = P2 - P1;
float nDotP21 = n.dot(P21);

// Ignore line parallel to (or lying in) the plane
if (fabs(nDotP21) < Epsilon)
    return false;

float t = -(n.dot(P1) + d) / nDotP21;
ofVec3f P = P1 + t * P21;

// Plane bounding the inside half-space of edge (p1, p2): 
//     normal: n21 = n x (p2 - p1)
//     offset: d21 = -n21.dot(p1)
//
// A point P is in the inside half-space iff n21.dot(P) + d21 > 0
//

// Edge (p1, p2)
ofVec3f n21 = n.cross(v21);
float d21 = -n21.dot(p1);

if (n21.dot(P) + d21 <= 0)
    return false;

// Edge (p2, p3)
ofVec3f v32 = p3 - p2;
ofVec3f n32 = n.cross(v32);
float d32 = -n32.dot(p2);

if (n32.dot(P) + d32 <= 0)
    return false;

// Edge (p3, p1)
ofVec3f n13 = n.cross(-v31);
float d13 = -n13.dot(p3);

if (n13.dot(P) + d13 <= 0)
    return false;

return true;
Run Code Online (Sandbox Code Playgroud)

对随问题发布的代码的一些评论:

  • ofVec3f如果可用,应优先使用(.dot()以及.cross()几何乘积等)的预定义操作(更具可读性,避免实现错误等),
  • 该代码最初遵循上述方法,但随后仅检查交点是否位于线段 [P1, P2] 的 3D 轴对齐边界框中。这与可能的其他错误相结合可以解释为什么结果不正确。
  • 我们可以验证交点是否位于(整个)三角形的 3D 轴对齐边界框中。虽然这不足以保证相交,但它可以用于剔除明显不相交的点并避免进一步复杂的计算。


Bru*_*evy 5

1)如果你只想知道线是否与三角形相交(不需要交点):

设p1,p2,p3表示你的三角形

在两个方向上非常远的地方选择两个点q1,q2.

设SignedVolume(a,b,c,d)表示四面体a,b,c,d的有符号体积.

如果SignedVolume(q1,p1,p2,p3)和SignedVolume(q2,p1,p2,p3)具有不同的符号AND SignedVolume(q1,q2,p1,p2),SignedVolume(q1,q2,p2,p3)和SignedVolume( q1,q2,p3,p1)具有相同的符号,然后有一个交叉点.

SignedVolume(a,b,c,d)=(1/6)*dot(cross(ba,ca),da)

2)现在,如果你想要交叉点,当1)中的测试通过时

以参数形式写出线的方程:p(t)= q1 + t*(q2-q1)

写出平面的方程:点(p,N) - 点(p1,N)= 0其中N =交叉(p2-p1,p3-p1)

将p(t)注入平面方程:dot(q1 + t*(q2-q1),N-p1)= 0

Deduce t = -dot(q1,N-p1)/ dot(q1,q2-q1)

交点是q1 + t*(q2-q1)

  • 游戏已经很晚了,但其他评论中尚未提及:返回语句中的 `det &gt;= 1e-6` 条件不应该是 `fabs(det) &gt;= 1e-6` 吗?我想这是对矩阵列的依赖性的检查,所以符号应该是无关的。我检查了你的算法(用 C 语言),这是唯一阻止我通过测试的因素。 (4认同)