RayTracing
Rasterization 不能处理全局效果 软阴影 多个光源等情况 快但质量不高
RayTracing 更精确但更慢 多用于离线 (实时光线追踪见RTR)
光线的假设:
- 光线一定沿着直线传播
- 光线之间无法碰撞
- 光线路径可逆,即从A发出的到B的光线,一定也可以从B发出到A(中途可发生反射和折射)
由光路可逆可以模拟从人眼发射光线的方法还原出所有的光路
从光源出发难以模拟那就反着从摄像机发射光线
Whitted-style光线追踪
Ray Casting
- 从人眼或摄像机向近投影平面上的每一个像素点发射一条光线到场景中
- 找到与场景物体的最近交点
- 连接交点和光源,判断这条连线之间是否有物体存在就可以知道该交点是否在阴影之中
- 根据不同着色模型计算着色情况写回像素中 (Blinn-Phong, Whitted-Style, Path Tracing)
利用Blinn-Phong模型进行局部光照模型计算那么它的效果与局部光照模型是一样的 需要真正的考虑全局效果
Recursive (Whitted-Style) Ray Tracing
不仅仅第一个相交的点贡献光给眼睛, 折射与反射之后再与物体相交的点也能贡献光 有光照射到其他物体,再沿着eye rays的反射或折射的光线方向传回人眼
直接光照,反射方向间接光,折射方向间接光 计算所有点的局部光照模型的结果,将其按照光线能量权重累加(递归) 得到全局效果的光照模型
注:
- 递归终止条件,比如说允许的最大反射或折射次数为10
- 每次反射和折射之后都有能量损耗,由系数决定,越往后的折射和反射光贡献的能量越小,这也是为什么在上文中提到根据光线能量权重求和
- 如果反射或折射光线没有碰撞到物体,一般直接返回一个背景色
- 经典的whited-style光线追踪遇到漫反射表面会直接利用blinn-phong模型计算颜色值返回,而不再递归下去
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 //伪代码
RayTracing (original_point, ray_direction, objects, depth)
{
if (depth > maxDepth)
return color (0,0,0);
if(IsHitObject (original_point, ray_direction,objects))
{
hitPoint =GetHitPoint();
normal =GetNormal();
reflectionDirection = reflect(ray_direction,normal);
refractionDirection = refract(ray_direction,normal);//如果没有折射,舍弃折射项
local_color = BlinnPhongShader(original_point,normal,light_position);
return local_color
+ k1 * RayTracing (hitPoint, reflectionDirection,objects,depth + 1)
+ k2 * RayTracing (hitPoint, refractionDirection,objects,depth + 1);
}
else
return background_color;
}
Ray-Surface Intersection (IsHitObject实现)
光线表示 r(t) = o + td (0<=t<$\infin$) 光线为射线 o起点d单位方向
- 光线与隐式曲面求交 (p是着色点
代入求解t - 光线与显示曲面求交
点如果在封闭形状内,向外打一条光线,得到的交点数量一定是奇数;如果在封闭形状外,则交点数一定是偶数 -> 遍历物体的所有三角形面,求交点数- 求光线与三角形所在平面的交点
- 判断交点是否在三角形内
- 快捷方法 Möller Trumbore Algorithm
将点的形式用重心坐标的形式表示,随后利用克莱姆法则求解线性方程组
推导: https://blog.csdn.net/zhanxi1992/article/details/109903792
- 求光线与三角形所在平面的交点
Accelerating Ray-Surface Intersection 加速光线追踪
判断光线与场景交点的时候,需要去进行所有三角形面与光线的求交,而且这仅仅是对一个像素而言 #pixels ⨉ # traingles (⨉ #bounces
Bounding Volumes
Axis-Aligned Bounding Box (AABB) 轴对⻬包围盒
光线与三对分别与x-axis y-axis z-axis垂直的平面相交
将先进入的交点(偏小的那个)记为 tmin, 后出去的交点(偏大的那个)记为 tmax( t 可以< 0 代表的是光线反向传播与对应平面的交点)
对三组tmin和tmax $\qquad t_{enter} =max\{tmin\},t_{exit} =min\{tmax\}$
当$t_{enter} < t_{exit}$的时候,光线所在直线一定在盒子中待过一段时间,也必然存在交点
Uniform Spatial Partitions (Grids) 均匀空间划分
AABB对只有一个极其复杂的单一人物模型和充斥着大量的细小模型效率提升有限
更好的划分场景形成不同的AABB
- Build Acceleration Grid
找一个包围盒均匀划分后在每个重叠小包围盒上存储物体模型信息 - Ray-Scene Intersection
利用bresenham算法判断出所有和光线相交的方格 若方格中存储有物体,再进一步与方格中的物体模型或是三角形面求交
最适合的场景就是空间中均匀布满了三角形面
格子的划分方法在大量均匀分布的物体上比较有效,然而在复杂空旷的场景中,会造成很多资源浪费
Spatial Partitions 空间划分
Oct-Tree 八叉树
每次将空间分为8个相等的部分,再递归的对子空间进行划分.当划分的子空间足够小或是空间中三角形面的数量很少的时候会停止划分。这种方法的显著缺点是,随着维度的上升划分的空间数量会呈指数级增长。n维空间对应$2^n$叉树KD-Tree
每次将空间划分为两部分,且划分依次沿着x-axis,y-axis,z-axis,依次递归划分,终止条件与八叉树类似 叶子节点存储对应空间的所有物体或三角面信息,中间节点仅存储指针指向两个子空间。
第一步判断光线是否与最外层的包围盒相交 如果相交进一步判断是否与对应的两个子空间相交 (图中做了简化,包围盒的左半边并没继续进行划分 实际上应该要划分的) 递归的执行这个步骤即可- 假设有一条光线射出
- 逐个对每个子树进行判断是否有与包围盒相交
- 若相交则对他的子树继续遍历知道将找到所有与光线相交的包围盒
- 将其包围盒下的物体查找交点
优点: 倘若光线与哪一部分空间不相交,那么则可以省略该部分空间所有子空间的判断过程
缺点: 判断包围盒与三角面的是否相交较难,因此划分的过程不是那么想象的简单,其次同一个三角面可能被不同的包围盒同时占有,这两个不同包围盒内的叶节点会同时存储这一个三角形面BSP-Tree
其与KD-Tree类似,唯一不同的是划分不再沿着固定一轴,可以任意方向划分,缺点自然是划分的空间没有规则性,求交困难。
Bounding Volume Hierarchy (BVH)
不再以空间作为划分依据,而是从对象的角度考虑Object partition,即三角形面
为了画图方便,只进行了左半部分的划分,右半部分其实同理
- 包围盒会重叠,但一个三角形面只会被存储在唯一的包围盒内
- 每次划分一般选择最长的那一轴划分 划分点选择所有三角面的重心坐标在对应轴坐标上的中位数进行划分
- 中间节点不存储物体三角面信息,只在叶节点中存储,终止条件可设定为当前包围盒内三角形数量足够少
1 | Intersect(Ray ray, BVH node) { |
PBR基础
Basic radiometry 辐射度量学
光线亮度的定义? 需要合适的物理量来描述光线
whitted-style 没有对漫反射的光线进行追踪 使用了Blinn-Phong这个不准确的经验模型
辐射度量学其实是对光照的一套测量系统和单位,它能够准确的描述光线的物理性质
光线的概念: Radiant energy, flux, intensity, irradiance, radiance
Radiant Energy and Flux (Power)
Radiant energy : Q[J = Joule] 辐射出来的电磁能量,单位为焦耳 类似功
Radiant flux(power) : $\Phi = \frac{dQ}{dt}[W = Watt][lm =lumen]^*$ 单位时间的能量 类似功率 一般偏向用radiant flux来衡量光线的亮度
sr:球面度,立体角国际单位
lm:流明,光通量国际单位
cd:坎德拉,光强单位,SI 7大基本单位之一
Radiant Intensity
Radiant itensity 从一个光源出发某一方向上的亮度
Irradiance 某一微小平面所接受到的光线亮度
radiance 一条传播光线所具有的亮度(不受传播方向影响而改变)
这里的亮度也可以理解为radiant flux
Radiant itensity:
$I(\omega) = \frac{d\Phi}{d\omega}$
$[\frac{W}{sr}][\frac{lm}{sr}=cd=candela]$
从光源发出的每单位立体角上的功率
立体角 solid angle $\Omega = \frac{A}{r^2}$
各向同性点光源 Radiant intensity:
$\Phi = \int_{S^2} I d\omega = 4\pi I$
$I = \frac{\Phi}{4\pi}$
irradiance
$E(x) = \frac{d\Phi(x)}{dA}$
$[\frac{W}{m^2}][\frac{lm}{m^2}=lux]$
每单位照射面积所接收到的power
当光线垂直照射平面时,照射到平面上的面积与光线本身的“宽度一致”。当光线斜着照射到平面时,此时的照射面积就不再是光线本身的“宽度”了,此时的照射面积A2=A/cosθ
可以解释Blinn-Phong模型中的垂直入射和距离衰减(光能衰减的并不是Radiant Intensity,而是Irradiance)
radiance
radiance就是指每单位立体角,每单位垂直面积的功率
像是Intensity和irradiance的结合。它同时指定了光的方向与照射到的表面所接受到的亮度
在irradiance中定义的每单位照射面积 在radiance中关于接收面积的部分是每单位垂直面积($\cos\theta$) $\qquad dA^{\perp} = dA\cos\theta$
准确的光线追踪与radiance关系非常大,渲染就是在计算radiance
Incident Radiance:每单位立体角到达表面的Irradiance $L(p,\omega)=\frac{dE(p)}{d\omega \cos\theta}$
Exiting Radiance:每单位面积离开表面发射的Intensity $L(p,\omega)=\frac{dI(p,\omega)}{dA \cos\theta}$
一个点(微分面积元)所接收到的亮度(irradiance),由所有不同方向的入射光线亮度(radiance)共同贡献得到
Radiance是某个单位面积向某个单位立体角辐射出去的能量,Irradiance是某个单位面积上接受到来自四面八方的能量
区别就在于辐亮度Radiance有方向的概念,而辐照度Irradiance没有
把半球面上的所有Radiance积分起来得到的就是Irradiance
Bidirectional Reflectance Distribution Function(BRDF) 双向反射分布函数
BRDF:Bidirectional Reflectance Distribution Function 双向反射分布函数
BSDF:Bidirectional Scattering Distribution Function 双向散射分布函数
BTDF:Bidirectional Transmittance Distribution Function 双向透射分布函数
BSSRDF:Bidirectional Scattering-Surface Reflectance Distribution Function 双向散射表面反射(次表面散射)分布函数
光线的反射: 一个点(微分面积元)在接受到一定方向上的亮度$(dE(\omega_i))$之后,再向不同方向把能量辐射出去$(dL_r(\omega_r))$
$dE(w_i)表示:从w_i方向射来的Radiance在到达反射点时,被吸收转化,然后得到的Irradiance \\
dL(x, w_r)则表示反射点向w_r方向反射出去的Radiance$
BRDF就是描述一个从不同方向入射之后,反射光线分布情况的函数 理想光滑表面会把入射光线完全反射到镜面反射方向,其它方向则完全没有。理想粗糙表面会把入射光线均匀的反射到所有方向
很容易求得单位面积从某一方向吸收了多少能量,而很难求出吸收之后辐射出去的Radiance分布情况 于是就定义一种函数来描述这种Radiance的分布
其实就是定义了一种比例(由材质决定) $\frac{吸收后向某立体角方向辐射出去的Radiance}{辐射前某单位面积dA接收到的Irradiance}$
反射方程
摄像机所接受到的$\omega_r$方向上的反射光,是由所有不同方向上入射光线的irradiance贡献得到的($L_i(p,\omega_i)\cos \theta_i d\omega_i$),而不同方向入射光线的irradiance对反射方向$\omega_r$的贡献程度则由物体表面材质决定,所以乘上了一个BRDF函数
得到完全正确的光线传播模型
入射光线的radiance不仅仅是光源所引起的,还有可能是其他物体上着色点的反射光线的radiance,恰好反射到当前的着色点p(即间接光照),同时其他物体上的反射光线的radiance依然也是由直接光照和间接光照构成,因此这与whitted-style当中的光线追踪过程十分类似,也是一个递归的过程
The Rendering Equation 渲染方程
$L_o(p,\omega_o)=L_e(p,\omega_o)+\int_{\Omega^+}L_i(p,\omega_i)f_r(p,\omega_i,\omega_o)(n \cdot \omega_i)d\omega_i$
在反射方程的基础之上添加了一个自发光项(Emission term)
$\cos \theta_i$用$n \cdot \omega_i$代替
- 点光源对一个点来说只有一个方向有入射光 没有积分
- 多个点光源的贡献全部求和即可
- 面光源相当于无穷多个点光源的集合,只需要对面光源所在的立体角范围进行积分
- 加入其它物体同样考虑成面光源 且将$L_i$视作$L_r$
而式子可简化为$l(u)=e(u)+\int l(v)K(u,v)dv$
离散化为$L=E+KL$
$L=(I-K)^{-1}E$
$L=E+KE+K^2E+K^3E+…$
K 为对光线进行反射的一种算子操作(由BRDF化来)
E为光源发出的光,KE则代表对光源反射一次的结果,即直接光照,那么前两项之和就是光栅化当中着色所考虑的结果,对于全局光照Global illumination来说,还考虑了$K^2E$,即一次弹射的间接照明,$K^3E$就是两次弹射的间接照明
Path Tracing
- Monte Carlo Integration 蒙特卡洛积分
目的: 当一个积分很难通过解析的方式得到答案的时候可以通过蒙特卡洛的方式近似得到积分结果
做法: 对函数值进行多次采样求均值作为积分值的近似
均匀采样的话,其实就相当于将整个积分面积切成了许许多多个长方形,然后将这些小长方形的面积全部加起来 还可以指定一个采样的分布p(x)来对被积分的值进行采样
N为采样次数,$X_i$为随机变量(采样值)这里除以pdf可以理解为一种加权,哪里采样的多哪里就多做平均
求均值的做法其实也是对期望的逼近
均匀采样:
若采样方式为均匀采样(采样pdf为均匀分布),则认为样本和采样值的乘积xf(x)
为单次采样的积分结果,随后多次采样取平均,得到最终近似结果
Whitted-Style Ray Tracing 做法是,光线在镜面反射表面弹射,而在漫反射表面停 不够正确
但上述得出的渲染方程是正确的
pathtracing主要解决raytracing中的漫反射(全局光照)问题
解出渲染方程的解主要有两个难点:
- 积分的计算
- 递归形式
- 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)d\omega_i$
得到的即为着色点p到摄像机或人眼的Radiance值 用Monte Carlo Integration可近似结果 $L_o(p,\omega_o)\approx\frac{1}{N}\sum_{i=1}^N\frac{L_i(p,\omega_i)f_r(p,\omega_i,\omega_o)(n \cdot \omega_i)}{p(\omega_i)}$
$p(\omega_i)=\frac{1}{2\pi}$(均匀采样)
只有当采样的方向$\omega_i$击中光源的时候,光源才会对该着色点有贡献1
2
3
4
5
6
7
8shade(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)
Return Lo - 考虑间接光照
当采样的ωi方向碰撞到了别的物体
对着色点P的贡献是在点Q的直接光照再乘上反射到该方向上的百分比 类似光线追踪的递归,不同在于该方法通过对光线方向的采样从而找出一条条可行的路径此时解出了渲染方程的积分值,也通过考虑直接光照与间接光照解决了递归 但还有几个缺陷1
2
3
4
5
6
7
8
9
10shade(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
- 首先舍弃自发光项且仅考虑直接光照
- Problem 1: Explosion of #rays as #bounces go up
p的着色需要q的环境光照信息,而q的环境光照信息又必须包含其他物体的环境光照信息,如此需要采样的光线数量会爆炸增长 => 每次只采样一个方向 N=1 (N!=1时称为分布式光线追踪但积分计算的结果会非常的noisy,虽然蒙特卡洛积分是无偏估计,但样本越少显然偏差越大 => 对每个像素重复多次寻找到多条路径,将多条路径的结果求得平均1
2
3
4
5
6
7
8shade(p, wo)
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)
Else If ray r hit an object at q
Return shade(q, -wi) * f_r * cosine / pdf(wi)
Return Lo
通过对经过像素的光线重复采样,每次在反射的时候只按分布随机选取一个方向,解决了只对经过像素的光线采样一次,而对反射光线按分布采样多次所导致的光线爆炸问题1
2
3
4
5
6
7
8
9//Very similar to ray casting in ray tracing
ray_generation(camPos, pixel)
Uniformly choose N sample positions within the pixel
pixel_radiance = 0.0
For each sample in the pixel
Shoot a ray r(camPos, cam_to_sample)
If ray r hit the scene at p
pixel_radiance += 1 / N * shade(p, sample_to_cam)
Return pixel_radiance Problem 2: The recursive algorithm will never stop!
递归没有出口 并不采用类似光线追踪当中设定反射深度显示的给出递归出口的方法
Russian Roulette (RR)(俄罗斯轮盘赌)设定一个概率P, 有P的概率光线会继续递归并设置返回值为Lo/P,有1−P的概率光线停止递归,并返回0。光线一定会在某次反射之后停止递归,并且计算的结果依然是无偏的,因为Radiance的期望不变(E=P⋆(Lo/P)+(1−P)⋆0=L0)
1
2
3
4
5
6
7
8
9
10shade(p, wo)
Manually 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- Problem 3: correct but not really efficient
由于只通过均匀采样选出一个方向,很少的光线可以hit光源,大多都浪费了
Sampling the Light
计算直接光照的时候改进为直接对光源进行采样 所有采样的光线都一定会击中光源
光源的面积为A,那么对光源进行采样的 pdf=1/A (∫pdfdA=1)
渲染方程的积分是定义在立体角上,定义在着色点的半球上的 不是定义在光源上的
渲染方程都是采样到哪个点就计算哪个点的立体角的积分,要求积分和采样是在一个域上, 这里在光源上采样,却还用着着色点的立体角微分,这显然是不对的
还要找到渲染方程中$d\omega_i$和dA的关系 由立体角定义
渲染方程改为
$L_o(x,\omega_o)=\int_{A}L_i(x,\omega_i)f_r(x,\omega_i,\omega_o)\frac{\cos\theta\cos\theta’}{\lVert x’-x \rVert^2}dA$
计算直接光照的积分值直接对光源采样(pdf=1/A)且不用RR,间接光照仍用先前的方法进行光线方向的均匀采样1
2
3
4
5
6
7
8
9
10
11
12shade(p, wo)
# Contribution from the light source.
Uniformly sample the light at x’ (pdf_light = 1 / A)
L_dir = L_i * f_r * cos θ * cos θ’ / |x’ - p|^2 / pdf_light
# Contribution 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 r hit a non-emitting object at q
L_indir = shade(q, -wi) * f_r * cos θ / pdf_hemi / P_RR
Return L_dir + L_indir - Problem 4: if the sample on the light is not blocked or not
计算直接光照的时候还需要判断光源与着色点之间是否有物体遮挡
只需从着色点x向光源采样点x’发出一条检测光线判断是否与光源之外的物体相交即可1
2
3
4
5
6# Contribution from the light source.
L_dir = 0.0
Uniformly sample the light at x’ (pdf_light = 1 / A)
Shoot a ray from p to x’
If the ray is not blocked in the middle
L_dir = ...
摄像机发出的光线第一次相交
- 交点是光源
- 交点是物体
- 向光源采样计算L_dir
- 向其他物体采样递归计算L_indir