光线步进(RayMarching)---AABB和OBB包围盒

问题:为什么做光线步进要学习包围盒
答案:因为全局搜索3DTexture费时,需要指定的位置进行步进,减少不必要遍历

射线和AABB包围盒的交点

AABB包围盒的简介可以网上找,很多的,这里不做介绍.
先给出射线和AABB盒的交点怎么求,出自于一篇知乎添加链接描述
在这里插入图片描述
先处理YZ的两个平面,可知射线和YZ两个面的左右交点在X轴的值分别是 和 ,因为是AABB包围盒,所以可以知道 和 的值,所以 和 也是可知的.则有
在这里插入图片描述
计算可得
在这里插入图片描述
其中
在这里插入图片描述
写到这一步了,可以想下, 作为光线方向在x轴上的分量
如果 为0,那就说明射线和YZ两个平面平行,但是不知道射线的起点是在包围盒里面还是外面;所以需要进一步判断,若 ,说明点是在包围盒外面的,则射线和包围盒肯定不相交;就算 ,也不代表起点在包围盒内,只能说明起点是在YZ无限延伸的平面内,需要进一步利用包围盒的YZ轴判断,即 才能真正确定P点在包围盒内.
如果 不为0,则有
在这里插入图片描述
在论文上的伪代码来看,由于是AABB盒,设中心点为
则知 ,其中 是包围盒三个轴的长度,可知都大于0
进一步知
在这里插入图片描述
其中
在这里插入图片描述
是包围盒三个轴的长度,可知都大于0
进一步知
在这里插入图片描述

判断AABB和OBB包围盒和射线是否有交点的原理是两个区间范围是否重复
先看下对应的链接
添加链接描述
添加链接描述
在这里插入图片描述
即max(start) >min(end),则两个区间不相交
再看下射线和AABB包围盒是否有交点的两个方法
1:依次判断和面是否相交 链接https://zhuanlan.zhihu.com/p/610258258
2:Slab方法 链接添加链接描述

第一种方法(直接口述)
判断射线起点是否在3D内部,如果在,则必相交
若射线起点不在3D内部,遍历每一个轴向,求出和近面的交点,判断交点是否在包围盒的内部
重点: 怎么求出近面的交点;如果射线方向和某一个轴向的方向的夹角是钝角(也就是点乘<0),那么近面就是该轴对应值Max的面;如果是锐角,那么近面就是该轴对应值Min的面,有demo演示
在这里插入图片描述
以遍历到X轴正方向为例
对于射线AB,可知是锐角,则近面就是X轴对应值小的Xmin面,也就是左边红色的面;
对于射线CD,可知是钝角,则近面就是X轴对应值大的Xmax面,也就是右边绿色的面
换句话说,需要提前计算好射线和轴的方向,这样就能算出和近面的交点,且根据射线的参数方程求出的t值,如果t值小于0,则射线和平面必不相交;如果t值大于0,则需要通过t值算出的交点位置判断是否在包围盒里面

第二种方法
原理就是:求出射线和6个面的交点(以参数的形式代替),也就是说有3个区间;确定好区间的左值和右值(需要进行两个t之间的判断,这就是论文上有对t0和t1做大小判断的原因,因为无法确定大小,所以强制判断);然后对三个区间的左值做max得到left,对三个区间的右值做min得到right,若left>right,则不相交;left<=right,相交.论文的伪代码即
在这里插入图片描述
现在以demo做演示
在这里插入图片描述
由于在Word上已经写好了公式,所以在CSDN上不想再写了,直接截图了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这就是为什么论文上要有交换最大最小值的原因,以确定 始终是最小值
现给出C++的基本实现代码 参考链接添加链接描述

/// @brief 判断射线和AABB盒是否有交点
/// @param pStartPos 射线起点
/// @param pEndPos 射线终点
/// @param pMinPlane 包围盒最小点 数组 里面有三个元素
/// @param pMaxPlane 包围盒最大点 数组 里面有三个元素
/// @param fMinValue 射线和近面交点的t值
/// @param fMaxValue 射线和远面交点的t值
/// @return 是否相交
bool JudgeAABBIntersection(const float* pStartPos, const float* pEndPos, const float* pMinPlane, const float* pMaxPlane, float& fMinValue, float& fMaxValue)
{
    static const float EPS = 1e-6f;
    float fRayDir[3];
    fRayDir[0] = pEndPos[0] - pStartPos[0];
    fRayDir[1] = pEndPos[1] - pStartPos[1];
    fRayDir[2] = pEndPos[2] - pStartPos[2];
    fMinValue = 0.0;
    fMaxValue = 1.0f;

    // 遍历三个面
    for (int i = 0; i < 3; i++)
    {
        // 射线方向和当前轴平行
        if (std::fabs(fRayDir[i]) < EPS)
        {
            if (pStartPos[i] < pMinPlane[i] || pStartPos[i] > pMaxPlane[i])
            {
                return false;
            }
        }
        else
        {
            //通过上面的求t值公式,求出AABB盒的每个轴上的两个面分别与射线的交点的t值,求出一个大值为远面交点t值,小值为近面交点t值,此时各个面的法向量n在该轴值可取为1,其它两个轴上分值为0
            const float ood = 1.0f / fRayDir[i];
            float t1 = (pMinPlane[i] - pStartPos[i]) * ood;
            float t2 = (pMaxPlane[i] - pStartPos[i]) * ood;
            if (t1 > t2) { float tmp = t1; t1 = t2; t2 = tmp; }
            if (t1 > fMinValue) fMinValue = t1;
            if (t2 < fMaxValue) fMaxValue = t2;
            if (fMinValue > fMaxValue) return false;
        }
    }
    return true;
}

射线和OBB包围盒的交点

OBB盒相对于AABB盒的一个特点就是轴向不是(1,0,0), (0,1,0), (0,0,1),的类似形式,但是求相交的思想是一样的.
唯一不同的就是需要根据包围盒的长宽高计算.
在这里插入图片描述如图所示,该图也是来自于链接添加链接描述
可知向量
在这里插入图片描述
则剩下的操作就是和AABB盒是一样的.
现给出C++的基本实现代码 参考实现
添加链接描述
添加链接描述


float GetDot3D(const float* pV1, const float* pV2)
{
    if (pV1 == nullptr || pV2 == nullptr)
    {
        return;
    }
    return pV1[0] * pV2[0] + pV1[1] * pV2[1] + pV1[2] * pV2[2];
}

/// @brief 射线和OBB盒是否相交 可能有些特殊场景没有考虑到 但思想没有问题
/// @param pStartPos 射线起点
/// @param pEndPos 射线终点
/// @param pOBBCenter OBB盒中心点
/// @param vecAxis OBB盒对应的轴方向
/// @param pOBBXYZ OBB盒对应的长宽高
/// @param fMinValue 射线和近面交点的t值
/// @param fMaxValue 射线和远面交点的t值
/// @return 是否相交
bool JudgeOBBIntersection(const float* pStartPos, const float* pEndPos, const float* pOBBCenter, 
                            const std::vector<float*> vecAxis, const float* pOBBXYZ,
                            float& fMinValue, float& fMaxValue)
{
    // 判空 其它输入参数类似 仅作示例
    if (pStartPos == nullptr || vecAxis.size() != 3)
    {
        return;
    }

    static const float EPS = 1e-6f;
    float fRayDir[3];
    fRayDir[0] = pEndPos[0] - pStartPos[0];
    fRayDir[1] = pEndPos[1] - pStartPos[1];
    fRayDir[2] = pEndPos[2] - pStartPos[2];
    fMinValue = 0.0;
    fMaxValue = 1.0f;

    float fCMinusP[3];
    fCMinusP[0] = pOBBCenter[0] - pStartPos[0];
    fCMinusP[1] = pOBBCenter[1] - pStartPos[1];
    fCMinusP[2] = pOBBCenter[2] - pStartPos[2];

    // 遍历三个面
    for (int i = 0; i < 3; i++)
    {
        // 射线方向和当前轴平行
        float fRayDirDotAxis = GetDot3D(fRayDir, vecAxis[i]);
        auto fCMinusPDotAxis = GetDot3D(fCMinusP, vecAxis[i]);
        auto fHalf = pOBBXYZ[i] / 2;
        if (std::fabs(fRayDirDotAxis) < EPS)
        {
            // 射线在OBB外 则必不相交
            if (std::fabs(fCMinusPDotAxis) > fHalf)
            {
                return false;
            }
        }
        else
        {
            const float ood = 1.0f / fRayDir[i];
            float t1 = (fCMinusPDotAxis - fHalf) * ood;
            float t2 = (fCMinusPDotAxis + fHalf) * ood;
            if (t1 > t2) { float tmp = t1; t1 = t2; t2 = tmp; }
            if (t1 > fMinValue) fMinValue = t1;
            if (t2 < fMaxValue) fMaxValue = t2;
            if (fMinValue > fMaxValue) return false;
        }
    }
    return true;
}

射线和AABB包围盒的交点在光线步进中的应用

讲了那么多,现在说下AABB的实际应用,在光线步进中的Shader中,就是利用这个思想实现射线和3DTexture(AABB)的噪声问题确定交点的,如图所示
在这里插入图片描述
glsl中min函数的解释,max函数也是类似的
在这里插入图片描述
说明下为什么Shader代码里的有些写法和上面的C++有些不一样呢.
1:在Shader要尽量避免if等逻辑判断,尽可能的使用Shader中自带的函数
2:图中97,98行,求出射线和三个轴的交点,这就是glsl语法的神奇之处,不用for循环,可以直接求出;
图中100,101行,相当于求出射线和三个轴的交点的最大最小值;
图中103-106行,利用两次min和max函数求出上述论文(两个区间的start和end)中描述的t0和t1,再利用两者的大小关系就可以判断射线和AABB盒是否相交;
图中115-116行,即求出射线交点和近远平面交点的t值;
其实还有优化空间,在hit=false的时候直接返回就可以

简易喷泉特效

20240129_032556

  • 22
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: AABB包围是一个长方形子,其各个面都与坐标轴平行。它通常用于快速确定物体是否与另一个物体发生碰撞。 包围球是一个具有固定半径的球形对象,它通常用于包围一个物体或一组物体。包围球可以用于求取物体之间的距离或者相交情况。 方向包围OBB是一个最适合物体形状的子,它可以朝着任意方向旋转。OBB可以用于求取物体之间的碰撞或相交关系。 固定方向凸包FDH是一个凸多边形,它是根据点云构造出来的具有一定厚度的表面。FDH可以用于进行3D渲染或者物体刚体碰撞计算。 希望我的回答能够满足您的需求。如果您还有其他问题,请随时提出。 ### 回答2: AABB包围包围球、方向包围OBB和固定方向凸包FDH是计算机图形学和碰撞检测中常用的表示物体边界的方法。 AABB包围(Axis-Aligned Bounding Box)是一种简单的包围形式,它是通过对象的最小和最大的x、y和z值定义的。AABB包围是一个与坐标轴对齐的长方体。在碰撞检测中,通过比较两个AABB包围之间的位置关系,可以快速判断是否发生碰撞。 包围球是一个以物体中心为原点,半径为半径的球体。它可以更准确地逼近物体形状,但相对于AABB包围来说计算复杂度更高。包围球对于检测是否有物体与其他物体相交很有用。 方向包围OBB(Oriented Bounding Box)是一种能够任意朝向的子。与AABB包围不同,OBB是一个可以在3D空间中旋转的长方体。OBB可以更准确地逼近对象的形状,但相对于AABB包围来说计算复杂度更高。OBB广泛应用于碰撞检测和物体包围等领域。 固定方向凸包FDH(Fixed Direction Convex Hull)是一个定义了对象的最小凸多边形的包围体。它可以通过一系列的向量定义,并且具有固定的朝向。FDH通常用于包围多个物体或者给定一个3D场景的碰撞检测。 ### 回答3: AABB包围(Axis-Aligned Bounding Box)是一种包围物体的立方体框,其边与坐标轴平行。这意味着包围的六个面总是与世界坐标系的坐标轴对齐。AABB包围通常用于快速确定物体之间是否相交,以及进行碰撞检测。 包围球(Bounding Sphere)是一种包围物体的球体,其半径足够小以便覆盖了整个物体。包围球的中心点位于物体的重心处。包围球常用于确定物体之间的距离,例如在碰撞检测和视锥剔除(Frustum Culling)中使用。 方向包围OBB(Oriented Bounding Box)是一种包围物体的立方体框,与物体的方向相关。与AABB不同,OBB的边不必与坐标轴平行,其方向可以被旋转调整以适应物体的朝向。OBB在一些模拟和碰撞检测算法中被广泛使用。 固定方向凸包FDH(Fixed-Orientation Convex Hull)是一种凸多边形,其边界能够完全包围物体。FDH通常在较早期的物理引擎中使用,用于对物体进行碰撞检测和坐标转换。通过简单的几何运算,FDH可以更高效地检测碰撞,并提供物体之间的最小接触区域。然而,FDH也有一些局限性,例如无法准确描述非凸物体的形状。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值