光线求交加速算法:边界体积层次结构(Bounding Volume Hierarchies)1-BVH引入

光线求交加速算法:边界体积层次结构(Bounding Volume Hierarchies)1

 

BVH引入

光线和物体求交的加速算法中,最常见的是物体(图元)细分和空间细分。边界体积层次结构(BVH)是一种基于图元(primitives)细分的光线相交加速方法,其中,图元被划分为不相交集的层次结构。 (相反,空间细分通常将空间划分为不相交集的层次结构。)下图显示了一个简单场景的边界框层次结构:

其中,图元存储在叶中,每个节点存储其所有子节点图元的边界框。 因此,当光线穿过树时,只要它不与节点的边界相交,就可以跳过该节点下方的子树。

对于一个二叉树形式的BVH,其在叶子结点中存储一个单一的图元,该树节点总数为2n-1,其中n是图元数。将有n个叶节点和n-1个内部节点。如果叶子存储多个图元,则需要更少的节点。BVH比kd树更快速地构建,kd树通常比BVH提供更快的射线相交测试,但是建造时间要长得多。另一方面,与kd树相比,BVH通常在数值上更健壮,并且由于舍入误差而导致的交集遗漏更少。

 

构建BVH

BVH的构建分为三个阶段。

  1. 计算有关每个图元的边界信息并将其存储在数组中,该数组将在树构建期间使用。
  2. 使用相应的分割算法来构建树,本文主要介绍四种分区算法(SAH, HLBVH, Middle, EqualCounts) ,构建的结果是一棵二叉树,其中每个内部节点持有指向其子节点的指针,每个叶子节点持有对一个或多个基元的引用。
  3. 最后该树被转换为更紧凑(因此效率更高)的无指针表示,以在渲染期间使用。

我们根据这三个阶段写出BVH构建代码:

// BVH构造函数,构造BVH2叉树
BVHAccel::BVHAccel(std::vector<std::shared_ptr<Primitive>> p,
                   int maxPrimsInNode, SplitMethod splitMethod)
    : maxPrimsInNode(std::min(255, maxPrimsInNode)),
      splitMethod(splitMethod),
      primitives(std::move(p)) {

    // 利用图元构造BVH2叉树,从以下三个阶段进行。(+)表示代码未展开 (=)表示展开后
    // 1.+初始化图元信息数组 
    // 2.+利用图元信息并使用相应的图元分割算法构建BVH
    // 3.+树的节点用深度优先排列表示
}

对于要存储在BVH中的每个图元,我们将其边界框的质心,其完整边界框及其索引存储在BVHPrimitiveInfo结构体的图元数组中。该结构体如下:

struct BVHPrimitiveInfo {
    BVHPrimitiveInfo(size_t primitiveNumber, const Bounds3f &bounds)
        : primitiveNumber(primitiveNumber), bounds(bounds),
          centroid(.5f * bounds.pMin + .5f * bounds.pMax) { }
    size_t primitiveNumber;//图元索引
    Bounds3f bounds;//图元边界框
    Point3f centroid;//图元边界框的质心
};

我们初始化图元信息数组的代码则为:

//1.=初始化图元信息数组
std::vector<BVHPrimitiveInfo> primitiveInfo(primitives.size());
for (size_t i = 0; i < primitives.size(); ++i)
    primitiveInfo[i] = { i, primitives[i]->WorldBound() };

接下来使用相应的图元分区算法以及图元信息构建BVH树,我们贴出代码:

// 2.=使用图元信息与相应的图元分割算法构建BVH树
    MemoryArena arena(1024 * 1024);
    int totalNodes = 0;
    std::vector<std::shared_ptr<Primitive>> orderedPrims;
    orderedPrims.reserve(primitives.size());
    BVHBuildNode *root;//构建完成树的根节点
    if (splitMethod == SplitMethod::HLBVH)
        root = HLBVHBuild(arena, primitiveInfo, &totalNodes, orderedPrims);
    else
        root = recursiveBuild(arena, primitiveInfo, 0, primitives.size(),
                              &totalNodes, orderedPrims);
    primitives.swap(orderedPrims);//orderedPrims是构造后排序过的图元,我们用其覆盖原图元

其中,我们把图元分区算法用枚举类型表示:

//SAH:表面积启发式法。HLBVH:线性边界体积层次结构法。Middle:中点分割法。EqualCounts:等数量分割法
enum class SplitMethod { SAH, HLBVH, Middle, EqualCounts };

HLBVH方法我们利用函数HLBVHBuild()构建,其他三个方法在函数recursiveBuild()中区分构建。MemoryArena为PBRT中内存管理类,用以优化内存分配。

每个BVHBuildNode代表BVH的一个节点。 所有节点都存储一个Bounds3f,它表示该节点下所有子级的边界框。 每

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值