百科问答小站 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 的提交记录比较干净,感兴趣的同学可以直接看每次提交都结构化了什么功能。

……

结语

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

祝收敛!




  

相关话题

  如何看待索尼为PS5注册光子映射技术专利? 
  游戏中有哪些看上去很简单,但实际上需要极高技术力或是极高成本的细节? 
  历届 SIGGRAPH 上有什么新奇、有趣的项目? 
  「贝塞尔曲线」有哪些作用和特点,该如何正确使用? 
  主流的游戏引擎都是如何解决Alpha Blending问题的呢? 
  计算机图形学有哪些著名的论文? 
  游戏中有哪些看上去很简单,但实际上需要极高技术力或是极高成本的细节? 
  Mac系统下有什么适合图形学的C++IDE?Clion还是VS? 
  程序员工作中占时间最长的是哪个步骤? 
  游戏中有哪些看上去很简单,但实际上需要极高技术力或是极高成本的细节? 

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





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