射线和三角面求交

一、平面射线与线段是否相交


1.1 相交检测

步骤

1、判断射线方向d单位向量)是否与线段所在方向AB是否平行,如果平行则不相交

2、假设射线与线段交点为P,则计算|AP||AB|的比例u

3、如果u<0或者u>1不相交

4、计算出|OP|的长度 t,由P=O+td即可计算出交点坐标


关键过程

1、d是否与AB平行

判断d是否和AB平行可以采用向量叉乘,如果叉乘结果为零,则表示两向量平行

2、计算ut

设交点为P,其坐标可以表示为:

P=O+td=A+uAB

所以:

uABtd=AO

即有:

{uxABtxd=xAOuyABtyd=yAO

依据克莱姆法则可知:

u=D1D

t=D2D

其中:

D=|xABxdyAByd|

D1=|xAOxdyAOyd|

D2=|xABxAOyAByAO|


1.2 几何意义

如下图向量ABAO构成成平行四边形ABBO,向量OPAB构成平行四边形OPPBABAB平移到AO重合),由于底边长都是|AB|且高度相同,所以这两个平行四边形面积相等。

另外平面向量叉乘可以表示两向量组成平行四边形的有向面积,所以t可以表示为ABAO的叉乘除以AB与单位方向向量d的叉乘,即上面的t=D2DD在该例中表示SODDBD2表示SABBO


1.3 代码实现

bool RayIntersection(const Point2 & ori, const Vector2& dir, const Point2& A, const Point2& B, double& t)
{
    Vector2 AB = B - A;
    // 1. 判断是否平行
    if (std::abs(dir.Cross(AB)) < std::numeric_limits<double>::epsilon() ){
        return false;
    }
    dir.Normalize();  // 归一化向量
    
    Vector2 AO = ori - A;
    
    double D = AB.Cross(-dir);
    double D1 = AO.Cross(-dir);  
     
    if(D1 < 0 || D1 > D){         // 减少一次除法运算,即 0 <= u <= 1。超出线段AB范围
        return false;
    }
    
    double D2 = AB.Cross(AO);
    t = D2 / D;
    return true;
}

二、空间射线与三角面是否相交


2.1 相交检测

步骤

1、判断射线方向d单位向量)与三角面所在平面是否共面,如果不共面则进行后续步骤,否则需要退回二维情况进行判断

2、假设射线与三角面ABC交点为P,则计算AP在向量AB方向的比例u和在向量AC方向的比例v

3、如果u<0或者u>1不相交

4、如果v<0或者u+v>1不相交如果u+v>1,则交点落在三角面BCD内)

4、计算出|OP|的长度 t,由P=O+td即可计算出交点坐标


关键过程

1、d是否与面ABC共面

可以采用ABAC向量叉乘计算出法线n,法线n满足右手法则,其长度表示平行四边行ABDC的面积;

然后判断法线n和方向向量d点乘是否为零,点乘为零则表示共面

2、计算uvt

设交点为P,其坐标可以表示为:

P=O+td=A+uAB+vAC

所以:

uAB+vACtd=AO

即有:

{uxAB+vxACtxd=xAOuyAB+vyACtyd=yAOuzAB+vzACtzd=zAO

依据克莱姆法则可知:

u=D1D

v=D2D

t=D3D

其中:

D=|xABxACxdyAByACydzABzACzd|

D1=|xAOxACxdyAOyACydzAOzACzd|

D2=|xABxAOxdyAByAOydzABzAOzd|

D3=|xABxACxAOyAByACyAOzABzACzAO|


2.2 几何意义

如下图向量ABAC以及AO构成成平行六面体(红色),向量AOAB以及AC构成平行六面体 (绿色)(ABAB平移到AP重合,ACAC平移到AP重合),由于底面都是ABDC且高度相同,所以这两个平行六面体体积相等。

另外三维向量叉乘可以结果长度表示两向量组成平行四边形的面积,方向为两个组成平面的法线方向,三个三维向量的混合积(先叉乘(底面积)再点乘(高))表示其有向体积。所以t可以表示为ABAC以及AO的混合积除以单位方向ABAC以及向量d的混合积,即上面的t=D3DD在该例中表示蓝色平行六面体的有向体积,D3表示红色平行六面体有向体积。


2.3 代码实现

bool RayIntersection(const Point3 & ori, const Vector3& dir, const Point3& A, const Point3& B, double& t)
{
    Vector3 AB = B - A;
    Vector3 AC = C - A;
    
    Vector3 n = AB.Cross(AC);
    // 1. 判断是否共面
    if (std::abs(dir.Dot(n)) < std::numeric_limits<double>::epsilon() ){
        // 共面,需要转换到平面判断是否相交,这里直接认为不相交
        return false;
    }
    dir.Normalize();  // 归一化向量
    
    Vector3 AO = ori - A;
    
    double D = n.Dot(-dir);
    double D1 = AO.Cross(AC).Dot(-dir);  
     
    if(D1 < 0 || D1 > D){         // 减少一次除法运算,即 0 <= u <= 1。超出线段AB范围
        return false;
    }
    
    double D2 = AB.Cross(AO).Dot(-dir);
    // 减少一次除法运算,即0 <= v <= 1, 其他情况超出线段AC的范围, 这里u+v<1限制落在三角面ABC中,u+v>1可能会落在BCD或四边形ABDC外部
    if(D2 < 0 || D1 + D2 > D){   
        return false;
    }
    
    double D3 = n.Dot(AO)
    t = D3 / D;
    return true;
}

参考链接

克莱姆法则及其几何意义

混合积

平行六面体有向体积示例

posted @   半夜打老虎  阅读(273)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
历史上的今天:
2021-03-03 pyinstall 打包 python代码为可执行文件(pytorch)
点击右上角即可分享
微信分享提示