百科问答小站 logo
百科问答小站 font logo



如何阅读PBRT3? 第1页

  

user avatar   infancy1996 网友的相关建议: 
      

我以前因为对真实感渲染的好奇去读 PBRT,书里依次介绍了 Shape、Camera、Sampler、BSDF、Light、Integrator……我一直看不明白,费了很大力气去读,才发觉这整本书写的都是蒙特卡洛光线追踪 Monte Carlo RayTracing,这和 smalllpt 本质上是一样的。去年偶然想到可以把 smallpt 一步步的改写成 pbrt,在这个过程中介绍蒙特卡洛光线追踪和 pbrt 的架构,用了几个月(基本)完成了:

可以把它当作是阅读 pbrt 的前菜,先熟悉相同结构下这个 smallpbrt 的工作流程,再去看 pbrt 应该会清晰一点。

文章用了别的标记语言来写,只能用 html 格式导出,这里贴一些片段:

smallpt[1] 是一个仅有 99 行的迷你光线追踪渲染器,在极小的体积下实现了图元、材质、光源、场景、相机、采样器、积分器等模块,可以说麻雀虽小五脏俱全。关于 smallpt 是如何实现这些功能的,网络上已经有些很好的介绍[2][3][4]。

pbrt[5] 既是一个多功能光线追踪渲染器的名字,也是对应书籍的简称[6],书中以文学编程 Literate Programming 的方式,介绍了这个读者如果能看懂就看得懂,看不懂就看不懂的渲染器。pbrt 具有一个相当优秀的架构,给很多渲染器带来了启发[7][8][9]。下文中提到的 pbrt 指渲染器,PBRT 指书籍。

smallpt 和 pbrt 虽然体量和功能差异巨大,但在笔者眼里却有着一样的核心功能:蒙特卡洛光线追踪(Monte Carlo Ray Tracing),这就像王垠说的:

很多人都不知道,有一天我用不到一百行 Scheme 代码就写出了一个「深度学习框架」,它其实是一个小的编程语言。虽然没有性能可言,没有 GPU 加速,功能也不完善,但它抓住了 PyTorch 等大型框架的本质——用这个语言写出来的函数能自动求导。这种洞察力才是最关键的东西,只要抓住了关键,细节都可以在需要的时候琢磨出来。几十行代码反复琢磨,往往能帮助你看透上百万行的项目里隐藏的秘密。
如何阅读别人的代码 yinwang.org/blog-cn/202

通过阅读一个小规模的软件,来了解这一类型软件的架构模式是很常见的做法[10]。但 smallpt 为了控制体积实在太简略了一点,导致它的结构并没有那么清晰,想从 smallpt 里看出 pbrt 的架构并不容易,而且它那 99 行的代码里还涉及了大量的理论技术。

本文将基于 pbrt 的架构一步步改写 smallpt,通过每一步的实际代码去解释蒙特卡洛光线追踪和 pbrt 的大致架构,方便对光线追踪、全局光照感兴趣的同学了解学习。

……

改写 smallpt

这一步会把 smallpt 改写成 pbrt,在改写之前先来介绍下 pbrt。

pbrt 的主要功能是根据输入的描述文件创建出场景,并根据相机的参数渲染出对应图像,它的整个工作过程如下:

  • SamplerIntegraotr:通过(蒙特卡洛方法)采样,来求解渲染方程(一个积分方程的)的积分器,也可以把它当做通常的渲染器 Renderer 看待。
  • SamplerIntegraotr::Render(): SamplerIntegraotr 的基类是抽象类 Integrator,它只有 Integraotr::Render() 这一个接口,pbrt 在创建完场景和积分器后,就会调用积分器开始渲染场景。每种积分器实现会用自己的方式去渲染场景。
  • 对于 SamplerIntegraotr::Render() 来说,它也会遍历图像平面 Film,通过采样器 Sampler 在图像的每个像素上生成相机要用的采样点 CameraSample ,然后传递给相机 Camera 生成光线 Ray ,寻找光线到场景 Scene 中的最近交点 Intersection ,在交点上计算半球积分,返回沿光线方向辐射出的亮度……与 smallpt 的主函数 main() 如出一辙。
  • SamplerIntegraotr::Li(): SamplerIntegraotr 的核心方法是 Li(ray, scene, sampler, depth),对应 smallpt 中的 Radiance(ray, sampler, dpeth) (smallpt 的场景是全局的,不用传进来)。在 Li(ray, scene, sampler, depth) 中会同时采样 BRDF 和光源,关于要如何采样光源,会放到额外拓展的部分介绍。

这就是 pbrt 的整体架构,也是 pbrt 的核心功能:求解渲染方程。(smallpt:我也是)

是不是对上了。

对于上面描述的这些概念,pbrt 都有对应的抽象类(接口类)来实现:

并且通过这组抽象类之间的交互(上面的箭头)来完成功能。每个抽象类都会有多种实现,比如对于材质 Material,就可以有哑光材质 MatteMaterial(通过完全漫反射 BRDF LambertenReflection 实现);镜面材质 MirrorMaterial(通过完全镜面反射 BRDF SpecularReflection 实现)。这些实现类基本放在 core/ 之外的其它文件夹下。

除此之外 pbrt 还实现了大量别的功能:并行/内存分配/统计/调试/场景解析等一系列功能,于是它的代码行数就来到了几万行(没统计过,大概是这个量级)。

99 行 vs 几万行,这两个数量级要怎么跨过呢?

给 smallpt 加功能是不可能的,假如只保留 pbrt 的核心功能,去掉那些实现类和其它功能呢?

  • 不需要那么多种几何形状,球体 Sphere 就够了:去掉 shape/ 目录
  • 不需要那么多种相机,透视相机 PerspectiveCamera 就够了:去掉 camera/ 目录
  • 不需要那么多种采样器,最基本的随机采样器 RandomSampler 就够了:去掉 sampler/ 目录
  • ……

至于场景中的空间加速结构 Aggregate,图像平面的滤波器 Filter,提供材质细节的纹理 Texture,实现体积渲染的介质 Medium 等,连抽象类都可以去掉。

到这里就只剩下了 core/ 这个目录,把它的代码整合一下,再去掉并行等一系列没了也能跑的功能,给每个抽象类(Shape,Sampler,Camera,BSDF/BxDF,Light,Integrator……)加上最简单的实现,我们就得到了一千行左右的 smallpt_rewrite.cpp。(也可以管它叫 smallpbrt.cpp )

如果在这个基础上再做精简的话:

  • 类结构也不要了:smallpt_comment.cpp (因此在 samllpt 里看不到 Sampler,Camera 这些类,但功能还在 )
  • 代码级别的结构和变量命名也不要了:smallpt_format.cpp (这个 Radiance(...) 的实现都是 tirck 吧)
  • 再压缩一下行数:smallpt.cpp
  • 把带有菲涅尔反射的材质也去掉:nanopt.cpp
  • 收手吧

在前几节,我们已经从 smallpt.cpp 反向实现了 smallpt_format.cpp 和 smallpt_comment.cpp ,接下来将把 300 行的 smallpt_comment.cpp 结构化为 1300 行的 smallpt_rewrite.cpp / smallpbrt.cpp ,这就有了本文的标题。

smallpt_rewrite.cpp 的提交记录比较干净,感兴趣的同学可以直接看每次提交都结构化了什么功能。

……

结语

回想起以前刚学渲染的时候,既看不懂公式,也看不懂代码,只能看着那些漂亮的渲染图发愣,那种被挡在外面的感觉真是太痛苦了,因此想尽自己的一点努力给感兴趣的同学铺点基础,虽然还有很多细节没有提到,但笔者认为本文已经把蒙特卡洛光线追踪的基础框架搭起来了,剩下的知识都可以逐个去了解,希望这可以在图形渲染的路上助大家一臂之力。

祝收敛!




  

相关话题

  什么是「实时渲染」技术? 
  在游戏中光线追踪全局光照与光线追踪环境光遮蔽这两种技术为什么会同时存在? 
  能否通过渲染矢量画面,从根本上解决游戏锯齿问题? 
  如何看待 2021 年图灵奖授予美国计算机科学家 Jack J. Dongarra? 
  如何看待程序员的三大浪漫被认为是操作系统、编译原理和图形学? 
  USC 大学教授、Pinscreen CEO Hao Li 是否存在论文造假、产品虚假宣传等问题? 
  PS5为什么会实现不了RDNA2的全部特性? 
  系统的学习计算机图形学,有哪些不同阶段的书籍的推荐? 
  如何看待 Google Docs 将从 HTML 迁移到基于 Canvas 渲染? 
  为什么时至今日仍有人质疑手机的图形性能? 

前一个讨论
你最喜欢游戏《艾尔登法环》里的哪个角色?
下一个讨论
最大似然估计法是如何实现的?





© 2024-09-19 - tinynew.org. All Rights Reserved.
© 2024-09-19 - tinynew.org. 保留所有权利