More On Ray Tracing

最近又看了看光线追踪..

Theory

上回提到, Path Tracing就是在试图解Kajiya86年提出的渲染方程:

$$I(x, x\prime) = g(x, x\prime)\left[\epsilon(x, x\prime) + \int_S \rho(x, x\prime, x\prime\prime)I(x\prime, x\prime\prime) dx\prime\prime\right]$$

Monte Carlo法来解它的话, 就是从每个像素都发一定数量的光线用于采样,按BRDF随机反射/折射, 最后所有样本取个平均.

有足够多的采样,Monte Carlo是能够收敛到真实解的, 但收敛的实在太慢, 我的程序里往往要每个像素三千多个样本才能保证输出图片有好质量. 这三千多里是包括了每个像素2x2的抗锯齿的,所以应该算是比局部光照模型慢了几百倍.

于是接下来大家的工作就是,怎样改进Sampling和Tracing的过程,使得程序可以快速收敛到一个较好, 甚至是无偏的结果上.

smallpt一个版本里就包含explicit lighting优化. Implicit lighting是说, 光线打到了光源上的时候, 再计算光源强度对这条光路的贡献; 而explicit lighting有点像局部模型那种方式, 就是光线打到一个表面时, 只要这一点到光源的连线没被挡住, 就按某种辐射模型给这一点增亮, 相当于强行采样了一条到光源的光路.

这种优化确实有极大提速, smallpt多了23行代码就能够做explicit lighting, 之后他说, 渲染下面这张图只需要 不到10s:

Explicit lighting的问题在于, 它处理不了caustic. 要渲染caustic必须要有"真正连接了光源"的光路. 在各种论文的光路图里, 一般把explicit lighting里这种强行连上的光路用虚线连, 称为"shadow ray". 由于它是直接通过是否被挡住来判断光路存在性的, 因此对于透明物体的caustic无能为力.

为了更好的结合implicit与explicit lighting, 改进tracing过程, 出现了 Bidirectional Path Tracing 方法. 它是从视点和光源都发出光线, 将两条光路上的点一对对进行shadow ray测试, 再强行连上一条explicit的光路(如下图), 再为每条连上的光路赋予一定权值, 以确保结果无偏. 相比较而言, explicit lighting相当于只与光源强行相连, 而bidir tracing把两条路上的点对相连, 增加了光路的重用次数, 也由于光源光路的存在, 使得caustic等效果真实了.

Metropolis Light Transport 是在sampling上做的改进, 它来自于Metropolis-Hastings 采样方法.

Metropolis Sampling方法解决的是这样一个问题: 我们有一个pdf叫做$f(x)$, 我们想采一堆符合这个分布的样本. 现在这个函数不知道怎么求值什么的, 更不能积分再求逆然后利用均匀分布做变换, 但是我们有一个正比于$f$的, 可求单点值的函数, 甚至仅仅是"统计意义可求值"的, 这时候我们如何采样$f$.

Path Tracing里, 想要的图像就是一个不可直接求值的函数, 但在每个像素处都是"统计意义可求值"的, 更进一步, 在每条特定的完整光路上的亮度, 是可直接求值的. Metropolis Sampling的方法就是, 在可求值的采样空间上构造一个Markov过程, 使他在采样空间上存在稳态, 且常返非周期(从而稳态唯一), 并且这个稳态分布hopefully就是我们的目标.

(这一部分数学细节还没看..

用在Path Tracing中, 就是将光路用于采样, 对其做扰动(mutation), 以一定转移概率换到另一个样本点. 这个扰动可以是各种形式的, 其作者提出的形式就包括添、删、改一个节点, 改变光线角度等, 对每种形式的扰动都要算出特定形式的转移概率.

Implementation

然后就纠结了..要有光路, 还要有扰动, 扰动转移概率要根据各种BRDF和几何关系来算, 复杂程度太大, 抽象不出 来..对于怎么实现, 没什么想法. 就找了一下现有的实现:

Mitsuba 是一个"research-oriented rendering system". 目测大部分都是Wenzel Jakob一个人写的..

代码有20W+行, 风格, 架构, 注释都极好, 而且有不少值得称道的地方:

  • 由Doxygen生成的很详细的api文档.
  • 由于架构特别好, 代码中大部分内容, 包括不同的形状, BRDF, 介质散射, 甚至渲染模型(如MLT), 都是用插件实现的. 多数都只有一个单独的cpp文件, 被编译成动态链接库之后调用...
  • 自动生成的算法文档..作者写了一个parser从代码中找出用latex写的注释, 生成了一份精美的pdf. cross-reference良好.
  • 优化强劲. 有SSE, 各种caching, 自己手动管理了一些内存分配, 还能grep到 __asm__
  • 跨平台. Linux/Win/OSX. 装了必要的库和工具之后一遍编译通过, 包括文档也是..很靠谱
  • 自带python api..
  • 用了GPU..
  • 有个开发博客, 作者没事上去贴张图什么的.
  • 很多不错的封装, 对流读写, 线程, 内存分配的手动管理, 智能指针, Cache, SSE都有专门的类负责管理,有不少值得研究和收藏的部分.
  • 构架奇葩的地方: 有一个大基类Object, 大家都有toString(), getType()方法. 另外还有一个神奇的类叫Class, 写了一些宏来继承这个类实现各种奇葩操作. Stroustrup在 The Design and Evolution of C++ 中批评过 Object 和 getType 的设计方式, 不过项目太大了这样设计可能也有一定道理.

稍微看了一下mitsuba的代码和文档之后, 大概明白了渲染部分的架构, 发现自己以前那个架构与它实在差的太远, 想扩展出一个MLT出来几乎不可能. 架构果然是件神奇的事..

Mitsuba说它是基于pbrt设计的, 于是之后又去看了pbrt.

pbrt既是一本书的名字, 也指这本书配的代码. 代码依然很靠谱, 渲染相关的构架与mitsuba差不多, 也做了很多优化, 也跨平台. 代码量有10W+, 看来应该算是mitsuba的原型.

神奇的是那本书, 网上可以找得到文字版精装pdf, 就弄来看了一下. 全书1200页, 无比详细的解释了pbrt里的几乎所有代码! 包括它们的理论推导, 架构设计, 代码实现, 算是在手把手教你写渲染引擎了. 覆盖的内容也特别丰富, 有几何求交, SAH-Based KDTree, Sampling Theory, 相机模型, 光学, 代码用到的四元数和矩阵分解.. 我听说过的技巧全都覆盖了.

而且这本书是按照Knuth所提出的Literate programming 方式写的..让我总算明白了literate programming是什么, 好像确实增加了可读性.

最后我去以6分钱一页的价格把这本书给打印了...近期看一看.

作者貌似是Pat Hanrahan的学生, 也很有名的样子, 为pixar开发过renderman.

References

Bidirectional Path Tracing论文.

MLT论文.

A Practical Introduction to Metropolis Light Transport. 稍微通俗一点的描述了MLT.

Mitsuba项目主页

Wenzel Jakob当助教的时候, 写了个光线追踪框架叫nori, 6000多行, 除了渲染模型之外全都写好了, 让学生在那个基础上开发. 这个人好靠谱.

PBRT主页

基于PBRT实现的面向应用级的渲染引擎LuxRender

Comments