Archive for 2020年4月

Phong Model and Physically-Based

2020/04/25

Phong model 大概是计算机图形领域最简单的材质模型。仅由三个 (组) 参数组成。Nuo Model Viewer 作为个人学习项目从实现 rasterization 渲染入手,自然最初只支持 Phong model。有个问题伴随着 Nuo Model Viewer 的扩展:是否要支持其它更复杂的材质模型?另一个相关问题是,如果 Nuo Model Viewer 支持 path-tracing,对于现有基于 Phong model 的模型是进行预处理转换成其它材质模型,还是在渲染中直接处理 Phong model ?

我准备在 Nuo Model Viewer 中实现 path-tracing 时发现手头大部分模型只有 Phong model 材质。更重要的是,计划实现的许多效果似乎不需要一种比 Phong 更复杂的材质模型。我决定在渲染算法中直接处理 Phong model 或者它的简单变种,因为可能有两个好处:第一是避免让代码更复杂去支持实际很少用到的材质模型;第二是避免直接照抄现有资料里对材质的处理,从而能发现渲染方法和材质模型集成的一些问题 [1]。

什么是「Physically-Based」?

首先把注意力从 Phong model 本身移开,从更宽泛的角度考虑材质模型在渲染方法中「可用」的标准。Path-tracing 渲染的方法和材质被称为 physically-based。渲染过程的 light tranport equation 可以说有比较严格的物理基础,但材质方面很难用简单模型直接模拟物理材料。 现今使用的 phiscially-based reflection models 的定义和真实世界材质的微观结构只有很弱的联系。所谓「弱联系」意味着图形学的 physically-based material 不代表「符合」真实世界,而仅仅代表「不违反」真实世界的某些限制。

最常见的 physically-based model 是 BRDF。满足「能量守恒 (energy-conservative)」和「光路可逆 (reciprocal)」两个限制的 reflection model 都可称为 BRDF。更「真实」的 reflection model 就是要满足这两个 BDRF 限制以外的更多限制。虽然很多介绍 path-tracing 的资料涉及了更「真实」的 reflection model,但几乎没有直接总结 BDRF 以外的限制。为了稍后能理解 Phong model 的局限以及如何修改让它能适应 path-tracing,把这些限制一起总结如下:

  • 能量守恒,对所有可能的 ω’,reflectance distrubtion 满足
  • 光路可逆,满足
  • 连续变化,如果观察真实材质时视角和光源的连续变化不会导致颜色的突变,那么虚拟材质的定义也要保持连续。
  • 边界条件,这条会在后面章节详细解释。

BDRF Normalization

现在回到 Phong model 本身。如果它能够直接或者在修改后满足上节列出的限制,就能适用于 path-tracing。在 Phong model 刚被提出时,计算机图形还通常只处理 singular 光源 (点光源或理想方向光源) 。这种情况下 Phong model 公式为:

这里的是出射光线与入射光线的理想镜面反射方向之间的夹角。因为后来提出的 micro-facet 模型以及实际观察的效果,在实际应用中已经被 half vector 和 normal 的夹角取代。本文后面也会改用,但目前的讨论中依然先用

要适应 path-tracing,这个公式首先要改写成基于 reflectance distribution 的积分形式,从而不再局限于 singular 光源。并且我们希望改写后的形式至少满足 BRDF 的两个限制。首先,把相对光强值替换为有实际物理意义的 radiance。然后在式子中加入 normalization 系数,下面我们分析如何对它们合理取值以满足 BDRF 甚至更多的 physically-based 限制。

在分析如何取值之前先解决另一个问题。在上面式子中出现了作为分母的情况,所以必须对为零的情况做下面的特殊处理:

这违反了前面提到的「连续变化」限制 [2]。所以很早出现的对 Phong model 改动之一是去掉分母,变为 [3]:

这个变化说明为了让 reflection model 满足 phiscally-based 的某个限制,我们会作出并不深究真实物理根据的「粗暴」改动。现在来确定的具体取值。这里先尝试只考虑「能量守恒」限制。假设材质只有 diffuse 部分 (即为零),从以前的 blog 可以得出在不大于 1 的前提下,有:

用类似的方法分析,先假设为零,满足能量守恒的不等式为:

这里不妨设 ω‘ 与 normal 重合,因为可以使不等式左侧最大化。左侧变换为:

可以得出在不大于 1 的前提下,有:

需要注意的是上面的推导基于而不是今天业界常用的。基于的推导复杂得多。所以本文只用基于的推导展示大致思路,就不具体推导基于的模式。而且基于的模式中的精确取值很复杂,实际工程中一般取下面的近似值:

代替的 Phong model 的变种称为 Blinn-Phong。经过 BDRF normalization 的 Blinn-Phong reflectance distribution 的完整公式为:

至此我们完成了修改 Phong 更加符合 physically-based 原则的第一步,BDRF normalization。在这个过程中有没有忽略或者违反了其它的 physically-based 限制,在后面会有更多的讨论。

Importance Sampling

为了在 path-tracing 过程中使 Monte Carlo 方法能更快收敛,需要在 path construction 过程中选择合适的 importance sampling。以前的 blog 里分析过,当 path construction 的采样概率分布为时 LTE 的 Monte Carlo estimator 为:

下面分别考虑 diffuse 和 specular 部分。对 diffuse 部分来说,只要让采样概率分布满足就可以让 estimator 简化。把一个 uniform 分布的随机变量转化为是非常简单的操作,这里不详细分析。

下面来分析 specular 部分。 Blinn-Phong model 只考虑 specular 的 estimator 可以直接写为:

关键在于找到合适的 来抵消。因为与 half-vector 相关,很难通过到入射光线的采样来抵消它。所以我们尝试用 sample half-vector 的方法来解决。有现成的办法 [4] 可以把 uniform 分布的随机变量转换为满足的随机量,根据概率分布的归一化可以得到 [5]:

根据微分关系,可以得出:

将上式代入之前的 estimator 公式,就可以得到 Blinn-Phong specular 部分的 Monte Carlo estimator 为:

边界条件

上面章节数学分析已经足够在 path-tracing renderer 里原封不动地实现 Blinn-Phong model。按照所述的方法 Nuo Model Viewer 做到了 path-tracing 和 rasterization 的效果几乎一致。

接下来我们当然想验证用这种实现能否完成更多 physically-based 效果。在 Nuo Model Viewer 的实际测试中,间接光照和体积光源的效果都不错。那么在 path-tracing 中经常被提及的镜面反射效果方面,Blinn-Phong model 的表现如何?

现在暂时脱离对「镜面效果」的分析,而去考虑如何衡量一个 reflectance distribution 的数学描述是否更好的符合物理实际。我们几乎不可能实际测量真实世界的物体表面在整个球面坐标上任意两个方向的光线反射能量比,也不可能随心所欲地构建物质来验证任意参数的 reflectance distribution 函数。但我们有可能做到两件事:第一,用实验测量或者严格的物理理论来得到特定方向上的光线反射能量;第二,把实际物质表面的某种特定性质对应到 reflectance distribution 参数的某个特定取值。这样就可以比较测量值或理论计算值是否和对应参数的 reflectance distribution 在特定方向的值足够接近。这就是用「边界条件」来衡量 reflection model 的质量。

回到「镜面效果」的问题。在真实世界里随便选择一个实际物体的表面,我们没法直接得出其对应的 Blinn-Phong 或者其它 reflection model 的参数。但是对于真实世界中非常光滑的接近镜面的表面我们可以做到。对于基于 micro-facet 模型的 reflection model 来说,「非常光滑」表示 micro-facet normal 的分布不再是连续范围,而是等于宏观的 surface normal。对于 Blinn-Phong model 来说,这表示的参数 m 为无穷大。

另一方面,我们可以在真实世界中通过物理理论分析和实验测量来得到光在光滑表面上的反射能量。通过 Fresnel equation 分析和测量的验证可知镜面反射方向出射光和入射光随入射角的变化规律。当从 0 开始增加时,在很大范围内出射光的 radiance 和入射光之比接近一个小于 1 的常数,在接近 90 度时这个比值会急剧上升,在 90 度时为 1。在物理学上把这个比值相对的函数称为 Fresnel reflectance,用表示。

现在回去检验 Blinn-Phong model 是否符合镜面反射下关于的「边界条件」。当 m 无穷大的时候其 reflectance distribution 是一个冲击函数,这时 Monte Carlo 的采样方向不再随机分布,而是固定在理想反射方向。其 estimator 就是积分的确定值。

而根据物理分析,我们期待的理想镜面采样值应该是:

上面的 reflectance distribution 的计算值中的导致了在边界条件下显然不符合实际的物理状况,连近似都谈不上。说明 Blinn-Phong model 不能胜任在 path-tracing 中实现镜面效果。Nuo Model Viewer 的测试结果是镜面反射的光强过低。因此我们要寻找新的符合镜面「边界条件」的 reflection model。我们希望在的情况下这个模型的 LTE 积分和上面期待的采样值近似或者相等。

但是,先等一下!既然我们希望采样值是上面的形式,那还用求出 reflectance distribution 吗?直接把这个采样值作为 Monte Carlo 过程的 estimator 不就可以了!确实如此,只不过还需要对它做个小改动。上面的式子并不能在接近 0 的时候连续的减少为 0,以此它依然有最初 Phong model 在处不连续的缺陷。考虑到这种不连续通常在观察角度 (也就是) 比较小时影响明显,所以我们把 estimator「粗暴」地改为:

从这个 estimator 可以反推出 specular reflectance distribution 为:

而我们可以在《Physically-Based Rendering》介绍的几种 model 中发现,其中的 Fresenel Blend model「恰好」符合上式。这大概也是它得名的原因 —— 与其说是通过完整物理模型推导出来的,不如说它只是从这单个实际物理特性出发,考虑 micro-facet 模型共有的的微分关系,为了满足「镜面」边界条件反向凑出来的模型。

前面提到过给定材料的光滑表面的从 0 开始的一个很大范围内都等于某个小于 1 的常数。我们可以把用作这个常数。对于大多数金属,其光滑表面的接近 Schlick 方法:

如果把上式推广到「不绝对光滑」的 micro-facet 模型表面,应该由与 micro-facet normal 相关的夹角取代。同时,因为 specular 部分的取值大于 Blinn-Phong model,Fresenel Blend 的 diffuse 部分要依据「能量守恒」作出调整。

Physically-Based Rendering

本文讨论了 Phong model 如何经过必要修改从而应用于 path-tracing。经过修正的 BRDF Blinn-Phong 可以用于 path tracing 的 importance sampling。但它也有不能实现镜面反射的局限性。

新选用的 Fresenel Blend model 满足镜面的边界条件。同时它的 reflectance distribution 可以适用于任意的,也就是任何 micro-facet model。所以类似于 Blinn-Phong,基于Fresenel Blend model 的 Monte Carlo 过程依然可以采用的 path construction 方法。这样 Nuo Model Viewer 只需要单一的 importance sampling 代码。

和 Blinn-Phong 一样,Fresenel Blend model 也由三个 (组) 参数定义。可以直接把前者的m 套用到后者上。但这样直接套用的结果当然和 Blinn-Phong 本身的效果不同。在无需明显镜面效果的模型上 Nuo Model Viewer 依然采用 Blinn-Phong model 来体现设计本意。需要镜面效果的部分通常是地面,特别是和真实图片 blending 时采用的 virtual ground plane。Nuo Model Viewer 在这部分采用 Fresenel Blend model。

脚注:

  1. 这也秉承了我一贯不喜欢用现成实验环境的习惯。
  2. 对 reflection model 的积分通常只考虑上半球,所以 naive 的想法是取值造成的不连续性似乎无关紧要。但不要忘记,只考虑上半球的原因正是因为假定在的条件下。所以必须避免处的不连续。
  3. 因为去掉分母使被积函数整体连续,并且在时总为零,所以在只考虑上半球的前提下可以去掉其中的 max 函数。
  4. Diffuse importance sampling 可以看作此方法在 m = 1 时的特例。
  5. 此处推导与上文 specular 部分的 BDRF normalization 方法类似。

单元测试和个人技能成长

2020/04/17

最近看到个对单元测试的看法:

单元测试是预防错误的主要手段。如果一个团队所在的领域对错误的容忍度高,而其市场需要 move fast,就 (暂时) 不用有单元测试;反之,若所在领域对错误容忍度低,那么重视实践单元测试的团队会取得优势。

然而,单元测试并不是「预防错误的主要手段」。首先单元测试并非全部测试。除了单元测试,还有组件测试,系统整体的人工测试,自动化的整体测试等等。团队资源应该在所有测试类型间合理分配,而不是预设单元测试最重要。其次,错误预防的关键点在于 escape to user 而不是 commit to repo。也就是说很多非测试技术,比如 Git 的合理 branching 策略和 back tracking 也可以更合理的成本降低错误。从最终结果的角度来看,若先不考虑 escape to user 造成的公开影响和修复 bug 的成本,系统整体的人工测试就可以发现所有错误。如果把软件开发的概念套用在流程设计上,整体人工测试是开发流程的基本功能,而其它方法都是开发流程的「优化」。那么可以想到关于优化的老话:「Premature optimization is the source of all evil.」 优化的前提是 profiling。制定流程应该把「只有整体人工测试」作为缺省情况,根据执行的实际结果谨慎加入其它技术。

说到这里谈谈这篇 blog 的真正重点。我之前涉及单元测试讨论的 blog 有好几篇,所以并不想再写一篇去扩展甚至重复以前的观点。这里我想到的不是分析的结论,而是分析这些看法的手段。最开始引用的看法把缺乏单元测试的视为一种「技术债务」而不是可以在团队中合理分析执行的长期策略。这是个错误的归类,而由这个错误可以想到技术人员如何思考自己的技能成长。

现今是「纯技术」有所贬值的时代。当年 Paul Graham 用 Lisp 作出网站卖得 5000 万美元而鼓吹了很久 Lisp。但回过头去看 Lisp 已没那么神奇。很多通用技术已经被成熟的模块甚至 AI 部分取代。技术人员需要思考的是有什么和技术密切相关但又不是「纯技术」的东西,可以帮助发展个人技能。对「技术债务」的理解和评估恰恰是技术人员可以体现不可替代性的重要项目。认清团队技术储备中哪些是资产,哪些是债务,如何合理的偿还或者重组技术债务,这些是和管理财务债务一样重要的技能。