←返回上页

Embree: A Kernel Framework for Efficient CPU Ray Tracing

Embree: A Kernel Framework for Efficient CPU Ray TracingEmbree 结构光线追踪核心单光线三角面求交单光线 BVH 求交光线包 BVH 遍历混合 BVH 遍历BVH 构建核心桶式表面积启发式 BVH 构建多线程 BVH 构建Embree 应用参考文献

Embree 结构

Embree 的结构:计算硬件 - Embree Common Infrastructure - Embree Kernel Layer - Embree API - 渲染器。

Embree 的关键组件为:

光线追踪核心

光线追踪核心(Traversal and Intersection Kernels)。

Embree 支持的 BVH 格式与基元类型、矢量化方法和指令集架构(ISA)很多,也可以只编译面剔除(face culling)、光线遮罩(ray masks)部分。

单光线三角面求交

单光线三角面求交(Single-Ray Triangle Intersection)。

对于不同处理器架构有不同的并行实现:

会通过 __AVX____AVX512F__ 启用不同的基类,有对应的实现。

单光线 BVH 求交

单光线 BVH 求交(Single-Ray BVH Traversal)。

对于四叉BVH(Quad-BVH)结构,支持向量宽度为 4 的处理器架构可以实现高效的遍历。Embree 没有对 AVX / AVX2 做映射到 8 宽 SIMD 的优化,只是改进了运算细节(融合加法和乘法)。

光线包 BVH 遍历

光线包 BVH 遍历(Packet Traversal and Intersection)。

Embree 使用经典光线包求交,而不是单程序多数据(SPMD)的方法。在每个 BVH 的节点上通过单指令多数据(SIMD)地分摊标量计算,Embree 在这一步对于不同 ISA 可以生成最佳指令组合。

混合 BVH 遍历

混合 BVH 遍历(Hybrid Traversal and Intersection)。

在混合相干(coherent)光线与非相干光线时,如果支持光线包和单光线的切换,可以加速求解。但针对光线包优化的 BVH 的内存存储顺序未必对于单光线是最佳的(反之亦然)。Embree 设定了阈值用于切换两种方式的使用。

BVH 构建核心

BVH 构建核心(BVH Construction Kernels)。

桶式表面积启发式 BVH 构建

桶式表面积启发式 BVH 构建(Binned SAH BVH Construction)。

Embree 构建了针对优化遍历成本的 BVH,使用表面积启发式(Surface Area Heuristic, SAH)和空间分割的对象分区的技术,在构建时对节点进行估价,产生最低预估成本的分区。

表面积启发式(Surface Area Heuristic, SAH)

假设:光线进入体积的概率与其表面积成正比。则对于划分 ,记两边三角形数量为 ,相关体积为 ,体积 的表面积为 ,可以量化预期遍历成本为:

其中 表示遍历子节点的开销, 表示求三角形交点的成本。

对于 KD 树来说,分区垂直于特定坐标轴,可以证明只有 个合理的分割位置。而对于 BVH ,分割会有 种方案,所以需要设计划分的方案,在 PBRT 的实现里,作者将分割维度的 span 分成 12 段,取期望开销最小的一种。

Embree 的 SAH BVH 具体实现在 /kernels/builders/bvh_builder_sah.h 中的 recurse 方法:

桶式 BVH 构建(Binned BVH Building)

对质心边界框宽度最大的轴进行划分,均匀分为 个相等的桶。根据三角面的重心分到对应组内,但三角形通常会超出桶的边界,这里对所有投影到的桶的数量 与其边界框 进行追踪,然后将 应用到对应桶的初始边界,然后进行合并边界。同时为了防止恶化,加入超参数用来调整该层构建。

上面 auto split = heuristic.find(brecord.prims, cfg.logBlockSize); 这里的启发式寻找使用了桶式 BVH 构建,Embree 实现如下:

对于 bin 的划分可以在 /kernels/builders/heuristic_ 开头的文件找到,这里不再赘述。

空间分割 BVH(Split Bounding Volume Hierarchy, SBVH)

将 KD 树和 BVH 更深度地结合,以引用的方式允许将一个物体分到两侧(将这个代价添加到成本函数中)。

多线程 BVH 构建

多线程 BVH 构建(Morton-Code BVH Construction)。

高维坐标映射(Morton Code)

对于高维整点坐标 ,写成二进制形式:,然后交错排列,得到 ,再转换成无符号整数,就得到了这一坐标全序的 Morton code 表示。Morton code 有很好的局部性,以下图的二维 Morton code 为例:

(a) 根据最高位划分、(b) 根据次高位划分、(c) 根据一、二位划分、(d) 根据一、三位划分。

实际计算 Morton code 时,我们将包含所有物体的全局包围盒在各个象限上先归一化再 等分,将每个物体的包围盒中心坐标转换到 上去,然后就可以得到 位的 Morton code,正好可以放进一个 uint32_t 里。

得到所有 Morton code 之后,我们对这些点进行一次基数排序,然后按照固定步长将所有物体划分为若干个组,就得到了彼此不交而组内点的距离也比较接近的很多个不同组,组内建 BVH 的操作在组间可以并行。

Embree 的 Morton code 存储在 64 位值中,具体的并行算法为:初始所有线程协作自同一节点划分三角形,当子节点的数量大于线程数量时,将节点划给线程,在三角形数少于 4 个时在此叶子节点处中止。

Embree 的 Morton 代码实现为:

线程方面代码实现为(singleThreadThreshold 被初始化为 (size_t)1024):

 

Embree 应用

Unreal Engine 5 中的 Lumen 可以实现全动态全局光照(Fully Dynamic Global Illumination),中间使用辐照度算法(Radiosity)来生成间接光(Indirect Lighting),理论上不是光线追踪而是光线投射(Ray Casting)。

传统的辐照度算法方法需要将场景划分为 Patch,而 Lumen 已经拥有了粗粒度的全局距离场(Global DF)以及粗粒度的 体素光照(Voxel Lighting),因此可以直接从表面缓存(Surface Cache)上射出光线,与全局距离场进行光线追踪求交,交点采样上一帧的体素光照后转换为二阶球谐,最后再根据 Normal 计算漫反射(Diffuse Transfer)球谐系数,计算出最终的间接辐照度(Indirect Radiance)。

这个间接辐照度也保存在辐照度缓存中,称为间接光,并与直接光合并计算得到最终的渲染光照,而下一帧的体素光照又来自于这一帧的辐照度缓存,因此后续所有的 Lumen 光照流程自然具有了间接光照。

表面缓存是算法的核心部分,Lumen 创造了 Card 的概念,用于定义面在表面缓存中存储的信息。

Lumen 会从多个角度捕获每个网格体的材质属性。这些捕获位置(这里叫做:卡(Cards))是针对每个网格体脱机生成的。

这里的"多个角度"就是指 6 个轴对齐方向(上、下、左、右、前、后),"捕获"指的是在 6 个轴对齐方向上以正投影方式光栅化 Mesh,获取 Mesh 对应的材质中的各个 Material Attributes(Albedo、Opacity、Normal、Emissive)后存储到表面缓存上。如果网格体具有复杂结构还会生成多个 Card。

在访问表面缓存时也需要通过 Card 将 Hit Point 进行物理地址转换到表面缓存图集空间中再执行采样。因此 Card 并非是单一的位置数据,其核心数据是一个局部轴对齐的边界框(Bounding Box)。

在初始化阶段还是需要光线追踪来进行处理表面缓存,也就是计算 Card,需要对体素化的 Mesh 按 Cube 轴对齐方向执行光线步进生成 Surfel,记录 Surfel 的信息,最后根据这些 Surfel 计算每方向 Card 的边界,具体流程(MeshCardRepresentationUtilities.cpp 下的 GenerateSurfelsForDirection 方法)为:

  1. 遍历当前轴对齐方向的 XY 平面(注意每个方向的 Basis 不同),沿着 Z 方向在每个 Cell 中发出 32 条射线进行光线求交,如果相交则增加一个面元采样点(Surfel Sample),记录交点、Normal、交点所在的 Cell Z 和前一个交点的 Cell Z 记为 MinRayZ,直到超出边界结束。
  2. 将面元采样点列表按照 Z 升序排序,如果 Z 相同则按 MinRayZ 降序排序、统计每个 Cell 的面元采样点数量以及相邻 Cell(Z 方向)之间的面元采样点数量差。
  3. 遍历每个 Cell 判断其对应的 Surfel 是否有效,如果有效则增加真正的 Surfel,记录 Cell 坐标以及 MinRayZ 的中位数。有效性判断是通过光线追踪判断是否在 Geometry 内部,不在内部则认为有效。

参考文献

  1. Embree: A Kernel Framework for Efficient CPU Ray Tracing: 提出 Embree 的论文。
  2. embree/embree: Embree ray tracing kernels repository:Embree 代码。
  3. Shallow bounding volume hierarchies for accelerated ray tracing: 验证 Quad-BVH 在 4 宽架构下的优化。
  4. On fast Construction of SAH-based Bounding Volume Hierarchies: 将 SAH 与 Bin-Build 应用到 BVH。
  5. Spatial Splits in Bounding Volume Hierarchies: SBVH,空间分割BVH。
  6. 渲染拾遗:BVH: 对 SAH 与 Morton Code 有形象地解释。
  7. Lumen: Real-time Global Illumination in Unreal Engine 5 - SIGGRAPH 2022:Lumen 原论文。
  8. Lumen Technical Details in Unreal Engine:Lumen 的技术细节。
  9. 游戏引擎随笔 0x30:UE5 Lumen 源码解析(二)Surface Cache 篇:详细描述 Card 的功能与实现。