线形对象与平面相交

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

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

L(t)=P+td⃗ 

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

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

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

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

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

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⃗ =0 aPx+bPy+cPz+D=0 直线位于平面内
  2. n⃗ d⃗ =0 aPx+bPy+cPz+D0 直线与平面平行
  3. n⃗ d⃗ 0 直线与平面有唯一交点

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

2. 射线与平面相交

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

总结如下:

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

3. 线段与平面相交

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

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

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

总结如下:

  1. 直线方程的方向向量必须是 P1P0 (直线和射线只要方向相同即可,甚至可以单位化方向向量,但是线段必须是 P1P0
  2. n⃗ d⃗ =0 aPx+bPy+cPz+D=0 直线位于平面
  3. n⃗ d⃗ =0 aPx+bPy+cPz+D0 直线与平面平行
  4. n⃗ d⃗ 0 t[0,1] 射线与平面有唯一交点
  5. n⃗ d⃗ 0 t[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]
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值