首页/文章/ 详情

接触基础知识:BVH 接触检测

1小时前浏览1

之前说到了很多空间索引结构,八叉树,bucket sorting,k-d tree:

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

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

接触基础知识:从四叉树八叉树到K-d tree

今天就来说说另一类空间索引结构,BVH (多层包围盒树,Bounding Volume Hierarchies),这种算法与四叉树与八叉树、K-D树最大的一个区别就是:之前讲到的树状结构,是以空间本身为主进行划分,而BVH则是由空间中的物体本身为主题,递归的构造树状结构。

当代的物理引擎,就很多采用BVH的方法进行构建空间中的索引数据结构,原因就在于上述说的主要不同,如下图所示:

得到BVH的方法如下:

  1. 循环每个物体,计算其包围盒
  2. 对临近的包围盒自进行归并,也就是取个并集,得到上一层的节点,比如橙色长方体+浅红色四棱锥形成上部灰色节点
  3. 再对多个灰色节点进行归并,得到黄色节点
  4. 最后在对黄色节点进行归并得到红色节点

至此所有的物体都已经划归到了这个BVH数据结构中,那么如果现在有个物体想检查与哪个物体是否碰撞,就从同步红色节点开始搜索就好了:

  1. 发现他只与左侧黄色节点重叠
  2. 发现他只与某个灰色节点重叠
  3. 最后找到真正的碰撞体

所以大家可以看到这个碰撞检测从上到下的流程跟之前的八叉树、K-D tree是十分类似的,复杂度也为

 

这就是一个简单(最天真版本的BVH)的生成与搜索的全过程。

除了上述说的自底向上的生成方式(可以看到,我们是最先循环树的最底层),另外还有一种自顶向下的生成方式,其实原理也非常好理解,最简单的思路就是分别对不同的轴进行二分,比如先沿着x轴取个中位数,然后对左边的物体和右边的物体分别构建bounding box, 然后在对下一层再构建bounding box,直到停止条件为止 (一般就是只有单一物体)。

那么说完了比较简单的BVH的生成与搜索方法(BVH的优化的花样老多了,我也并不全知道),我们就来讨论一下,这玩意为啥在物理引擎中用的很多,相比于传统的八叉树、K-D tree等,其最大的优势就在于它的 ”以物体为主体,而不是以空间为主题“。以下是物理引擎中常见的场景 


从中可以看到,在这种场景下,大多数物体是不动的,而那几个人,人的手,手里的刀则是无时无刻不在动的,这个时候呢为了快速进行碰撞检测,首先不可能不用任何空间索引而直接进行无脑全部搜索,而当用了空间索引,由于人时时在动,完全没有任何可预测性,就要动态的更新空间索引结构,那么BVH的优势就显示出来了,由于其是针对”物体的“,我们可以动态的更新物体的包围盒子,这个开销很小,然后再往上层更新上层节点的包围盒子,必要的时候再进行树旋转、合并和分裂等操作,所以这是一条”局部更新“的路线,大多数不动的东西根本不需要更新。而于此相比对于八叉树,k-d tree 等由于被划分的是”空间“,或者更严格来说是”有一些物体的空间“,物体 位置形状的改变,会造成整个树状索引发生改变(当然也有些优化方法),但总的来说在上述这种动态的场景中开销也是会大很多。

那么回到有限元,为啥有限元接触检测中BVH的高度没有物理、游戏引擎中这么高呢,原因在于大多数有限元接触分析研究的是变形体与变形体的接触,且两个物体每个单元每个节点都在变形,所以在大变形、大滑移的情况下,可能无论如何我们都得进行全局的空间索引重新构建,这个时候哪个构建的最快(相比BVH是局部更新快,全局都更新就未必快),可能才是最优的选择。



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

有限元基础知识:KNN与径向基函数插值

有一个和接触分析非常类似的领域,那就是不同物理场、不同结果数据之间的映射插值,如果需要互相映射插值的两者是有网格数据的,那么插值的过程与接触计算基本上没啥差异,还更简单一些,但如果其中一套数据是基于节点的,而没有网格单元信息,则需要用到KNN (K近邻点, k nearest nodes)与径向基函数 (RBF, radial basis functions)进行映射插值。很多做神经网络的朋友们可能都听过KNN,但这个的KNN更加的清晰明确,就是找离一个节点最近的k个对应的节点,做多物理场仿真(尤其是弱耦合)的朋友们应该都知道有款软件叫preCICE或者mpCCI,本质上他们就是提供了基于网格单元的映射插值与基于KNN与RBF的节点插值。 这个被映射插值的物理量可能是各种各样的,可能是压力、应力、温度等等,用统一的方法(略有不同的实现方式,主要考虑是否要考虑守恒的物理量等因素)进行插值。而在这个过程中使用KNN就听起来很简单,最简单的思路就是全局遍历,比如从一个点云(每个点上都对应一些物理量),向另一片点云(或网格)进行搜索,那么这个搜索复杂度可是老高了。那么想着降低搜索的复杂度,其实也很简单,这就用上了我们之前讲的空间划分算法,尽可能的在更小的范围内进行搜索: 有限元基础知识:空间箱排序(bucket sorting)接触基础知识:从二分到四叉树与八叉树接触基础知识:从四叉树八叉树到K-d tree以下就是一段非常简单的python代码,快速的给出knn的检测:from sklearn.neighbors import KDTreetree = KDTree(all_nodes)distances, indices = tree.query(query_point, k=3) 然后在找到这k个近邻的节点之后,我们就可以通过RBF方式进行插值,可能有朋友会问,这里为啥我们不用拉格朗日插值、线性插值等等,这个原因也很好理解,因为你找到的k近邻点不一定在空间中分布是啥样的,他们中间也没有网格连着。那RBF其实很好理解,可以理解为就是以这个网格节点为中心,然后沿着径向逐渐减小,最后随着离中心越远,逐渐缩减到0,也就是在插值中不再进行贡献,具体如下图所示: 然后诸如对一个物理量的插值就可以写成 其中 为第k个节点的径向基函数。那么通过上述的方式就可以实现两个区域基于节点数据的映射插值。大概长这样: 可能有熟悉神经网络的朋友们会觉得这玩意不是有限元吗,这里的概念咋跟神经网络里面那么多概念是重合的,其实答案就是:数学原理都差不多,学好数学吧!来源:大狗子说数值模拟

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