层次包围体(Bounding volume hierarchies, BVH)是一种基于图元(Primitive,构成场景的基本元素,如三角形、球面等)划分的空间索引结构。说它是基于图元的,是因为它是将场景中的图元划分成不相交集的层次结构(Hierarchy of disjoint sets);与之相对的基于空间(Spatial)划分的空间索引结构(例如下一节里介绍的KdTree)则是将空间划分成不相交集的层次结构。

上图展示了一个简单场景的BVH,图元被存储在叶节点上,每一个节点都存储了一个包围了其所有子节点内图元的包围盒。

基于图元划分的空间索引结构的有几个重要性质:

  • 每一个图元在整个结构中只会出现一次。但是同一块空间区域可能会被多个节点所包含。
  • 基于图元划分的空间索引结构消耗内存的上限是已知的。若每一个节点仅包含两个子节点,叶节点只存储一个图元,所有非叶节点的度都为
    ,则总结点的个数为
    个叶节点,
    个非叶节点,二叉树的性质)。

BVH相比于KdTree构建效率更高,但是遍历效率略低。另外,BVH相较于KdTree拥有更好的数值鲁棒性,更不容易因为浮点数舍入误差而导致弃真(实际相交,但是计算结果为不相交)情况的出现。

BVH的构建

整个构建过程可以大致分为三步(第二步和第三步可以直接合并,不过分成三步实现更加便于学习和理解):

  • 计算场景中每一个图元的AABB包围盒、质心(一般取包围盒的中心)并存储到数组中。
  • 根据不同的划分策略构建树状索引结构。
  • 将得到二叉树转化更加紧凑的表示形式(无指针,内存连续)。

PBRT中第二步得到的树中,非叶节点持有指向其子节点的指针,叶节点持有指向其所包含图元数组第一个元素的指针和所包含图元的个数。

整个构建的过程是一个递归(可以通过栈转化为循环)的过程。每一次递归接受一个左闭右开的区间

,处理数组中第
个到第
个图元。若区间中的图元数小于一个给定的值(叶节点最大图元数目),则递归终止并返回一个叶节点。否则我们就需要考虑将区间内的图元按照一定的策略划分为
两部分(划分的过程可以使用std::partition函数对原数组进行更新),并递归地对划分好的
进行构造并返回子节点的指针。

构建过程中最重要的问题就是如何对图元进行划分。对于非叶节点,假设其只有两个子节点,若其包含了

个图元,则总共有
中划分的可能(
个图元,每一个图元可以被分到左边或者右边,则总共有
种情况,然后减去全部被分到一边的情况即可得到
)。这显然太多了,因此一般我们都是沿着某一个坐标轴进行划分,这样一来所有的情况只有
(书上是这么写的,不知道怎么算出来的。根据书中的插图我感觉应该是:将
个图元沿着坐标轴依次排开,并将图元简化为质心,则一共有
个间隙,则总共有
种划分的可能)。划分时要尽可能减少划分后两部分包围盒重叠的体积,因为重叠的体积越大,Ray穿过重叠区域的可能性越大,遍历两个子树的可能性就越高,计算消耗越多。

因为我们最终的目的是要减少划分后左右子节点重叠的体积,因此一般在图元跨度最大的坐标轴上进行划分。这里,图元的跨度可以用图元的包围盒来衡量也可以用图元的质心来衡量。

两种最简单的划分方法是:

  • 取坐标轴跨度的中点

    ,若节点的坐标小于
    则将其划分到左节点,否则将其划分到右键点(中点划分)。
  • 最左边的
    个被划分到左节点,最右边的
    个被划分到左节点(等量划分)。

当图元分布不均匀的时候,上面两种方法得到的划分结果会变得很差。

左:中点划分或等量划分;右:比较理想的划分

一种更为常用且效果更好的方法是基于表面积的启发式评估划分方法(Surface Area Heuristic,SAH),这种方法通过对求交代价和遍历代价进行评估,给出了每一种划分的代价(Cost),而我们的目的便是去寻找代价最小的划分。

假设当前节点的包围体中存在

个物体,设对每一个物体求交的代价为
,如果不做划分依次对其求交则总的代价为:
如果这些物体划分为2组,这两组物体分别处于它们的包围盒A和B中。设光线击中它们的概率分别为
,需要注意包围盒
之间存在重叠,且它们并不一定会填满其父节点的包围体,因此
的和不一定为1,且它们的和越大说明
的重叠程度越大。综上所述,当前节点求交的代价可以写为:
其中
表示遍历树状结构的代价。一般来说,我们假设对所有图元的求交代价是相同的,可设
,又遍历的代价小于求交的代价,可设
。设包围盒A中图元的个数为
,B中图元的个数为
,则:
光线击中包围盒的概率可以根据包围体的表面积来估计,即在父节点的包围体C中,A和B的表面积越大它们被击中的概率也就越大,设
的表面积为
,则有:

SAH考虑到了图元在空间中的分布也考虑到了子节点包围体的重叠程度,在实际应用中拥有很好的效果。

在实现的时候,相比于计算可能划分的代价然后寻找代价最小的划分,一种更好的办法是将节点

所包围的空间沿着跨度最长的那个坐标轴的方向将空间均等的划分为若干个桶(Buckets),划分只会出现在桶与桶之间的位置上。如图所示,若桶的个数为
则只会有
种划分的可能。
图中节点沿着坐标轴的方向被划分为了6个桶

设图元质心在划分坐标轴上位置坐标的分量为

,则该图元所处桶的索引号为:

遍历

中所有的图元,统计每一个桶中图元的个数以及每一个桶的包围盒。注意,桶与桶之间存在重叠。得到这些信息后,就能计算每一种划分的代价:

需要注意的是,若当前所有图元质心的位置都相同,则需要进行特殊处理,一般有两种方法:

  • 直接建立一个叶节点,该叶节点直接包含这些质心位置相同的图元。
  • 按照数量均等的分给两个子节点。

既然这些图元都靠在一起了,那么无论你怎么分都没用,反正都要挨个求次交,所以第一种方法更好一些。

c++ 结构体遍历_PBRT-E4.3-层次包围体(BVH)(一)相关推荐

  1. 通过css类/选择器选取元素 文档结构和遍历 元素树的文档

    通过css类选取元素 html所有的元素拥有class属性,该属性会对元素进行分组,标识为某一组. js中使用className属性来保存HTML的class的属性值 var NodeList = d ...

  2. 二叉树的链式结构递归遍历实现

    二叉树的链式结构递归遍历实现 1.     BinTree.h文件 #ifndef__BINTREE_H__ #define__BINTREE_H__ #include<stdio.h> ...

  3. 多智能体强化学习_基于多智能体强化学习主宰星际争霸游戏

    大家好,今天我们来介绍基于多智能体强化学习主宰星际争霸游戏这篇论文 Grandmaster level in StarCraft II using multi-agent reinforcement ...

  4. 多智能体系统——竞争网络下异构多智能体系统的分组一致性问题 Group consensus of heterogeneous multi-agent system (附论文链接+源码Matlab)

    多智能体系统--竞争网络下异构多智能体系统的分组一致性问题 (附论文链接+源码Matlab) Yu F, Ji L, Yang S. Group consensus for a class of he ...

  5. Unity自动化碰撞体生成方法-Normal Collider 和 碰撞体代理

    如何在Unity中对角色快速创建碰撞体(Normal Collider)和碰撞体代理(Fake Collider) 前言 快速给Humanoid人形角色添加碰撞体,使用Unity 的Collider系 ...

  6. 引体向上 - 宽握/窄握引体向上动作图解

    引体向上 - 宽握/窄握引体向上动作图解 引体向上(Chin-up)是锻炼背部的王牌动作,主要锻炼背阔肌和大圆肌.由于可以在握距.握法.拉起位置有所变化,引体向上种类形式多样. 目标锻炼部位:背阔肌. ...

  7. 什么是共用体?如何定义和使用共用体?

    在C语言中,共用体(Union)是一种特殊的数据类型,允许在相同的内存位置存储不同类型的数据.共用体的成员共享同一块内存空间,因此在任意时刻只能存储其中一个成员的值.共用体的大小由其中最大的成员决定. ...

  8. 有笔记本就能玩的体感游戏!TensorFlow.js实现体感格斗教程

    晓查 编译整理 量子位 出品 | 公众号 QbitAI 小时候的你在游戏中搓着手柄,在现实中是否也会模仿这<拳皇>的动作?用身体控制游戏角色的体感游戏很早就已出现,但需要体感手柄(Wii) ...

  9. 包围体之AABB、球体的定义+相交测试

    包围体是啥? 我们直观觉得碰撞往往是两个多边形相交,但是在计算机中这么算是很复杂的,特别是边很多的物体时 为了减少计算,就需要把几何体用一个简单的体空间包围起来(简单体是指:长方体.球体) PS:包围 ...

最新文章

  1. Codeforces Round #417:E. FountainsSagheer and Apple Tree(树上博弈)
  2. html 文档自动获取css,前端基础中css选择器,html文档 ,与javascrip中基本的获取
  3. Fragment-FragmentMannager中的方法
  4. python基础代码-python基础,python基础代码大全
  5. oppo如何更新计算机,OPPO R17Pro手机怎么升级和降级系统?
  6. Ajax的异步,是鸡肋还是鸡排?
  7. 徒手实现Spring的IOC
  8. 【Win10】UAP/UWP/通用 开发之 x:Bind
  9. Java中的枚举类型学习
  10. 队列,链队列,链式存储的队列
  11. 人脸识别接口_双目模组摄像头人脸识别技术中活体检测
  12. oracle10g--使用expdp导出数据和impdp导入数据
  13. 计算机内存改成多少合适,32g内存需要设置虚拟内存吗?32g内存虚拟内存设置多少合适...
  14. Apache Doris 在京东广告报表查询场景下的应用
  15. python销毁线程_python线程销毁
  16. css3(属性选择器,结构伪类选择器,伪元素选择器 ,css3盒子模型,滤镜filter, cale, 过渡transition))
  17. java相关资料下载
  18. linux下删除文件夹及下面所有文件
  19. PyTorch中的叶节点、中间节点、梯度计算等知识点总结
  20. 关于安卓保存网络图片并更新图库

热门文章

  1. [Catalan]求解随机出栈可能数(洛谷P1044题题解,Java语言描述)
  2. 【VB.NET】VB.NET文件问题的解答
  3. 17款优秀的Vue UI组件库汇总
  4. 继三星、华为外,苹果提交的专利显示它也要开发可折叠手机了...
  5. “变形金刚”为何强大:从模型到代码全面解析Google Tensor2Tensor系统
  6. C语言错误处理方法、C++异常处理方法(throw, try, catch)简介
  7. springMVC一些实践总结
  8. zorka源码解读之Beanshell与zorka的交互实现
  9. 【oracle案例】ORA-01102: cannot mount database in EXCLUSIVE mode .
  10. 各种网络模拟器的下载链接