射线与三角形相交检测

直观方法:判断射线是否与三角形所在的平面相交,如果相交,计算出射线与平面交点,如果交点在三角形内,则射线与三角形相交

接上一篇 射线与平面相交检测,在这里直接使用射线与平面相交计算出来的交点坐标,判断交点坐标是否在三角形内。

判断一个点是否在三角形内,需要使用到 Barycentric Coordinates(质心坐标系)

质心坐标系的基本概念:

几何结构中,图形中的点相对各顶点的位置,三角形中各顶点由(1, 0, 0),(0, 1, 0),(0, 0, 1)分别表示,则三角形内所有点的坐标 (u, v, w) 可以通过和三个顶点的关系求得,

且 w = 1 - u - v

     u, v, w >= 0

三角形内各点坐标实际位置,可由公式 V(x,y,z) = u * p0, v * p1 + w * p2求得,其中 p0, p1, p2 分别为三角形各顶点的实际坐标。

看下图

三角形内任一点 V 的坐标可以由一个顶点和它相邻的两条边来表示

V = p0p1 * v + p0p2 * u

其中 向量p0p1 = p1 - p0,向量 p0p2 = p2 - p0

在3D 下计算 u、v、w 比较麻烦,效率比较低,所以一般情况下是将三角形投影到一个2D平面上来计算,根据三角形法线在 x/y/z 三条轴上的分量值中最大者所在轴作为投影平面,如果三角形的法线为 (x,y,z) = (0.267, 0.535, -0.802),因为z值最大,则选取投影面为 X轴Y轴组成的  XY平面,有同学可能会疑问 x、y 值 大于0,z 值小于零,为什么是 z值最大??因为这里取的是绝对值。

根据投影后计算的 u、v 值判断交点是否在三角形内

代码逻辑如下

public class RayTriangleCollision
{
    /// <summary>
    /// 射线与三角形相交检测
    /// </summary>
    /// <param name="source">射线起点坐标</param>
    /// <param name="rayDirection">射线方向以及长度</param>
    /// <param name="p0">三角形一个顶点</param>
    /// <param name="p1">三角形一个顶点</param>
    /// <param name="p2">三角形一个顶点,三角形三个顶点是有顺序要求的,因为输入的顶点次序(顺时针、逆时针)决定三角形法线的朝向</param>
    /// <param name="hitPos">相交情况下交点坐标</param>
    /// <returns>返回值为 true 为相交、false 为不相交</returns>
    public bool IsRayTriangleCollision(Vector3 source, Vector3 rayDirection, Vector3 p0, Vector3 p1, Vector3 p2, ref Vector3 hitPos)
    {
        Vector3 E1 = p1 - p0;
        Vector3 E2 = p2 - p1;
        // 三角形所在平面法向量
        Vector3 triangleNormal = Vector3.Cross(E1, E2);

        // 射线起点到三角形一个顶点的向量
        Vector3 PC = p0 - source;
        float dot_rayDir_planeNormal = Dot(rayDirection, triangleNormal);
        float dot_pa_planeNormal = Dot(PC, triangleNormal);

        if (   dot_rayDir_planeNormal == 0 
            || (dot_rayDir_planeNormal > 0 && dot_pa_planeNormal < 0) 
            || (dot_rayDir_planeNormal < 0 && dot_pa_planeNormal > 0))
        {
            return false;
        }

        // 向量长度不足以到达三角形所在的平面
        if (Mathf.Abs(dot_rayDir_planeNormal) < Mathf.Abs(dot_pa_planeNormal))
        {
            return false;
        }

        // 计算射线到平面的距离
        float length = dot_pa_planeNormal / dot_rayDir_planeNormal;
        // 计算射线与平面交点坐标
        hitPos = source + rayDirection * length;

        float u0, u1, u2;
        float v0, v1, v2;
        // 找到主要的轴,选择投影平面
        if (Mathf.Abs(triangleNormal.x) > Mathf.Abs(triangleNormal.y))
        {
            if (Mathf.Abs(triangleNormal.x) > Mathf.Abs(triangleNormal.z))
            {
                u0 = hitPos.y - p0.y;
                u1 = p1.y - p0.y;
                u2 = p2.y - p0.y;
                v0 = hitPos.z - p0.z;
                v1 = p1.z - p0.z;
                v2 = p2.z - p0.z;
            }
            else
            {
                u0 = hitPos.x - p0.x;
                u1 = p1.x - p0.x;
                u2 = p2.x - p0.x;
                v0 = hitPos.y - p0.y;
                v1 = p1.y - p0.y;
                v2 = p2.y - p0.y;
            }
        }
        else if (Mathf.Abs(triangleNormal.y) > Mathf.Abs(triangleNormal.z))
        {
            u0 = hitPos.x - p0.x;
            u1 = p1.x - p0.x;
            u2 = p2.x - p0.x;
            v0 = hitPos.z - p0.z;
            v1 = p1.z - p0.z;
            v2 = p2.z - p0.z;
        }
        else
        {
            u0 = hitPos.x - p0.x;
            u1 = p1.x - p0.x;
            u2 = p2.x - p0.x;
            v0 = hitPos.y - p0.y;
            v1 = p1.y - p0.y;
            v2 = p2.y - p0.y;
        }

        // 计算分母,检查其有效性
        float temp = u1 * v2 - v1 * u2;
        if (temp == 0)
        {
            return false;
        }
        temp = 1.0f / temp;
        //计算重心坐标,每一步都检测边界条件
        float alpha = (u0 * v2 - v0 * u2) * temp;
        if (alpha < 0)
        {
            return false;
        }

        float beta = (u1 * v0 - v1 * u0) * temp;
        if (beta < 0)
        {
            return false;
        }

        float gamma = 1.0f - alpha - beta;
        if (gamma < 0)
        {
            return false;
        }

        return true;
    }

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

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
破片射线三角形面元相交算法中,存在一种直接判断的方法,即Möller–Trumbore算法(M-T算法)[1][2。M-T算法通过射线三角形相交检测来确定破片射线是否与三角形面元相交。这个算法利用了射线与平面的相交检测以及射线三角形相交检测。 具体来说,M-T算法使用了克莱姆法则和三矢量的混合积来判断射线三角形相交情况。通过计算射线三角形相交点,可以确定是否存在相交。该算法相比传统的求解方法更直接、更快速,并且适用于各种场景。 因此,使用Möller–Trumbore算法可以有效地判断破片射线三角形面元是否相交。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [【收藏好文】一文读懂射线三角形相交算法Moller-Trumbore算法](https://blog.csdn.net/oakchina/article/details/124385464)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [空间射线三角形相交算法的两种实现](https://blog.csdn.net/charlee44/article/details/104348131)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值