首页/文章/ 详情

接触基础知识:从二分到四叉树与八叉树

2月前浏览135

前面讲了很多具体的搜索方法,如

有限元基础知识:AABB包围盒的计算

有限元基础知识:如何计算OBB包围盒

有限元基础知识:接触检测中的分离轴定理SAT

也有说了最为基础的空间分割算法 

有限元基础知识:接触计算中的包围盒子

有限元基础知识:空间箱排序(bucket sorting)

今天就来讲一下一种经典的空间分割方法(也随之带来一种数据结构),其实这种方法很简单,我们在1维的逻辑中有一种经典方法叫做二分(遇事不决便二分),那么在2D平面上就是今天要说的四叉树,而在3D空间中就是八叉树,可以理解为     树,     为空间的维度

二分法顾名思义,就是一种逐渐缩小范围的方法,每次将搜索范围除2,比如一个人让你猜他多大岁数,你大概看出来他在25-35之间,你就先猜一下30,他说大了,你再猜一下28,......, 这样几波下来你很快就知道他多大了。

四叉树就跟这个逻辑非常一致,之前已经讲过一个接触面和另一个接触面相交,可以先确定出来一个大的相交的AABB 的 boundingbox,然后在2D中就一个这样的盒子一划四,这么划分出去,直达某个终止条件,具体可以看如下的示意图


一样的思想可以拓展到八叉树上,如下图所示 ,唯一的区别就是从平面到空间,从叉除四个节点到叉出八个节点

大家在很多论文和书上会听到有人说“树结构”画上面类似的图,初看其实可能不明觉厉,其实nothing fancy。

这个思想非常简单,那么他有啥好处呢,好处就在于可以更快速的定位,回想刚刚猜岁数的例子,猜的人也可以从25一路猜到35,而用二分的好处就是在于整个过程的时间复杂度是     , 而一个个查的复杂度是     , 这两者在数据量越大的时候效率差距会越明显。

现在想象我们在2D中用最为天真的构造方法,将接触面的主面     个单元面放到一个四叉树结构中,然后我们有     个从节点(点对面接触搜索逻辑)需要进行接触索,那么时间复杂度是啥样的呢

 

这里每一个从节点需要进行一次主面的定位,而定位主面的复杂度是    。当然还有一些优化方法可以进一步提升,但不是今天要讨论的,后续可作为番外篇。

今天就简单说了一下四叉树和八叉树,其实对于空间分割和接触检测的树状结构还有很多,也有自己不同的适用场景,比如k-d tree, BSP (二元空间分割) tree, BVH (bouding volume hierarchy) tree。总的来说今天讲的是最简单的(当然不如直接画格子的空间箱排序简单),但也有诸多细节尤其是在FEM接触检测中,下次我来详细说说,然后再展开到其他树结构。

最后我突然又想到一个好玩的事,如果各位朋友喜欢这一系列,全都看下来说不定会有意外的惊喜,比如爱打游戏的朋友,所谓的光追(Ray Tracing)底层呢也都事用的ray casting和这些空间分割树状结构啥的,然后游戏中对于接触检测不同与有限元一般称为碰撞检测(collision detection)。


来源:大狗子说数值模拟
碰撞UM游戏
著作权归作者所有,欢迎分享,未经许可,不得转载
首次发布时间:2025-06-10
最近编辑:2月前
大狗子说数值模拟
博士 传播国际一流的数值模拟算法
获赞 10粉丝 25文章 72课程 0
点赞
收藏
作者推荐

有限元基础知识:AABB包围盒的计算

上次说了接触计算中的几种不同的包围盒子,有限元基础知识:接触计算中的包围盒子下面开始分别讲一下各自的编码实现,先从最简单的AABB开始,AABB包围盒由于每个面(3D)都与坐标系平行,所以其实只需要找到,x,y,z三个方向上所有节点的最大值和最小值就行了。下边以C++代码为例,给大家示范一下AABB的计算流程//函数定义usingpoint3D=std::array<double,3>;structAABB{point3Dmin;point3Dmax;voidinit();voidaddPoint(constpoint3D&p)};voidAABB::init(){min[0]=min[1]=min[2]=FLT_MAX;max[0]=max[1]=max[2]=-FLT_MAX;};voidaddPoint(constpoint3D&p){if(p[0]<min[0])min[0]=p[0];if(p[0]>max[0])max[0]=p[0];if(p[1]<min[1])min[1]=p[1];if(p[1]>max[1])max[1]=p[1];if(p[2]<min[2])min[2]=p[2];if(p[2]>max[2])max[2]=p[2];};//真正的形成一个AABBAABBbox;box.init();//对于主面或者从面的节点循环//pointsisavectorofpoint3Dfor(autop:points){box.addPoint(p);}那么这样一个主面或者从面的包围盒子就形成了,然而在真实的有限元计算中,由于我们需要定位潜在可能发生的接触,或者接触已经发生了,已经有了微小的穿透,以上还需要进行一点点补充,就是增加一些buffer,给现在的box扩大一圈,一般的做法如下:voidAABB::addBuffer(doubledis){min[0]-=dis;min[1]-=dis;min[2]-=dis;max[0]+=dis;max[1]+=dis;max[2]+=dis;};box.addBuffer(dis);这样我们就完成了一个扩大化的boundingbox,如下图所示,蓝色的就是最开始计算的box,而红色虚线就是加上了buffer的box那么然后就是如何检测两个boundingbox是否接触了,这个也非常简单,若所有轴均重叠则碰撞,否则就不boolAABBIntersect(constAABB&box1,constAABB&box2){return(box1.min[0]<=box2.max[0]&&box1.max[0]>=box2.min[0])&&(box1.min[1]<=box2.max[1]&&box1.max[1]>=box2.min[1])&&(box1.min[2]<=box2.max[2]&&box.max[2]>=box2.min[2]);}这样我们就能检测两个AABB的box是否接触,做一个粗放式的检查。好了今天就先到这,明天讲一下OBB与分离轴定理SAT的编码实现,敬请期待。来源:大狗子说数值模拟

未登录
还没有评论
课程
培训
服务
行家
VIP会员 学习计划 福利任务
下载APP
联系我们
帮助与反馈