其他分享
首页 > 其他分享> > 射线与平面相交检测

射线与平面相交检测

作者:互联网

射线与平面相交检测

3D 中射线与平面相交检测
本篇讨论的射线是无限长,平面无限大
下图展示的几种位置关系:相交、不相交
相交包括:只有一个交点 (射线3)、射线在平面内有无数交点 (射线4)
不相交:射线与平面平行 (射线1),射线与平面不平行也不相交 (射线2、射线5)

在这里插入图片描述

判断射线2和射线5和平面不相交,添加辅助线如下
在这里插入图片描述
在平面上任取一点C
射线2中:P1为射线起点,向量P1A 是射线的方向向量, 连接P1C
点乘:Dot(向量P1A,平面法向量 Normal) > 0
点乘:Dot(向量P1C,平面法向量 Normal < 0
两个点乘结果异号(一个大于0,一个小于0) 则判定射线2 与平面不相交

射线5中:P2为射线起点,向量 P2B 是射线的方向向量,连接P2C
点乘:Dot(向量P2B,平面法向量 Normal) < 0
点乘:Dot(向量P2C,平面法向量 Normal > 0
两个点乘结果异号(一个大于0,一个小于0) 则判定射线2 与平面不相交

判断射线1、射线4,添加辅助线如下图
在这里插入图片描述

在平面上任取一点C
射线1中:P1为射线起点,向量P1A 是射线的方向向量, 连接P1C
点乘:Dot(向量P1A,平面法向量 Normal) = 0 说明 P1A 垂直于平面法向量,则P1A平行于平面
点乘:Dot(向量P1C,平面法向量 Normal < 0 说明 P1C 至少一个端点不在平面上
两个点乘结果(一个等于0,一个小于0) 则判定射线2 与平面平行但不在平面内

射线4中:P2为射线起点,向量 P2B 是射线的方向向量,连接P2C
点乘:Dot(向量P2B,平面法向量 Normal) = 0 说明 P2B 垂直于平面法向量,则P2B平行于平面
点乘:Dot(向量P2C,平面法向量 Normal = 0 说明 P2C 垂直于平面法向量,则P2C平行于平面
则 P2BC 所组成的平面和原平面平行,C点又是原平面中的点,则P2BC和平面重合,则P2B在原平面内
结论射线4在平面内

射线3的相交检测,如果相交,求交点坐标。看下图辅助线以及标记
在这里插入图片描述
平面上任取一点C
PB 为从点P到平面做的垂线,h 为向量PB 的长度
O为射线3与平面的交点,f 为向量PO的长度,
O点坐标 = P坐标 + 向量PO
向量PO = 射线3方向向量(rayDirection 单位向量) * f 简写为 rayDirection * f
O点坐标 = P坐标 + rayDirection * f
我们通过点C来求解 f

在三角形 PBC 中
h = Math.Abs( Dot(向量PC,平面法向量Normal)) 因为我需要的是长度,所以取绝对值
这一步可以得到 h 的值。

在三角形 PBO 中
h = Math.Abs( Dot(向量PO,平面法向量Normal)) 然后将 向量PO = rayDirection * f 带入公式

h = Maht.Abs(Dot(rayDirection * f,平面法向量Normal), 平面法向量 Normal),其中 f 是标量,可以提取出来

h = f * Maht.Abs(Dot(rayDirection * f, 平面法向量 Normal)

f = h 除以 Maht.Abs(Dot(rayDirection, 平面法向量 Normal)

最终O坐标 = P坐标 + rayDirection * f

代码逻辑如下

/// <summary>
/// 射线与平面相交检测
/// </summary>
public class RayPlaneCollision
{

    float dot_rayDir_planeNormal;
    float dot_pa_planeNormal;

    /// <summary>
    /// 射线与平面相交检测
    /// </summary>
    /// <param name="source">射线起点坐标</param>
    /// <param name="rayDirection">射线方向</param>
    /// <param name="planePos">平面上任一点坐标</param>
    /// <param name="planeNormal">平面法向量</param>
    /// <returns></returns>
    public RayPlaneCollisionEnum IsCollision(Vector3 source, Vector3 rayDirection, Vector3 planePos, Vector3 planeNormal)
    {
        Vector3 PC = planePos - source;
        dot_rayDir_planeNormal = Dot(rayDirection, planeNormal);
        dot_pa_planeNormal = Dot(PC, planeNormal);

        // 射线平行于平面
        if (dot_rayDir_planeNormal == 0) 
        {
            if (dot_pa_planeNormal == 0)
            {
                // 射线和平面平行并且射线在平面内
                return RayPlaneCollisionEnum.IN_PLANE;
            }
            // 射线和平面平行但是射线不在平面内
            return RayPlaneCollisionEnum.SEPARATION;
        }

        if (dot_rayDir_planeNormal > 0 && dot_pa_planeNormal > 0)
        {
            return RayPlaneCollisionEnum.COLLISION;
        }

        if (dot_rayDir_planeNormal < 0 && dot_pa_planeNormal < 0)
        {
            return RayPlaneCollisionEnum.COLLISION;
        }

        return RayPlaneCollisionEnum.SEPARATION;
    }

    public Vector3 CollisionPosition(Vector3 source, Vector3 rayDirection, Vector3 planePos, Vector3 planeNormal)
    {
        if (IsCollision(source, rayDirection, planePos, planeNormal) != RayPlaneCollisionEnum.COLLISION)
        {
            return Vector3.zero;
        }
        float length = dot_pa_planeNormal / dot_rayDir_planeNormal;

        Debug.LogError(dot_pa_planeNormal + "   " + dot_rayDir_planeNormal);
        return source + rayDirection * length;
    }

    public float Dot(Vector3 vector1, Vector3 vector2)
    {
        return vector1.x * vector2.x + vector1.y * vector2.y + vector1.z * vector2.z;
    }
}

本篇开始说的限制条件 射线无限长、平面无限大,如果射线限制了长度 s,平面限制了区域(有N个顶点的多边形),对于不相交的判定没有影响

对于射线和平面相交的,如果射线限制长度,则 根据上边计算的长度 f 和 限制长度 s 比较,如果 s 小于 f,则射线长度不够,不相交
如果 s >= f,计算出 O 点坐标,然后判断 O点坐标是否在平面范围内

对于射线在平面内的思考下

标签:Normal,相交,planeNormal,射线,向量,平面,Dot
来源: https://blog.csdn.net/LIQIANGEASTSUN/article/details/119462082