闫令琪:Games101 现代计算机图形学-光线追踪(三):渲染方程和路径追踪path ray tracing & 作业Assignment07解析

路径追踪技术(Path tracing,PT)已经是当下工业中离线渲染使用的主流技术,不管是商业渲染器如皮克斯的RenderMan,Solid Angle的Arnold等,还是迪士尼的in-house渲染器Hyperion以及Weta Digital的Manuka都是基于路径追踪技术。
但是搜遍全网也没有一篇能够通俗易懂的讲清、讲全一个path tracing,本文希望达到这个目标!!!

0 whitted光线追踪的局限

闫老师课上举了下图这两个例子说明whitted ray tracing的局限性。其实根本原因在于whitted光线追踪是非物理的,首先对于材质的处理,没有办法模拟真实材质那种漫反射,折射和反射都是理想情况,漫反射直接渲染颜色,这样就无法表现下图右边那种情况;其次一个点的颜色其实是受直接光照和间接光照影响的,但是whitted光线追踪是无法反映间接光照的影响,路径追踪是求解渲染方程,在渲染方程中,某个点的颜色其实该点周围立体角上所有光线对该点的积分。
在这里插入图片描述

1 辐射度量学

1.1 光线的表示 Radiance

这个概念是渲染方程和路径追踪中最关键的概念。因为渲染方程要用数学的方式去描述光的传播,那很自然的一个问题就是:

光线用数学怎么描述?

在这里插入图片描述
上图是闫老师课件中给的光线的描述radiance
光的能量功率Φ非常好理解,那么怎么由Φ定义出radiance呢?

在这里插入图片描述

光的能量功率Φ可以认为由光源发出的这一圈一圈的能量,那么一条光线便是这圈上的一个点,对于半径为1的球,上面一个点能量表示为上图中的E,对于外圈的球上一个点的能量,表示为E',分母除了和球的立体角相关,还和 r 2 r^2 r2相关,而 r 2 r^2 r2可以表示为面积。从这两个位置上的能量可以发现,这个球上任意一点的能量,和立体角面积两个变量相关。
那么可以很自然地推出一条光线的数学表示:
在这里插入图片描述

这个分母上的cosθ怎么理解?

1.2 物体表面上一个点的亮度 Irradiance

搞定了光线的表示,还有一个定义需要解决,物体表面一个点是需要接受四面八方的漫反射光线,这个点的最终亮度怎么表示?
一图胜千言:
在这里插入图片描述
对于上图中的那个点的亮度,显然只需要把立体角内所有的光线进行积分即可。
从上面光线的表示,可以很容易写出微分形式:
在这里插入图片描述
则某个点的亮度,写成积分即可:
在这里插入图片描述
这个变量,叫irradiance:是表示一个点接收的周围光线的亮度。

1.3 BRDF(Bidirectional ReflectanceDistribution Function)和材质

BRDF的中译名叫双向反射分布函数。
这里是查阅资料时发现的比较全面的综述和细致的讲解:
《RTR3》提炼总结】(六) 第七章 · 高级着色:BRDF及相关技术
基于物理着色:BRDF
我们看到一个表面,实际上是周围环境的光照射到表面上,然后表面将一部分光反射到我们眼睛里。双向反射分布函数BRDF(Bidirectional Reflectance Distribution Function)就是描述表面入射光和反射光关系的。入射光就是上面讲的Irradiance,出射光就是radiance。

BRDF定义为:
对于一个方向的入射光,表面会将光反射到表面上半球的各个方向,不同方向反射的比例是不同的,我们用BRDF来表示指定方向的反射光和入射光的比例关系,数学公式为
在这里插入图片描述

基于物理着色:BRDF这篇文章里提供了两种解释,为什么定义成这两个量的比值,而不是 d L L \frac{dL}{ L} LdL等之值,从数学上讲,当出射方向立体角趋于0时,这个比值的极限也是0,所以没有意义,从实验的角度,入射方向的radiance却非常难测,所以采用这个比值。
公式中为什么是 d L r ( ω r ) dL_r(ω_r) dLr(ωr),因为定义里就是指定方向的入射光和出射光,所以这里的 ω \omega ω是被微分的量。

1.4 渲染方程

渲染方程要解决的问题就是,怎么样描述看到的物体表面一个点上的光亮。这个点进入观察点的那个物理量,啰嗦一点,其实就是那个点漫反射到观察点的光线,而光线就是radiance。
由上面的BRDF公式可以很容易写出:
d L r ( ω r ) = f r L i ( ω i ) c o s θ i d ω i dL_r(ω_r) = f_r L_i(ω_i) cos\theta_id\omega_i dLr(ωr)=frLi(ωi)cosθidωi
写成积分形式就是该光线完整的表达:
L r ( ω r ) = ∫ f r L i ( ω i ) c o s θ i d ω i L_r(ω_r) =\int^{}_{}f_r L_i(ω_i) cos\theta_i{d\omega_i} Lr(ωr)=frLi(ωi)cosθidωi
接下来用闫老师的课上PPT表示:
在这里插入图片描述

在这里插入图片描述

老师课上的公式中的符号说的并不清楚,网上发现了这张图,说的还挺不错的:
在这里插入图片描述

2 渲染方程的求解

2.1 渲染方程视角下的光线传播

在这里插入图片描述
对于这个漫反射来说,很自然会有疑问:绿色墙上的颜色会漫反射到立方块上,立方块上的也会漫反射到墙上,这就有点鸡生蛋、蛋生鸡的循环,这个过程到底是怎么样?我们求解渲染方程的话,终止光线弹射的条件是什么?
假如我们把时间放慢到可以观察光线传播的程度,以物体表面一个点Point点为例,观察:
①可以看到会有一根光线打到这个点,这个时候物体有一次亮度;
②所有物体表面都有一根光线被打量后,光线开始第一次弹射,Point点会被周围弹射的光线打中,叠加刚才的直接光照,会有第二次光亮;
③开始第二次弹射……
④第三次弹射等等……
闫老师的课上给了一个例子:
在这里插入图片描述

然而,到目前为止,跟渲染方程还没什么关系??是的,哈哈哈……
闫老师课上通过一系列神操作,通过渲染方程推导出下面的数学表达式,即某点的光照可以分为自己发的光 E E E,直接光照 K E KE KE,二次弹射 K 2 E K^2E K2E等。上面的思想实验,其实是可以通过数学推导出来,虽然我真的没听懂……[捂脸]
在这里插入图片描述

光栅化的局限就在于,它从原理上,是只能反映第一次光照,因此无法呈现全局光照

2.2 蒙特卡洛积分

渲染方程显然没有解析解,并且数值离散解也几乎不可能,所以这时候就得祭出蒙特卡洛大法。以下内容来源于:蒙特·卡罗(Monte Carlo)积分详解

对于一个连续函数f,它的积分公式为:

F = ∫ a b f ( x ) d x F = \int _{a}^{b}f(x)dx F=abf(x)dx

对应的,f的蒙特·卡罗积分公式如下:

F N = 1 N ∑ i = 1 N f ( X i ) p d f ( X i ) F^{N} = \frac {1}{N}\sum _{i=1}^{N}\frac {f(X_{i})}{ pdf(X_{i}) } FN=N1i=1Npdf(Xi)f(Xi)

蒙特卡罗最关键的就是理解这条公式了。其他延伸探讨都可以暂时忽略。那么这条公式如何理解呢?首先第一点是,虽然这条公式没有积分符号 ∫ \int ,但是它认被称为积分,这是因为这公式的作用相当于在对f(x)做积分,只不过不那么“精确”,即蒙特·卡罗积分是对理想积分的近似。

那么这个近似是如何完成的?很简单,核心就是两个字:采样(Sampling)。对一个连续函数的采样方法是在该函数的定义域中随机挑N个值,并求出对应的N个 f ( X i ) f(X_{i}) f(Xi),就得到了样本集合。再对这些样本集合做一些换算,就可以得到一个近似的积分了。对于蒙特·卡罗积分,采样样本越多,就越逼近真实的积分结果,这是蒙特·卡罗积分的最核心特性。

继续观察上面的公式,里面还有一个极其重要的参数:pdf(probability distribution function,概率分布函数)。pdf还有个近亲pmf,下面小节详解pdf、pmf的由来。

pdf和pmf

  • pmf(probability mass function),指的是离散的随机变量的概率分布函数
  • pdf(probability distribution function), 指的是连续的随机变量的概率分布函数

离散的随机变量X的数学期望为:

E [ X ] = ∑ x i p m f ( x i ) x i E[X] = \sum _{ x_{i} }pmf(x_{i})x_{i} E[X]=xipmf(xi)xi

连续的随机变量X的数学期望为:

E [ X ] = ∫ − ∞ ∞ p d f ( x ) x d x E[X] = \int ^{\infty }_{-\infty }pdf(x)xdx E[X]=pdf(x)xdx

pdf和pmf名字接近,含义也是接近。pdf、pmf函数的参数都是样本值x,返回值是概率,即表示一个样本出现的概率,所有样本的出现概率之和(概率的积分)应等于1。要注意的是,pdf、pmf的存在说明有可能每个样本的出现概率都是各不相同的。

pmf
pmf的简单例子就是基于均匀分布的离散的随机变量X,此时 p m f ( X i ) pmf(X_{i}) pmf(Xi)恒等于 1 N \frac{1}{N} N1,含义是每个随机样本的出现概率等于 1 样 本 总 数 \frac{1}{样本总数} 1

通过这个例子也印证了pmf的性质:pmf函数的所有结果值之和等于1。

pdf
借用http://www.scratchapixel.com/的一个很好的例子来说明:

在这里插入图片描述

这个例子中,目标问题是求出该函数[a,b]段曲线下方的面积(最后一幅图的黑色区域),也就是要求该函数[a,b]段的积分。基于蒙特·卡罗积分的解法,就要用上面给出的公式:

F N = 1 N ∑ i = 1 N f ( X i ) p d f ( X i ) F^{N} = \frac {1}{N}\sum _{i=1}^{N}\frac {f(X_{i})}{ pdf(X_{i}) } FN=N1i=1Npdf(Xi)f(Xi)

在此图中,做了四次随机采样,得到了四个随机样本 x i x_{i} xi x 1 、 x 2 、 x 3 、 x 4 x_{1}、x_{2}、x_{3}、x_{4} x1x2x3x4,并且进而得到了这四个样本的 f ( x i ) f(x_{i}) f(xi)值: f ( x 1 ) 、 f ( x 2 ) 、 f ( x 3 ) 、 f ( x 4 ) f(x_{1})、f(x_{2})、f(x_{3})、f(x_{4}) f(x1)f(x2)f(x3)f(x4)。(原文没有提及如何得到 f ( x i ) f(x_{i}) f(xi)。函数f是奇形怪状的,不太可能有表达式存在,难道是用尺子量的?暂且忽略这个事吧。)

有了这4个样本后,可以针对每一个样本求一个近似面积值,这个面积值等于 f ( x i ) ( b − a ) f(x_{i}) (b - a) f(xi)(ba)。为什么可以这样做呢?是因为每一个单独的样本是对原函数f的近似,即在每个样本中,认为 f ( x ) f(x) f(x)恒等于 f ( x i ) f(x_{i}) f(xi),从而让原函数曲线简化成一个矩形区域,而矩形的面积显然就是长(b-a)乘以宽 f ( x i ) f(x_{i}) f(xi)

得到4个近似面积值后,再求出它们的均值(数学期望),就完成了蒙特·卡罗积分。把上述流程汇总得到:

A r e a = 1 4 ( f ( x 1 ) ( b − a ) + f ( x 2 ) ( b − a ) + f ( x 3 ) ( b − a ) + f ( x 4 ) ( b − a ) ) Area = \frac {1}{4}(f(x_{1})(b - a) + f(x_{2})(b - a) + f(x_{3})(b - a) + f(x_{4})(b - a)) Area=41(f(x1)(ba)+f(x2)(ba)+f(x3)(ba)+f(x4)(ba))

= 1 4 ( b − a ) ( f ( x 1 ) + f ( x 2 ) + f ( x 3 ) + f ( x 4 ) ) = \frac {1}{4}(b - a)( f(x_{1}) + f(x_{2}) + f(x_{3}) + f(x_{4}) ) =41(ba)(f(x1)+f(x2)+f(x3)+f(x4))

= 1 4 ( b − a ) ∑ i = 1 4 f ( x i ) = \frac {1}{4}(b - a)\sum _{i=1}^{4}f(x_{i}) =41(ba)i=14f(xi)

此时,对比下蒙特·卡罗积分公式:

F N = 1 N ∑ i = 1 N f ( X i ) p d f ( X i ) F^{N} = \frac {1}{N}\sum _{i=1}^{N}\frac {f(X_{i})}{ pdf(X_{i}) } FN=N1i=1Npdf(Xi)f(Xi)

发现两个式子非常相似,对式子做下转换得到:

A r e a = 1 4 ∑ i = 1 4 f ( x i ) 1 b − a Area = \frac {1}{4}\sum _{i=1}^{4}\frac {f(x_{i})}{\frac {1}{b - a} } Area=41i=14ba1f(xi)

于是可以知道 p d f ( x i ) pdf(x_{i}) pdf(xi)等于:

p d f ( x i ) = 1 b − a pdf(x_{i}) = \frac {1}{b - a } pdf(xi)=ba1

这意味着,对于连续函数f,f的每个可能取值x的出现概率等于x的取值范围[a,b]的倒数 1 b − a \frac{1}{b-a} ba1

在实际应用场合,随机变量X要写成F(X),即可能需要对X做一个转换再使用。这时候要注意F(X)的pdf不等于X的pdf。

蒙特·卡罗积分的数学期望等于理想积分?
对于下面的 F F F F N F^{N} FN

F = ∫ a b f ( x ) d x F = \int _{a}^{b}f(x)dx F=abf(x)dx
F N = 1 N ∑ i = 1 N f ( X i ) p d f ( X i ) F^{N} = \frac {1}{N}\sum _{i=1}^{N}\frac {f(X_{i})}{ pdf(X_{i}) } FN=N1i=1Npdf(Xi)f(Xi)

是否随着N变大, F N F^{N} FN会逼近 F F F?即 F N F^{N} FN的数学期望是否等于 F F F?Monte Carlo Methods in Practice文章中给出了推导过程:

2.png

第二行到第三行是最不好理解的。因为这里其实用到了新的知识点:Law of the unconscious statistician(简称:LOTUS)。LOTUS的应用情景是,已知随机变量X的概率分布,但不知道f(x)的分布,此时用LOTUS公式能计算出函数f(x)的数学期望。LOTUS的公式如下:

f(x)是离散函数时:

E [ f ( X ) ] = ∑ x i f ( x i ) p m f ( x i ) E[f(X)] = \sum _{x_{i}}f(x_{i})pmf(x_{i}) E[f(X)]=xif(xi)pmf(xi)

f(x)是连续函数时:

E [ f ( X ) ] = ∫ − ∞ ∞ f ( x ) p d f ( x ) d x E[f(X)] = \int _{-\infty }^{\infty}f(x)pdf(x)dx E[f(X)]=f(x)pdf(x)dx

(建议对比第二小节开头的两条公式来理解)

有了LOTUS公式,再来看第二行到第三行的转换,就好理解了:

E [ f ( X i ) p d f ( X i ) ] = E [ f ( x ) p d f ( x ) ] = ∫ − ∞ ∞ f ( x ) p d f ( x ) p d f ( x ) d x E[ \frac {f(X_{i})}{pdf(X_{i})} ] = E[ \frac {f(x)}{pdf(x)} ] =\int _{-\infty }^{\infty}\frac {f(x)}{pdf(x)}pdf(x)dx E[pdf(Xi)f(Xi)]=E[pdf(x)f(x)]=pdf(x)f(x)pdf(x)dx

= ∫ − ∞ ∞ f ( x ) d x =\int _{-\infty }^{\infty}f(x)dx =f(x)dx

  • 上面推导充分证明了,蒙特卡洛积分是无偏的
  • 可以进行非均匀采样,结果依然是无偏

上面的内容已经足够继续进行下面的旅程,此处贴出两个知乎上的优质贴,后续继续嗑蒙特卡洛积分。
蒙特卡洛积分 - chopper的文章 - 知乎
Monte Carlo数学原理 - papalqi的文章 - 知乎

2.3 渲染方程的蒙特卡洛形式

渲染方程没法直接求解,求解的思路就是上面的蒙特卡洛积分。参考两个公式,可以很方便地写出来
在这里插入图片描述
不考虑自发光的情况的渲染方程形式:
在这里插入图片描述
这个方程理论上是可以求解的, p d f ( ω i ) pdf(\omega_i) pdf(ωi)可以假设就是半球面上均匀采样, f r f_r fr可以初定一个固定的值,但是还有一个问题:

radiance该怎么表示?比如最初的灯光,它怎么表示 L i ( p , ω i ) L_i(p,\omega_i) Li(p,ωi)

2.4 递归求解的构建和终止条件

2.4.1 采样指数发散的问题

一图胜千言,先上图。
在这里插入图片描述
在P点的出射光线按照上面的方程形式,需要采样N条光线作为入射光线,这N条光线,每条假如与物体交点为Q,对每一个Q点又需要采样N条光线,这样光线弹射几次, N ∗ N ∗ N … … N*N*N…… NNN这个计算量就爆炸了。
解决的思路也非常的简单粗暴:
N = 1 N=1 N=1,每次只采样一条光线。
公式里的求和 ∑ N \sum_{N} N里的 N N N不是通过对像素光线相交点采样 N N N,改为对每个像素点采样 N N N条光线,每条光线每次碰撞只采样一条。
上图!
在这里插入图片描述

2.4.2 采样终止条件:俄罗斯轮盘赌

上面的求解还有一个关键问题,递归的终止条件是什么?如果没有递归的终止条件,这个采样会无穷进行下去。
在whitted ray tracing中,是通过直接规定弹射次数来作为终止条件,粗暴地讲,在path ray tracing中其实也可以这么做,但是这么做不是最优的,存在的问题有:①因为是随机采样,有的可能是不重要的路径,对于这些不重要的路径,其实并不需要那么多次采样,浪费计算资源;②有的采样可能是重要路径,其实需要多次采样,出现采样不足的情况。蒙特卡洛估计作为一种概率估计,很重要的就是采样,怎么样正确、高效地采样,是path ray tracing中很重要的一个方向。
所以直接固定次数不是不行,但是显然不是很好的做法。
于是乎,俄罗斯轮盘赌算法应运而生。
其实原理很简单,就是设置一个概率值 P ∈ [ 0 , 1 ] P\in[0,1] P[0,1],每一次反射,生成一个相关概率,如果概率小于 P P P就继续传播并且 L o = L / P L_o=L/P Lo=L/P,否则就停止传播。这样既能实现对光线的终止,同时估计还是无偏的。
E 期 望 = P ∗ ( L / P ) + ( 1 − P ) ∗ 0 = L E_{期望} = P*(L/P) + (1-P)*0 = L E=P(L/P)+(1P)0=L

作业框架里, P P P就直接生成一个随机数,并没有和BRDF联系起来,是简化版本。

2.5 求解效率的提高

问题到此结束了吗?哈哈, 当然没有!
上面的已经是一个正确的解决方案了,但是,存在一个很大的问题,就是效率太低了,举个栗子:
在这里插入图片描述
实际场景中,按照上述步骤,光线反射过程中能打中光源获得亮度的很少,这样绝大部分采样的光线返回都是0,这会造成大量的浪费,和噪点。
在这里插入图片描述
重新拿上面的传播图示意,对于P点,之前的思路是蓝色的采样线,如果他没有打中光源,这个采样就浪费了,但是实际上,对于P点,他肯定是有光照颜色的,因为有直接光照,因此挽救这个采样线的思路也很简单:加上直接光照。
所以每次采样的时候,返回的颜色,除了采样光线,再加上直接光照。其实跟2.1小节的分析也是一样的,第一轮光线传播是直接光照,所以光线漫反射过程中,至少第一轮的直接光照应该参与漫反射。
在这里插入图片描述
但是方程中是对立体角 ω \omega ω进行积分,光源对应的是面积,需要把面积转换为立体角。其实非常简单,因为立体角的定义就是面积除以距离的平方。
在这里插入图片描述
因此方程变成下面的形式,对光源积分的话, p d f = 1 S 面 积 pdf = \frac{1}{S_{面积}} pdf=S1
在这里插入图片描述
在实际写代码的时候,还要加上光源是否被遮挡的判断。

3 渲染方程的代码实现

3.1 path tracing伪代码

shade(p,wo)
{
	求光线相交的物体
	if(物体== 光源)return 光源颜色

	//直接光
	对光源上采样一个点x',pdf_light=1/A;
	if(这个点跟P点不存在遮挡)
		L_dir = L_i * f_r * cos θ * cos θ’ / |x’ - p|^2 / pdf_light
	
	//间接光
	取一个随机概率P_RR
	if(P_RR满足俄罗斯轮盘赌)
	{
		采样一个漫反射方向wi;
		确定漫反射相交点q;
		L_indir = shade(q, wi) * fr(wi, wo, N) * dotProduct(wi, N) / pdf(wi, wo, N)/ Scene::RussianRoulette);
	}
	Return L_dir + L_indir
}

3.2 作业代码

// Implementation of Path Tracing
Vector3f Scene::castRay(const Ray& ray, int depth) const
{
    // TO DO Implement Path Tracing Algorithm here

    //求入射光线wo的交点
    Intersection intersection = intersect(ray);
    Vector3f hitcolor = Vector3f(0);

    //判断是不是直接采样到光源了
    if (intersection.emit.norm() > 0)
        hitcolor = Vector3f(1);
    else if (intersection.happened)
    {
        //确定入射光线的方向、相交点坐标、交点法线
        Vector3f wo = normalize(-ray.direction);
        Vector3f p = intersection.coords;
        Vector3f N = normalize(intersection.normal);

        //对光源取一个采样点,确定采样点的方向、相交点坐标、交点法线
        float pdf_light = 0.0f;
        Intersection inter;
        sampleLight(inter, pdf_light);     //光源内随机采样一个点
        Vector3f x = inter.coords;
        Vector3f ws = normalize(x - p);
        Vector3f NN = normalize(inter.normal);

        Vector3f L_dir = Vector3f(0.0f);

        //求直接光照
        if ((intersect(Ray(p, ws)).coords - x).norm() < 0.01) //判断灯光有没有遮挡
        {
            L_dir = inter.emit * intersection.m->eval(wo, ws, N) * dotProduct(ws, N) * dotProduct(-ws, NN)/ (((x - p).norm() * (x - p).norm()) * pdf_light);
        }

        //求间接光照
        Vector3f L_indir = Vector3f(0);
        float P_RR = get_random_float();
        if (P_RR < Scene::RussianRoulette)
        {
            //采样一个漫反射方向
            Vector3f wi = intersection.m->sample(wo, N);
            L_indir = castRay(Ray(p, wi), depth) * intersection.m->eval(wi, wo, N) * dotProduct(wi, N) / (intersection.m->pdf(wi, wo, N) * Scene::RussianRoulette);
        }
        hitcolor = L_indir + L_dir;
    }
    return hitcolor;
}

在这里插入图片描述

4 path ray tracing的优化方向

本文只是路径追踪的起点,其实读懂上面的过程就能发现,这里面可以优化的地方太多,比如
采样,闫老师课上包括作业框架只是介绍了平均采样,
还有蒙特卡洛积分数学角度更好的求解,
还有点光源怎么处理,
怎么提高速度,运算量这么大的算法怎么做到实时的??

这篇文章,介绍了一些去除噪点的改进算法,非常棒
在路径追踪中分别采样直接与非直接光照

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值