PBRT (Physically based rendering toolkit)是一个基于光线追踪的物理渲染系统。该应用程序主要包括了一个核心代码core,和相关的组件component。组件通过定义抽象类的方式定义了该组件的接口和需要完成的功能。而核心代码用来协调这些组件,从而完成一个完整的渲染过程。关于这个渲染引擎的API介绍和系统介绍可以参考Matt Pharr和Greg Humphreys的书:Physically Based Rendering: From Theory to Implementation (Second Edition)。全书1167页,内容丰富讲解细致。
首先我们了解下PBRT系统中的13个抽象基类。正是这13个抽象基类的派生对象共同组合,形成了渲染的整个流程。如果需要扩展PBRT形成自己的算法,来渲染图片,也是通过继承这13个抽象基类完成自己的算法,从而在core/api.h和core/api.cpp中写入自己的接口,在scene文件中指明相关的参数,从而生成对应的图片。这13个抽象基类为(原书第16页):
基类名 | 目录位置 | 相关章节 |
Shape | shapes/ | 3.1 |
Aggregate | accelerators/ | 4.2 |
Camera | cameras/ | 6.1 |
Sampler | samplers/ | 7.2 |
Filter | filters/ | 7.7 |
Film | film/ | 7.8 |
Material | materials/ | 9.2 |
Texture | textures/ | 10.3 |
VolumRegion | volums/ | 11.3 |
Light | lights/ | 12.1 |
Renderer | renderers/ | 1.3.3 |
SurfaceIntegrator | integrators/ | Ch.15 intro |
VolumIntegrator | integrators/ | 16.2 |
目录位置指的是这些抽象基类定义的位置,你可以在相应目录下找到.h和.cpp文件,相关章节是书中对这些抽象积累内容详细讲解的章节。PBRT系统的核心代码位于目录src/core下,main()函数位于src/main/pbrt.cpp。而对于这13个抽象积累的实现实例位于相应的目录位置,例如src/shapes目录下包含了对Shape这个抽象积累的若干实现:Cone/Cylinder /Disk /Heightfield /Hyperboloid /LoopSubdiv /NURBS/Paraboloid / Sphere /Triangle /TrangleMesh等。
PBRT程序的执行主要包括两个环节:第一个环节是解析用户提供的场景描述文件(.pbrt文件),第二个环节是整个渲染过程。场景描述文件以后缀.pbrt结尾,主要描述了组成场景的几何信息,材料特性,所用的光源类型,以及每个环节所调用的算法。场景文件的获取有两种方式,一种是通过文本编辑器直接手动编辑场景文件或者对已有场景文件进行修改;第二种是从建模软件导出.pbrt的场景文件,PBRT官网上提供了从其他软件中导出.pbrt场景文件的脚本,可应用于3dsMax, Blender, Mathematica 和Structure Synth等。场景文件解析后,会初始化相应的类,接着进入渲染过程。渲染过程中,类之间的关系如下图所示:
在这个渲染的过程中,Render()类首先从场景文件中读入关于geometry,lights,cameras的数据。从Sampler类创建一个sample对象并将其传递给Camera类。Camera类根据sample信息获得相应的从film平面的ray信息,并将这个ray信息传回给Render。之后,ray数据传递给Integrators类,将使用SurfaceIntegrator和VolumeIntegrator来计算每一个ray所带的radiance。最后,sample和radiance信息都传递给Film类,Film完成数据的存储和图片的生成。
明白了这个过程,我们通过对代码的调试来熟悉整个PBRT系统的流程。
打开pbrt.sln解决方案,在项目文件pbrt上右键,选择Properties,在打开的pbrt Property Pages中选择Configuration Properties->Debugging,在右侧的Command Arguments中,填入
..\..\scenes\killeroo-simple --ncores 1
在Sampler::Render()和SamplerRendererTask::Run()设立断点。跟踪程序,即可了解PBRT程序的流程。因为PBRT考虑了并行计算,--ncores1选择了单线程运行方式,有利于我们对程序的理解。