射线与AABB型包围盒相交算法

本文详细解析了射线与轴对称包围盒(AABB)的相交算法,通过计算射线与包围盒各面的交点,判断是否相交。适用于3D游戏开发和计算机图形学,提供了OpenGLES三维空间射线与AABB盒相交的计算源码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基础知识:

AABB包围盒,也叫轴对称包围盒,意思就是它的六个面总是分别平行XYZ三个轴的

相交计算原理:

计算射线与包围盒每个面的平面的交点,计算这个点是否在包围盒面的范围,在就是相交,不在就是没有相交

图解:

用个2D图形简单讲解一下
在这里插入图片描述

首先从图中可以看到射线Ray和红色的包围盒相交了,但是怎么计算出来(毕竟在3D世界中,不可能全靠眼睛去看…)

这个包围盒有4个面,2个平行于Y轴,2个平行于X轴,有两个关键点,最小点min和最大点max(即xy值最小和最大,有正负)

下面是伪代码

//首先是看射线Ray,这条射线的原点是origin,方向是direction,
Ray ray;
if(ray.origin.x < min.x)//原点的X小于min的X,那么测试射线与垂直于X轴的面的相交点。
{
    //假设射线的X方向到达min平面的时间为t
   float t   =   (min.x - _origin.x) / _direction.x;
    //那么可以计算出射线与平面的交点为hitPoint1
    Point hitPoint1 = ray.origin + ray.direction * t;
    if(hitPoint1.y> min.y && hitPoint.y < max.y)//比较,是不是在包围盒的面内
    {
        return true;
    }
}
if(ray.origin.y < min.y)//原点的y小于min的y,那么测试射线与垂直于Y轴的面的相交点。
{
   //假设射线的Y方向到达min平面的时间为t
   float t   =   (min.y - _origin.y) / _direction.y;
   //那么可以计算出射线与平面的交点为hitPoint2
   Point hitPoint1 = ray.origin + ray.direction * t;
    if(hitPoint1.x> min.x && hitPoint.x < max.x)//比较,是不是在包围盒的面内
    {
        return true;
    }
}

三维空间里也是同样的道理。

下面是OpenGLES三维空间射线与AABB盒相交的计算源码

 /**
        *   测试射线box相交
        *   如果相交,返回值中的first == true.否则false
        *   second为射线到点的距离
        *   调用getPoint方法,则返回交点
        */
	    std::pair<bool, T> intersects(const AxisAlignedBox<T>& box) const
        {
            T           lowt    = 0.0f;
            T           t;
            bool        hit     = false;
            tvec3<T>    hitpoint;
            tvec3<T>    min      =   box.getMinimum();
            tvec3<T>    max      =   box.getMaximum();

            /**
            *   点在包围盒里面
            */
            if ( _origin > min && _origin < max )
            {
                return std::pair<bool, T>(true, 0);
            }

            // Check each face in turn, only check closest 3
            // Min x
            if (_origin.x <= min.x && _direction.x > 0)
            {
                t   =   (min.x - _origin.x) / _direction.x;
                if (t >= 0)
                {
                    // Substitute t back into ray and check bounds and dist
                    hitpoint = _origin + _direction * t;
                    if (hitpoint.y >= min.y && 
                        hitpoint.y <= max.y &&
                        hitpoint.z >= min.z && 
                        hitpoint.z <= max.z &&
                        (!hit || t < lowt))
                    {
                        hit     =   true;
                        lowt    =   t;
                    }
                }
            }
            // Max x
            if (_origin.x >= max.x && _direction.x < 0)
            {
                t   =   (max.x - _origin.x) / _direction.x;
                if (t >= 0)
                {
                    // Substitute t back into ray and check bounds and dist
                    hitpoint = _origin + _direction * t;
                    if (hitpoint.y >= min.y && 
                        hitpoint.y <= max.y &&
                        hitpoint.z >= min.z &&
                        hitpoint.z <= max.z &&
                        (!hit || t < lowt))
                    {
                        hit     =   true;
                        lowt    =   t;
                    }
                }
            }
            // Min y
            if (_origin.y <= min.y && _direction.y > 0)
            {
                t   =   (min.y - _origin.y) / _direction.y;
                if (t >= 0)
                {
                    // Substitute t back into ray and check bounds and dist
                    hitpoint = _origin + _direction * t;
                    if (hitpoint.x >= min.x && 
                        hitpoint.x <= max.x &&
                        hitpoint.z >= min.z && 
                        hitpoint.z <= max.z &&
                        (!hit || t < lowt))
                    {
                        hit     =   true;
                        lowt    =   t;
                    }
                }
            }
            // Max y
            if (_origin.y >= max.y && _direction.y < 0)
            {
                t   =   (max.y - _origin.y) / _direction.y;
                if (t >= 0)
                {
                    // Substitute t back into ray and check bounds and dist
                    hitpoint = _origin + _direction * t;
                    if (hitpoint.x >= min.x &&
                        hitpoint.x <= max.x &&
                        hitpoint.z >= min.z &&
                        hitpoint.z <= max.z &&
                        (!hit || t < lowt))
                    {
                        hit     =   true;
                        lowt    =   t;
                    }
                }
            }
            // Min z
            if (_origin.z <= min.z && _direction.z > 0)
            {
                t   =   (min.z - _origin.z) / _direction.z;
                if (t >= 0)
                {
                    // Substitute t back into ray and check bounds and dist
                    hitpoint = _origin + _direction * t;
                    if (hitpoint.x >= min.x && 
                        hitpoint.x <= max.x &&
                        hitpoint.y >= min.y &&
                        hitpoint.y <= max.y &&
                        (!hit || t < lowt))
                    {
                        hit     =   true;
                        lowt    =   t;
                    }
                }
            }
            // Max z
            if (_origin.z >= max.z && _direction.z < 0)
            {
                t   =   (max.z - _origin.z) / _direction.z;
                if (t >= 0)
                {
                    // Substitute t back into ray and check bounds and dist
                    hitpoint = _origin + _direction * t;
                    if (hitpoint.x >= min.x && 
                        hitpoint.x <= max.x &&
                        hitpoint.y >= min.y && 
                        hitpoint.y <= max.y &&
                        (!hit || t < lowt))
                    {
                        hit     =   true;
                        lowt    =   t;
                    }
                }
            }
            return std::pair<bool, T>(hit, lowt);
        }
    };
层次包围盒算法(Hierarchical Bounding Volume Hierarchy)是一种用于加速射线物体碰撞检测的算法。它通过构建一个层次结构,将场景中的物体划分为多个包围盒,从而减少射线物体的碰撞检测次数,提高检测效率。 典的层次包围盒算法有以下几种: 1. BVH(Bounding Volume Hierarchy):BVH是一种基于二叉树结构的层次包围盒算法。它将场景中的物体划分为一系列包围盒,并通过递归地将这些包围盒组织成二叉树。每个节点的包围盒包围了其子节点的包围盒,从而形成了一个层次结构。在进行射线物体碰撞检测时,可以通过遍历BVH树来快速确定射线相交的物体。 2. AABB树(Axis-Aligned Bounding Box Tree):AABB树是一种基于轴对齐包围盒的层次包围盒算法。它将场景中的物体划分为一系列轴对齐包围盒,并通过递归地将这些包围盒组织成树形结构。每个节点的包围盒坐标轴平行,从而简化了碰撞检测的计算。在进行射线物体碰撞检测时,可以通过遍历AABB树来快速确定射线相交的物体。 3. KD树(K-Dimensional Tree):KD树是一种基于k维空间划分的层次包围盒算法。它将场景中的物体划分为一系列k维空间的区域,并通过递归地将这些区域组织成树形结构。每个节点的划分平面某个坐标轴平行,从而实现了对k维空间的快速划分。在进行射线物体碰撞检测时,可以通过遍历KD树来快速确定射线相交的物体。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值