确定三角形和平面的交点

21
我有一个三角形和一个平面(在3D空间内),如何计算它们相交的线段,如果没有相交则需要检测出这种情况。
我希望的最终结果是两个三维向量,它们定义了线段的起点和终点。
为了帮助你们,我已经计算出了面的平面与平面之间的交点光线,我只需要找到端点来将该光线剪切成一个线段。
对于那些喜欢看代码的人:
Face face;        //a face, defined by 3 points
Plane plane;      //a plane, defined by a normal vector and a distance
Ray intersection; //a ray, defined by a point and a direction, initialised to the intersection of the face plane and the face

Segment s = CalculateSegment(face, plane, intersection); //this method needs defining
4个回答

22

这里是一些伪代码建议。先给出简单版,稍后再给出更健壮的版本(只是为了帮助区分原则和细节)。

// Assume the plane is given as the equation dot(N,X) + d = 0, where N is a (not
// neccessarily normalized) plane normal, and d is a scalar. Any way the plane is given -
// DistFromPlane should just let the input vector into the plane equation.

vector3d planeN;
float planeD;

float DistFromPlane( vector3d P)
{
// if N is not normalized this is *not* really the distance, 
// but the computations work just the same.
    return dot(planeN,P) + planeD;
}

bool GetSegmentPlaneIntersection( vector3d P1, vector3d P2, vector3d& outP)
{
  float d1 = DistFromPlane(P1),
        d2 = DistFromPlane(P2);

  if (d1*d2 > 0)  // points on the same side of plane
     return false;

  float t = d1 / (d1 - d2); // 'time' of intersection point on the segment
  outP = P1 + t * (P2 - P1);

  return true;
}

void TrianglePlaneIntersection(vector3d triA, vector3d triB, vector3d triC,
                               vector3dArray& outSegTips)
{
   vector3d IntersectionPoint;
   if( GetSegmentPlaneIntersection( triA, triB, IntersectionPoint))
     outSegTips.Add(IntersectionPoint);

   if( GetSegmentPlaneIntersection( triB, triC, IntersectionPoint))
     outSegTips.Add(IntersectionPoint);

   if( GetSegmentPlaneIntersection( triC, triA, IntersectionPoint))
     outSegTips.Add(IntersectionPoint);
}

现在增加一些健壮性:
[编辑:明确考虑了平面上单个顶点的情况]
vector3d planeN;
 float planeD;

float DistFromPlane( vector3d P)
{
    return dot(planeN,P) + planeD;
}

void GetSegmentPlaneIntersection( vector3d P1, vector3d P2, vector3dArray& outSegTips)
{
  float d1 = DistFromPlane(P1),
        d2 = DistFromPlane(P2);

  bool  bP1OnPlane = (abs(d1) < eps),
        bP2OnPlane = (abs(d2) < eps);

  if (bP1OnPlane)
     outSegTips.Add(P1);

  if (bP2OnPlane)
     outSegTips.Add(P2);

  if (bP1OnPlane && bP2OnPlane)
     return;

  if (d1*d2 > eps)  // points on the same side of plane
     return;

  float t = d1 / (d1 - d2); // 'time' of intersection point on the segment
  outSegTips.Add( P1 + t * (P2 - P1) );
}

void TrianglePlaneIntersection(vector3d triA, vector3d triB, vector3d triC,
                               vector3dArray& outSegTips)
{
   GetSegmentPlaneIntersection( triA, triB, outSegTips));
   GetSegmentPlaneIntersection( triB, triC, outSegTips));
   GetSegmentPlaneIntersection( triC, triA, outSegTips));

   RemoveDuplicates(outSegTips);  // not listed here - obvious functionality 
}

希望这能给你一个想法,但仍然有很多潜在的优化。例如,如果你正在计算大网格中每个三角形的交点,你可以为每个顶点计算并缓存DistanceFromPlane一次,并且只需检索它参与的每个边缘。还可以根据您的情况和数据表示进行更高级的缓存。


我认为应该是 p1 + t * (p2 - p1); 而不是你所写的内容? - Martin
我想感谢您,并就一个问题请求澄清:triangleplaneintersection使用相同的参数三次调用“getsegmentplaneintersection”。这是正确的吗?还是我们应该传递“TriA-TriB”、“TriB-TriC”、“TriC-TriA”(我对这个主题很薄弱,仍然专注于解决方案,抱歉);另外,“eps”变量是什么?谢谢。 - roamcel
1
@roamcel:谢谢。错别字已经修正。'eps'是'epsilon'的缩写,是一个常见的变量名,用于表示小数值的容差(例如,在有限精度下应该为零或以上的计算可能会非常接近零)。 - Ofek Shilon

2
将三个点插入平面方程(由您列出的四个参数a,b,c,d定义),确定哪些对在平面的相反侧。
给定平面方程:
Ax + By + Cz + D = 0
其中A,B,C是法线(单位长度),D是到原点的距离,将点(x,y,z)代入并查看此结果是否为正或负。对于位于平面上的点,它将为零,并且当结果不为0时,符号将告诉您点所在的侧面。因此,选择在相反侧的点对(最多有2个),并使用标准的射线/平面交点公式计算这两个线段与平面的交点,这些将是您寻找的线段的2个点。
编辑 想一想,将点插入平面方程中得到的值应该对于插值两个点之间以获取线段与平面的交点很有用。
让Len Fn = Axn + Byn + Czn + D成为插入点n的结果。 然后假设F1 = -4,F2 = 8。因此,点P1和P2在平面的两侧。我们还会有P = P1 * 2/3 + P2 * 1/3是从P1到P2的线段与平面相交的点。将其普遍化为适当的公式留作练习。

正常情况下不必是单位长度(尽管如果它不是单位长度,D就不代表距离)。其余部分是正确的。另外,您忘记提到所有点都位于平面的一侧且没有交点的情况。 - SigTerm
是的,我有点粗心——你关于normal和方程的说法是正确的。此外,1/3和2/3被颠倒了(已编辑),较小的值更接近平面并获得更接近1的权重。当所有点都在一侧时,所有Fn将具有相同的符号,没有交点。 - phkahler

1

这取决于你有哪些库。我创建了自己的几何库,可以计算线与平面的交点。在这种情况下,计算三角形的三条边的交点,然后计算它们中哪些位于顶点之间。这可能是0(无交点),或2个交点,这就是你想要的情况。(有特殊情况,两个点是重合的-三角形的一个点)。


1

找到三角形边缘的每条线段与平面的交点。合并相同的点,然后:

  • 如果没有交点,则没有交集
  • 如果存在一个交点(即你找到了两个交点但它们在容差范围内是相同的),则三角形的一个点刚好触碰平面
  • 如果有两个点,则它们之间的线段就是交点

下一步,在 Stack Overflow 上搜索线段与平面交点算法(或者使用你的框架提供的算法)...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接