games101 – 4 – Ray Tracing

games101 – 4 – Ray Tracing

对应的Lecture 13 ~ 17

为什么需要Ray Tracing

光栅化无法处理全局效果

  • 软阴影;
  • 光反弹次数超过一次的情况,如glossy reflection,indirection illumination;
  • 光栅化的很快,但是质量比较低;
  • raytracing,很准确,但是很慢;

Recursive (Whitted-Style) Ray Tracing

An improved Illumination model for shaded display” T. Whitted, CACM 1980

Ray-Surface Intersection

射线的定义:\(r(t) = o + td, 0 \le t \lt \infty\),一个起始点,和方向构成了一个射线。

对于射线和方程隐式表示的表面,可以通过求解方程式得到交点。更常见的场景是射线和三角网格模型进行求交计算。对于射线和三角形求交计算,可以先和平面进行求交,然后再判断交点是不是位于三角形内部。

(23条消息) Möller-Trumbore算法-射线三角形相交算法_zhanxi1992的专栏-CSDN博客

\[O + tD = (1 – b_1 – b_2)P_0 + b_1P_1 + b_2P_2 \\
O – P_0 = (P_1 – P_0)b_1 + (P_2 – P_0)b_2 – tD \\
S = E_1b_1 + E_2b_2 -tD
\]

矩阵的形式表示如下:

\[\begin{bmatrix}
-D & E_1 & E_2
\end{bmatrix}
\begin{bmatrix}
t \\
b_1 \\
b_2
\end{bmatrix} = S
\]

然后利用克莱姆法则(\(Ax = c=>x_i = detA_i/detA\))求解,

如果遍历三角网格中的所有三角形,对每个三角形进行相交计算,并找到最近的交点,整个过程是非常耗时的。需要的开销为#pixels x #triangles (x #bounces).

Bounding Volumes

可以利用包围盒,进行加速。针对复杂物体,构建出包围盒,然后如果射线不和包围盒相交,那么射线就不和包围盒内部的物体相交,如果相交,然后再进行内部物体的相交测试,而包围盒相交监测速度非常快。常见的包围盒有:包围球,轴对⻬包围盒,非轴对齐包围盒。更常用的是使用轴对齐包围盒,因为针对它的相交计算很简单。

在计算直线和Axis-Aligned Box相交的时候(此处允许t<0),分别针对每个维度,计算出\(t_{min}\)\(t_{max}\),那么\(t_{enter}\)等于各个维度\(t_{min}\)的最大值,\(t_{exit}\)等于各个维度\(t_{max}\)的最小值。如果\(t_{enter} < t_{exit}\),那么相交。

但是ray并不是一条直线需要对负值的情况进行额外判断:

  • 如果\(t_{exit} < 0\),那么不相交;
  • 如果\(t_{exit} \ge 0\)并且\(t_{enter} < 0\),那么射线的原点位于box内部,相交;
  • 那么ray和AABB相交,当且仅当\(t_{enter} < t_{exit}\ \&\& \ t_{exit} \ge 0\)

Spatial Partitions

  • Uniform Spatial Partitions (Grids) 适用于很多对象均匀的分布在空间中的场景。

更常用的是不均匀空间划分。

常见的分法如下:

K-D Tree – OI Wiki (oi-wiki.org)

Bounding Volume Hierarchy (BVH)

构建一个BVHs,需要进行如下操作:

  • 找到包围盒;
  • 将包围盒中的对象分成两个部分(细分的时候需要选择维度最大的方向进行划分,这里通常选择当前node轴长最大的轴,切分的位置通常位于中间);
  • 重新计算子集的包围盒;
  • 达到某种条件后中断,通常是node中的对象少于一定数量;
  • 将对象存储到各个节点中;

BVH的数据结构主要分为两类:内部节点和叶子节点。内部节点包括:包围盒,子节点;叶子节点包括:包围盒,对象集合;

BVH树的遍历:

Intersect(Ray ray, BVH node) {
	if (ray misses node.bbox) return;
	if (node is a leaf node)
		test intersection with all objects;
		return closest intersection;
	
	hit1 = Intersect(ray, node.child1);
	hit2 = Intersect(ray, node.child2);
	
	return the closer of hit1, hit2;
}

Radiometry

radiant energy: the energy of electromagnetic radiation. \(Q\ [J=Joule]\); 能量整体

radiant flux: the energy emitted, reflected, transmitted or received, per unit time. \(\Phi = dQ/dt\),单位是[W=Watt],或者是[lm = lumen]。单位时间的能量

另外几个关键概念,示意图如下:

Radiant intensity: the power per unit solid angle emitted by a point light source. \(I(\omega) = d\Phi/d\omega\),单位是[W/sr],或者是[lm/sr = candela]。单位时间单位立体角的能量;立体角的概念,微分,积分,如下:

Irradiance:the power per (perpendicular projected) unit area incident on a surface point.\(E(x) = d\Phi(x)/dA\)单位面积单位时间的能量(面积为垂直于光线方向);面积理解的示意图如下:

Radiance: the power emitted, reflected, transmitted or received by a surface, per unit solid angle, per projected unit area. 示意图及公式如下:

irradiance和radiance概念对比,irradiance表示的是单位面积接收到的所有能量,radiance表示,从某一个单位立体角看过去的单位面积上接收的能量。

Bidirectional Reflectance Distribution Function (BRDF)

BRDF: 用来表示从每个方向过来的光线,有多少会贡献到每个outgoing方向。represents how much light is reflected into each outgoing direction \(\omega_r\) from each incoming direction. 对应的公式如下:

上面的公式中为什么需要对Lr做微分,对Ei做微分呢?具体解释可以参见:关于BRDF公式理解的笔记_fatever的博客-CSDN博客,摘录关键内容如下:

反射方程

表示的不同方向的irradiance对出射方向上的贡献的积分。

渲染方程

在反射方程的基础上再加上emission项,得到渲染方程。

Understanding the rendering equation

图中,当一个光源的时候,用间接反射光取代了入射光。

Probability Review

随机变量的概率密度函数:\(P(a\le x\lt b) = \int_a^b p(x) dx\)。p(x)即为概率密度函数。从负无穷到正无穷的图形对应的面积为1。对应的期望为\(E(X) = \int xp(x)dx\).

Monte Carlo Integration

蒙特卡罗方法(Monte Carlo Method)概述——概念、起源、思路、应用、特点、计算程序、计算举例和文献资料(21k) (sohu.com)

蒙特卡洛光线追踪技术_Dezeming的博客-CSDN博客

如果需要求解定积分,\(\int_a^bf(x) dx\),首先需要知道x的概率分布,那么可以转换成求解积分,\(\int_a^bf(x)/p(x)\cdot p(x) dx\),那么用蒙特卡洛进行期望估计,得到:

Path Tracing

Whitted-Style Ray Tracing的条件是:总是执行镜面反射和折射;碰到漫反射表面停止。这种近似对于一些场景会存在问题:1)对于glossy reflection表面;2)在diffuse材质,不会有反射;

之前给出的rendering equation是正确的,那么如何求解呢?

A Simple Monte Carlo Solution

问题:假设我们需要渲染下面场景中一个点(仅考虑直接光照):

那么可以得到如下的反射方程:

\[L_o(p,\omega_o)=\int_{\Omega^+}L_i(p,\omega_i)f_r(p,\omega_i, \omega_o)(n\cdot\omega_i)\mathrm{d}\omega_i
\]

要求解这样的积分,我们利用蒙特卡洛积分,构建“f(x)”,为:\(L_i(p,\omega_i)f_r(p,\omega_i, \omega_o)(n\cdot\omega_i)\),并且先假设\(\omega_i\)的概率密度函数为\(p(\omega_i)=1/(2\pi)\),半球上均匀分布,那么可以得到:

如果在光线追踪的时候,一条光线hit到了物体,而没有hit到light,需要怎么处理呢?那么首先需要计算hit到物体的位置,贡献的光是多少。如下:

shade(p, wo)
	Randomly choose N directions wi~pdf
	Lo = 0.0
	For each wi
		Trace a ray r(p, wi)
		If ray r hit the light
			Lo += (1/N) * L_i * f_r * cosine / pdf(wi)
		Else If ray r hit an object at q
			Lo += (1/N) * shade(q, -wi) * f_r * cosine / pdf(wi)
	Return Lo

这样就ok了么,并不然。上述的处理方式还存在什么问题呢?

问题1:光线次数随着碰撞次数的增多而指数增长

问题1:光线次数随着碰撞次数的增多而指数增长。如下图所示:

为了避免光线数量的暴增。随机只选择一条光线进行处理。如下:

shade(p, wo)
	Randomly choose 1 directions wi~pdf
	Lo = 0.0
	Trace a ray r(p, wi)
	If ray r hit the light
		Lo += L_i * f_r * cosine / pdf(wi)
	Else If ray r hit an object at q
		Lo += shade(q, -wi) * f_r * cosine / pdf(wi)
	Return Lo

这种方式的话,如果一个像素上只有一条光线穿过,那么得到的结果噪声会很大,因此需要多条射线穿过一个像素位置,然后对结果进行平均。这样针对一个像素位置的伪代码如下:

ray_generation(camPos, pixel)
	Uniformly choose N sample positions within the pixel
	pixel_radiance = 0
	For each sample in the pixel
		Shoot a ray r(camPos, cam_to_sample)
		If ray hit the screen at p
			pixel_radiance += 1 / N * shade(p, sample_to_cam)
	Return pixel_radiance

问题2:此时shade函数如何终止

问题2:此时shade函数中还存在一个问题,就是函数进行无限循环。解决方案是提供轮盘赌的方式(Russian Roulette)。假设我们手动设置一个概率值,P,在发射一条光线的时候,给出一个随机值,如果范围在P~1,那么返回:0,否则返回:Lo/P。这样得到的Lo的期望为:E = P * (Lo/P) + (1-P)*0 = Lo。将这部分内容加到shader伪代码中如下:

shade(p, wo)
	Manully specify a probability P_RR
	Randomly select ksi in a uniform dist. in [0, 1]
	If (ksi > P_RR) return 0.0
	
	Randomly choose ONE direction wi-pdf(w)
	Trace a ray r(p, wi)
	If ray r hit the light
		Return L_i * f_r * cosine / pdf(wi) / P_RR
	Else If ray r hit an object at q
		Return shade(q,-wi) * f_r * cosine / pdf(wi) / P_RR

问题3:如何提高采样效率

上述的实现并不是很高效,如下图所示:

如果光源面积比较大,那么发射较少数量的光线,就可以碰撞到光源,如果光源面积比较小,那么需要发生很多的光线才行。这样如果我们在半球表面进行均匀采样,那么会有大量的光线是wasted的。那么怎么解决呢?

可以将采样分为两部分,1)针对光源进行采样,2)针对非直接光照场景进行采样;

从渲染方程得知,接收到的光照信息,是针对立体角进行积分的,那么怎么转换到,通过光源面积进行积分呢?从立体角的定义得到:

\[dw = \frac{dA\cos\theta’}{||x’-x||^2}
\]

那么渲染方程可以重写为:

此时shade伪代码可以写为:

shade(p, wo)
	# Contribute from the light source
	Uniformly sample the light at x' (pdf_light = 1/A)
	# test blocked
	Shoot a ray from p to x'
	If the ray is not blocked in the middle
		L_dir = L_i * f_r * costheta * costheta' / |x' - p|^2 / pdf_light
	
	# Contribute from other reflectors
	L_indir = 0.0
	Test Russian Roulette with probability P_RR
	Uniformly sample the hemisphere toward Wi (pdf_hemi = 1/2pi)
	Trace a ray r(p, wi)
	If ray hit a non-emitting object at q
		L_indir = shade(q, -wi) * f_r * costheta / pdf_hemi / P_RR

Materials and Appearances

Materials == BRDF。BRDF用来表示从每个方向过来的光线,有多少会贡献到每个outgoing方向。

Snell’s Law

传输方向从i到t,那么如果\(\eta_i/\eta_t > 1\)可能发生完全内反射(即,没有折射的现象)。

Fresnel Reflection

用来解释:视线垂直于表面时,反射较弱,而当视线非垂直表面时,夹角越小,反射越明显,这一种现象。

Fresnel Reflection – 菲涅尔反射 – Tekkaman – 博客园 (cnblogs.com)

准确解和近似解分别如下:

微表面

微表面,针对每一个元素都可以视为mirror。

没有覆盖到的内容

  • uniformly sampling the hemisphere; How?
  • Monte carlo integration allows arbitrary pdfs. What’s the best choice?
  • Do random numbers matter ? Yes! (low discrepancy sequences)
  • I can sample the hemisphere and the light. Can I combine them? Yes! (multiple imp. sampling)
  • The radiance of a pixel is the average of radiance on all paths passing through it. Why? (pixel reconstruction filter)
  • Is the radiance of a pixel the color of a pixel? No. (gamma correction, curves, color space)
  • deeper path tracing.