线形对象与平面相交

来源:互联网 发布:国牌lolita淘宝店铺 编辑:程序博客网 时间:2024/06/04 18:27

三维空间中的线形对象包括直线、射线和线段,为了统一三者的表达,在三维图形学中一般使用一个点和方向向量的方式来描述这些线形对象。

假设有一个点P位于线形对象上,方向向量是d⃗ ,那么这个线形对象可以使用下面的公式来表达:

L(t)=P+td⃗ 

基于这个表达式,直线方程可以描述为:

L(t)=P+td⃗ (t)

射线方程可以描述为(P是射线的端点):

R(t)=P+td⃗ (0t)

在描述线段时,使用一对点{P0P1},则线段方程是:

S(t)=P0+t(P1P0)(0t1)

平面的描述方式很多,最简单的是使用一般式 ax+by+cz+d=0,向量 n⃗ =[a,b,c]是平面的法向量。

1. 直线与平面相交

PlaneLineIntersection

如上图所示,直线方程L是 P+td⃗ , 平面方程是 ax+by+cz+d=0,假设二者有交点(交于点Q),那么直线上的Q点也一定满足平面方程,也就是:

Q=P+td⃗  满足 ax+by+cz+D=0 (为区别直线的方向向量,平面方程的常数项写成D),将Q点带入平面方程中,得到:

a(Px+tdx)+b(Py+tdy)+c(Pz+tdz)+D=0

t=(D+aPx+bPy+cPz)adx+bdy+cdz

写成向量的形式是:

t=(D+n⃗ P)n⃗ d⃗ 

针对这个表达式展开讨论:

  1. 如果 n⃗ d⃗ =0 那么说明直线平行于平面或者直线位于平面之内。怎么判断直线是否位于平面之内呢?很简单,判断直线的另一个点P是否位于平面内即可,也就是把P点带入平面方程中,如果满足平面方程,那么直线位于平面内,不满足则二者平行。
  2. 如果 n⃗ d⃗ 0,那么直线和平面有唯一的交点,将t值带入到 Q=P+td⃗ 可以求出二者的交点坐标。

总结如下:

  1. n⃗ d⃗ =0aPx+bPy+cPz+D=0 直线位于平面内
  2. n⃗ d⃗ =0aPx+bPy+cPz+D0直线与平面平行
  3. n⃗ d⃗ 0 直线与平面有唯一交点

在编码过程中由于浮点数的舍入误差,一般将结果与一个阈值ϵ进行比较,通过比较快速判断直线与平面是否相交。

2. 射线与平面相交

射线的方程表达式与直线类似,但是射线稍有不同的是它表达式中的 P一定是射线的端点,如果P是射线的端点,那么计算的过程与直线中的结论完全一样,只不过需要在判断相交的时候t的取值,如果取值范围 t0,那么就可以带入到射线方程中求出交点Q,否则(也就是t<0)二者没有交点。

总结如下:

  1. n⃗ d⃗ =0aPx+bPy+cPz+D=0 直线位于平面内
  2. n⃗ d⃗ =0aPx+bPy+cPz+D0 直线与平面平行
  3. n⃗ d⃗ 0t0 射线与平面有唯一交点
  4. n⃗ d⃗ 0t<0 射线与平面没有交点

3. 线段与平面相交

线段的计算过程同样与射线类似,可以通过直线方程

S(t)=P0+t(P1P0)(0t1)

来计算,需要注意的是线段的方向向量 d⃗ ,必须是 P1P0,这样t的取值范围就是在[0,1]区间,如果t的取值范围不是[0,1],那么线段与平面无交点。

总结如下:

  1. 直线方程的方向向量必须是P1P0 (直线和射线只要方向相同即可,甚至可以单位化方向向量,但是线段必须是P1P0
  2. n⃗ d⃗ =0aPx+bPy+cPz+D=0 直线位于平面
  3. n⃗ d⃗ =0aPx+bPy+cPz+D0 直线与平面平行
  4. n⃗ d⃗ 0t[0,1] 射线与平面有唯一交点
  5. n⃗ d⃗ 0t[0,1] 射线与平面没有交点

4. 算法实现

使用OpenSceneGraph实现线段和平面的相交判断的代码如下:

/* *  线段和平面的相交关系判断:返回值是0表示没有交点,返回值是1表示二者有唯一交点,返回值是2表示线段位于平面内**/int intersectSegmentPlane(const osg::Plane& plane, const osg::LineSegment& lineSegment, osg::Vec3d& intersection){    double epsilon = 1e-6;    osg::Vec3d planeNorm = plane.getNormal();    osg::Vec3d segmentDirection = lineSegment.end() - lineSegment.start();    double dotPlaneNormSegDir = planeNorm * segmentDirection;    osg::Vec4d planeParam = plane.asVec4();    double lineInPlane = planeParam.x() * lineSegment.start().x() + planeParam.y() * lineSegment.start().y() +        planeParam.z() * planeParam.z() + planeParam.w();    if (fabs(dotPlaneNormSegDir) < epsilon)    {        //如果直线端点满足平面方程,那么直线在平面内        if (fabs(lineInPlane) < epsilon)        {            return 2;        }        else        {            return 0;        }    }    double planeNormDotP = -(planeParam.w() + planeNorm * lineSegment.start());    double t = planeNormDotP / dotPlaneNormSegDir;    if (t < 0 || t > 1)        return 0;    intersection = lineSegment.start() + segmentDirection*t;    return 1;}

5. 参考文献

  1. Line and Segment Intersections
  2. [书籍:Geometric Tools for Computer Graphics]