【问题标题】:How to choose one of two correct intersection between plane and triangle edges in 3D?如何在 3D 中选择平面和三角形边缘之间的两个正确交点之一?
【发布时间】:2019-09-28 13:20:55
【问题描述】:

我在 3D 空间的某处有一条线(AB)和一个三角形(P0P1P2)。换句话说,三角形有 3 个点(每个 [x,y,z]),直线有两个点(也有 [x,y,z])。线的一点位于三角形内(AB)。主要目标是在屏幕上找到构成线条一部分的三角形边缘的交点(AB)。

我做什么:

1.从线开始(A)、线结束(B)和相机位置计算平面法线:

    Vector3 Normal = Vector3.Cross(A - cameraPos, B - cameraPos);

2.通过检查三角形的每条边找到两个交点:

    bool IsPlaneIntersectLine(Vector3 Normal, Vector3 A, Vector3 B, Vector3 EdgePoint1, Vector3 EdgePoint2, out Vector3 Intersection)
    {
        float dotProduct = Vector3.Dot(Normal, (EdgePoint1 - EdgePoint2));
        if (dotProduct == 0f)
            return false;
        float dot1 = Vector3.Dot(Normal, (A - EdgePoint2));
        float distance = dot1 / dotProduct;
        if (distance > 1f || distance < 0f)
            return false;
        Intersection = EdgePoint2 + distance * (EdgePoint1 - EdgePoint2);
        return true;
    }
    Vector3 intersection1, intersection2, intersection3;
    bool isEdge1Intersected = IsPlaneIntersectLine(Normal, A, B, triangle.P0, triangle.P1, out intersection1);
    bool isEdge2Intersected = IsPlaneIntersectLine(Normal, A, B, triangle.P1, triangle.P2, out intersection2);
    bool isEdge3Intersected = IsPlaneIntersectLine(Normal, A, B, triangle.P2, triangle.P0, out intersection3);

结果我有两个交叉点,但只有 一个是正确的 - 交叉点,即 线的一部分AB)在屏幕上。我也有限制 - 我无法将此点转换为屏幕空间进行检查 - 两个交点中的哪一个位于 AB 之间。

我也试过检查线向量和相交向量之间的点积:

    float dot = Vector3.Dot((Intersection - A), (B - A));
    if (dot < 0f)
    {
        return false;
    }

但有些三角形/边不适合这种情况。

如何在屏幕上找到三角形边缘上的一个交点(AB)?

【问题讨论】:

标签: c# vector 3d geometry intersection


【解决方案1】:

这里出现的是游戏开发者的答案,而不是几何专业的答案,所以它会让大多数数学家畏缩,但它通常应该有效。

由于三角形是平面的,并且您已经计算了平面法线,您可以创建一个变换矩阵,将三角形旋转,使其法线垂直向上,并将其中心平移到 [0,0,0]。 (我无法解释创建这个矩阵的数学原理,但是有许多内置了这个逻辑的开源库,基本上是一个逆“查看”矩阵)。这实际上是一个世界到本地空间的矩阵。

一旦你有了那个矩阵,你可以将它应用到线上的两个点上,将它移动到局部空间中,然后你就有一个非常简单的任务来确定线与 XZ 轴平面的交点(即求解哪里Y 等于零)。虽然您在这里拥有临时 2D 的所有内容,但您可以使用重心坐标来检查交点是否在三角形中、边界上或外部也很容易。

在本地空间中找到答案点后,通过矩阵的倒数对其进行变换以将其移回世界空间,然后就可以完成了。

【讨论】:

    【解决方案2】:

    如果您想要一个 2D 交点,如屏幕上所见,由一条直线和最多两条边组成,那么您首先需要计算投影坐标。
    有关详细信息,请搜索您了解转换(使用 4x4 矩阵)的任何好的教程。请务必了解三种转换:模型、视图和投影。 一旦你有 2D 数据(在投影后忽略“深度”坐标),你的问题就会减少到测试三个 2D 线-线交点。

    如果您想要 3D 交点(原始数据,无转换),则搜索线平面交点(三角形的平面)。一旦你得到交点,如果有的话,计算从这个点到每条边的距离。

    【讨论】:

      【解决方案3】:

      正如我已经在 previous answer 中描述的一种方法,与更通用的算法有关,检查这种特殊情况的一种直接方法是形成两个叉积向量

      V1 = Vector3.Cross((A - cameraPos), (Intersection - cameraPos))

      V2 = Vector3.Cross((Intersection - cameraPos), (B - cameraPos))

      这两个向量垂直于平面(cameraPos, A, B),因此位于一条公共线上。如果它们都指向同一个方向,那么当你的视线从A 点开始向B 点旋转时,你会看到第一个点A,然后指向Intersection,然后指向@987654331 @。但是,如果向量 V1V2 指向相反的方向,那么您将看不到点 A 和 B 之间的交点。因此,为了解决您的问题,我希望执行以下算法工作:

      V1 =  Vector3.Cross((A - cameraPos), (Intersection - cameraPos));
      V2 =  Vector3.Cross((Intersection - cameraPos), (B - cameraPos));
      dot = Vector3.Dot(V1, V2);
      
      if dot < 0f
      {
         return false
      } 
      

      请注意,所有这些算法主要适用于可见三角形,因此对于不可见的三角形,需要小心。此外,如果 A 和 B 在同一个三角形中,这些算法会遇到异常。如果A 已经在一个三角形的边缘,那么这个修复是不必要的,我认为你可以直接从general algorithm 转到第3 步。

      【讨论】:

        猜你喜欢
        • 2019-06-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-12
        • 2018-05-01
        • 1970-01-01
        相关资源
        最近更新 更多