【Heskey带你玩渲染】pbrt-v4中的wavefront path tracing

Wavefront Path Tracing

首先,老规矩:

未经允许禁止转载(防止某些人乱转,转着转着就到蛮牛之类的地方去了)

B站:Heskey0


注:本文需要CUDA和PBRT的知识,推荐书籍《CUDA C Programming》

pbrt第四版的书还没出,很多哥哥姐姐萌 (*^▽^*) 看代码的时候会很懵逼。因为中文网上还没有相关的博客,为了帮助大家理解,我写下这篇博客,帮助大家了解pbrt第四版的一个核心 —— wavefront

1. Introduction

我们可以通过CUDA,OpenCL这些接口实现在GPU上编程。在GPU上面编程和CPU有很大的不同,熟悉CUDA的都知道,在一个SM上同时可以并行32个线程

即使这样,GPU编程也会由很多难题。在这个线程束中:

  1. 有一部分线程中途挂掉了(if-else)
  2. 高带宽的CPU没有办法利用起来,GPU和CPU之间的内存拷贝会变得昂贵
  3. GPU的缓存贼小,kernel太大的话,很可能会冲爆缓存

为了尽可能避免这些难题,学术界的大佬萌就想出了一个办法 -> wavefront path tracing,这个方法被集成到了pbrt-v4中。这个方法的核心就是——把以往写得很大的kernel拆分成很多的小kernel。

2. 先分析一下老方法

老方法的kernel体量很大:

  1. 生成路径
  2. 对光源采样
  3. 不同材质的解决方案不同

在GPU的编程中,分支会导致线程资源不能够被充分应用。老方法中分支主要会出现在两个地方:

  1. 在采样path的时候,path随时会中断。在一个thread终止之后,线程束不会终止,也就是说,一个path中断之后其它的path还会跑,中断的path依然会占用线程资源

  2. 一个线程束中的path命中不同材质的时候,会导致线程束中每个thread的逻辑不同。

3. Wavefront

3.1 使用路径池

wavefront path tracing方法中,维护了一个path池,这个池的大小为 \(1M(=2^{20})\) 个path,当path中断的时候,需要重新生成path,这个时候就可以去池子里面取path的状态(path state)被存储到global memory(DRAM)中,每个path包括shadow ray和extension ray,占用212 bytes。1M个path,每个path占用212 bytes,所以总共就占用了212 MB的内存。(虽然把path池的内存提上去能够提升性能,但path池的内存太大的时候,性能的提升就不明显了,所以1M个path已经够用了)

3.2 拆分kernel

把kernel拆分到3个stages:

  • logic stage
  • material stage
  • ray cast stage

3.2.1 Logic stage

logic stage只有一个kernel,这个kernel的任务就是推动path的前进:

  • 计算light sample path和extension path的MIS权重
  • 更新throughput
  • 确定path是否终止了(Russian roulette“杀死”了ray,ray射出场景)
  • 确定ray击中了什么材质
  • 发送一条到material stage的请求

3.2.2 Material stage

每一堆消耗差不多的材质对应一个material kernel。比如,消耗大的就放在一个kernel,消耗小的放到另一个kernel。老方法中,所有的材质代码都在一个kernel里面,击中了不同材质,就用一个switch-case做分支。

3.3.3 Ray cast stage

ray cast kernel的任务就是投射出extension ray和shadow ray。与此同时,为了加载logic stage的输出结果,path state需要记录ray buffer中的索引

3.3 内存

wavefront formulation最大的缺点就是 —— path state 需要存储到内存中。那么就需要对memory layout做一些调整,使这个缺点变“弱”。对于GPU来说,使用SOA形式的memory layout会更加高效(使用SOA比AOS快了80%)。

3.4 Queue

通过为材质和光线投射阶段生成紧凑的请求Queue,确保每个启动的kernel总是能执行线程束中的所有线程。 这里的Queue是简单的预先分配的全局内存缓冲区,因此它们可以包含池中每个路径的索引。 每个队列在全局内存中都有一个item计数器,该计数器在写入队列时自动增加。 通过将项目计数器设置为零来清除队列。

posted @ 2022-03-06 21:30  Heskey0  阅读(899)  评论(0编辑  收藏  举报

载入天数...载入时分秒...