光线追踪技术 清华大学 pdf_光影之美:路径追踪算法初探

9a0c0dfce9bbdf38880241ca0197e521.png
本文使用Zhihu on VSCode 插件发布。(这里吐槽一下知乎写markdown和latex的难用) 本文来自我的2020计算机图形学课程作业报告。原报告中为了对结果进行对比分析,有一些表格,但是知乎不支持表格,只好单独放图。 本次作业使用的是助教 @Ubp.a 提供的框架,膜一波助教。

仿照SIGGRAPH论文,在介绍之前放部分结果。

465dc80026eb5c28f534d95799f81bc6.png

f0801549db4f5686494574e73bc0670f.png

90a9747e7f55890f178f07276d0e72e2.png

目录

  1. 路径追踪
  • 原理介绍
    • 基于物理的光线渲染基础知识
    • 光线追踪算法
    • 蒙特卡洛采样方法
    • 路径追踪算法
  • 实现中的关键问题与解决方法
  • 测试结果

2. 环境贴图重要性采样

    • 原理介绍
    • 实现中的关键问题与解决方法
    • 测试结果

3. 参考文献

强烈推荐闫令琪老师的Game101-tracing4课程,路径追踪讲得非常清楚!

GAMES101-现代计算机图形学入门-闫令琪_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili​www.bilibili.com
09de088e083df1f621e4516cf5715761.png

一. 路径追踪

  1. 原理介绍
  • 基于物理的光线渲染基础知识(PBR: Physics Based Rendering)

为了能够使用计算机绘制出接近真实世界光影效果的图片,我们需要考虑物理上,光线是如何与物体相互作用然后进入人眼、造就色彩斑斓的美丽世界的。这里由于篇幅的限制,略去对几何光学、波动光学知识的介绍,仅介绍一些光线渲染技术中的物理概念。

    • 辐照率Radiance
      光线的Radiance被定义为光线单位面积单位立体角上的功率。空间位置
      上沿着方向
      的Radiance可以用符号
      表示。
    • BRDF函数
      现实世界中不同材质的物体对于光的反射模式千变万化。为了描述材质在空间位置
      对入射光线
      的反射情况,我们使用BRDF函数
      。BRDF函数的本质是描述漫反射物体在反射光线时,出射光线在不同出射方向上的能量分布。这里不对BRDF函数做具体的介绍,有一篇讲得很详细的文章链接在此:基于物理着色:BRDF
    • 渲染方程
      渲染方程是光线渲染的核心,它给出了对光线渲染物理模型的准确描述,满足渲染方程的光线渲染算法可以保证结果的物理正确性

      其中
      代表着物体接收到来自光源的
      直接光照的辐照率,
      代表着物体接收到的来自别的非自发光物体的反射光(
      间接光照)的辐照度,其公式如下:

      某一点的光照是来自直接光照与间接光照的叠加,如下图所示。可以看到在仅有直接光照的情况下,画面较暗,很多没有被光源直接照射到的物体的细节被隐藏在黑暗之中。而在有直接光照+间接光照的情况下,画面整体变亮,并且能够照亮左图并不能看到的物体细节。

7cac2b1c176306b8010e123b9961d3a6.png
    • 光线追踪算法
      光线追踪算法是光线渲染早期的一种重要的渲染方法。正如其名,其核心思想是让光线从相机出发寻找光源。准确地说,从相机平面上每一个像素点发射出一条光线,该光线在场景中将经过一系列反射/折射,最终到达光源或者背景。最终到达背景的光在该像素点处呈现背景相应位置的颜色(乘以路径上的一系列因子),而到达光源的光在该处呈现光源颜色(同样地,乘以路径上的一系列因子)。

fd051ddf67d0117e02dd842babaab6cd.png


其原始算法(Whitted-style)主要过程如下。

color trace(point p, vector d, int step)
     {
     	color local, reflected, transmitted;
     	point q; 
     	normal n;
     	if(step > max) return(background_color);
     
     	q = intersect(p,d,status);
     	if(status == light_source)
     		return(light_source_color);
     	if(status == no_intersection)
     		return(background_color);
     
     	n = normal(q);
     	r = reflect(q,n);
     	t = transmit(q,n);
     	local = phong(q,n,r); 
     	reflected = trace(q,r,step+1);
     	transmitted = trace(q,t,step+1);
        return(local+reflected+transmitted);
     }


但光线追踪算法主要存在的缺陷有:

  1. 当光源面积较小时,从相机发射出的光线最终到达光源的概率随之变小,导致很多光线因为无法到达光源被浪费,形成大量噪点。如下图所示。

b294567b93c9a545e4d6f9c59bf350f8.png


2. 光线追踪算法实际上并不是在求解渲染方程,没有使用BRDF函数而是基于简单的光线反射/折射原理,因此结果的物理正确性不能得到保证。

为了解决这些问题,研究者提出了下面将介绍的基于蒙特卡洛采样方法的路径追踪算法。

  • 蒙特卡洛采样方法


蒙特卡洛采样方法是利用概率统计知识求解数值积分的一种方法。
问题描述如下:
我们需要求解如下所示的数值积分。


可以使用计算机通过采样的方法对该积分的值进行估计:

我们的目标是构造随机变量

,得到对
的一个无偏估计,即

如果我们已经知道了

式中的随机变量
的概率分布

则可以令
, 使得

这样,我们就得到了
的一个无偏估计。

但问题是:实际中,我们往往不知道

的概率分布函数
,只能猜测其概率分布函数。最常使用的猜想概率分布有均匀分布等。

假设我们共采样

个样本
,则我们就得到了对原积分的一个估计。

该估计满足:

根据不同采样点的重要性,我们还可以引入参数

,实现重要性采样。
满足:

则:

以上就是使用蒙特卡洛采样算法求解数值积分的核心思想。

  • 路径追踪算法


路径追踪算法是基于蒙特卡洛采样算法的光线渲染方法,其核心思想与光线追踪算法一致,也是让光线从相机出发寻找光源,但具体做法是:

从相机平面的每一个像素点发射出多条光线寻找光源,光线在与场景中物体相交,发生反射时,按照预先设定的概率分布函数从半球面选择一个方向出射,并按照BRDF函数分配出射光线的能量分布。其余部分与光线追踪一致。


路径追踪算法的本质是将多条光路对光照效果的贡献按照它们出现的概率结合在了一起,实现了渲染方程。
如下图所示。

bcede03ad542769d7181b253e66b89c6.png


值得一提的是,为了能够让算法中的光线反射的迭代能够终止,我们可以引入Russian Roulette的技巧:通过每次迭代时生成一个随机数来决定是否进行下一次光线的反射,阻止了无限迭代的可能。
其算法过程如下所示。

shade(p, wo)
    Test Russian Roulette with p_rr, if fail then return 0.0;
    Randomly choose one direction wi with pdf(wi);
 	Trace a ray r(p, wi);
    if ray r hit the light:
        return L_i * f_r * cos / pdf(wi) / p_rr;
	else if ray r hit an obj at position q:
        return shade(q, -wi) * f_r * cos / pdf(wi) / p_rr;

ray_generation(camPos, pixel)
	Uniformly choose N sample postitions within pixel;
    pixel_radience = 0.0;
    for each sample in the pixel:
		Shoot a ray r(camPos, cam_to_sample_direction);
         If ray r hit the scene at p:
            pixel_radience += 1/N * shade(p, cam_to_sample_direction);
	return pixel_radience;


但值得注意的是,路径追踪算法并没有解决当光源面积很小时,发射出的光线很难寻找到光源的问题。

解决这一问题的一个很自然的思路是:既然从相机出发发射光无法到达光源,那么能否直接从光源出发发射光到达物体,直接计算直接光照?根据这一思路,我们可以使用对光源采样的策略提高路径追踪算法的效率。

c2a9c73f9bd8f7fe5832fc8e76921db0.png


此时由上图中的几何关系,将在BRDF半球上的积分转换到面光源所在的平面A上:


这一步的本质是改变蒙特卡洛积分中的采样概率函数,在光源方向的立体角上采样,其余部分不采样。我们将之前的路径追踪划分为:采样直接光照(对光源采样)与采样间接光照(均匀采样)两部分。

改进后的路径追踪算法中shade的主要过程为:

shade(p, wo)
	// direct light, sampling the light
    Uniformly sample the light at x with pdf_light = 1/A;
    Check visibility, 
    if invisible 
         L_dir = 0;
    else L_dir = L_i * f_r * cos(theta1) * cos(theta2) / |x - p|^2 / pdf_light;
 
	// indirect light, BRDF sampling
    L_indir = 0.0;
    Test Russian Roulette with p_rr
    Randomly choose one direction wi with pdf(wi);
    Trace a ray r(p, wi);
    if ray r hit an non-emitting obj at position q:
        return shade(q, -wi) * f_r * cos / pdf(wi) / p_rr;

    return L_dir + L_indir; 

2. 实现中的关键问题与解决方法
原理部分已经把路径追踪算法的过程讲得比较清楚了,实现中其实没太大问题。这里可以提的一个问题是当前点是否在面光源背面的判断方法:可以通过判断cos的正负号可以判断点是否在面光源的背面,结果如下所示。

对这样一个图中的光源:

04889b68cf0bf74f4620b570001cb9fe.png

检查前(上图)后(下图)对比:

abac97b1f6ab9ab2d09c2823e6c65460.png

dd1157b45ea433a5fe546cc171833389.png

其他测试结果
康奈尔盒
从上到下依次是:1. 只有环境光的结果 2. 只有面光源的结果 3. 面光源+环境光 4. 面光源+环境光+间接光(最终结果)

610ef455684c618bc0c1586d450cd8c4.png

760b51d40ffba6230a62e1fe10f51d71.png

a43fb9a16f6050157fa855af3a00e5cd.png

1b686acef814b862eba8ecfa91b07883.png

星空下的球

321f314bd1650b0930cb58a27f23ea26.png


皇家超跑(个人作业中最满意的图)

465dc80026eb5c28f534d95799f81bc6.png


街头超跑

4cbc84d226aa21fb3b495e2b4018c687.png


斯矛革和它的宝物

59a0546007080c2ac71c333a25f1b58f.png


另一只镜面龙斯矛革

f0801549db4f5686494574e73bc0670f.png


不同材质(铜,金,银)的M4A1(模仿CSGO反恐精英中沙漠小城的场景)

335779b3736029aab1b38db479fb4a17.png

二. 环境贴图重要性采样

  1. 原理介绍
    环境贴图中,环境光源的分布往往是不均匀的。因此在直接光照中计算环境光的部分,我们可以对环境贴图按照像素的亮度进行重要性采样,如下图所示。

5d924d37121c2efdab6df16f56a42c82.png


构建抽样表可以使用别名法(这个参考资料讲得很好)使得采样的时间复杂度为

.

在别名法中我们将构建环境贴图中每个像素点的采样概率表
的转换关系如下:

其中
分别为环境贴图的宽度与高度,
与交点法向量的夹角。

(你若问我上面的式子为什么是这样的,请见文章深入理解 PBR/基于图像照明 (IBL)(助教写的2333)

2. 实现中的关键问题与解决方法

  • 使用别名法构建抽样表
    我使用了vector<pair<std::pair<int, int>, pair<int, int>> > env_light_alias_table_idx存放别名法中的像素点的下标,使用vector<pair<float, float>> env_light_alias_table_p 存放别名法中的概率表。
    采样时只需要生成两个随机数,为
    时间。
auto rand_n = int(rand01<float>() * (env_h * env_w - 1));
auto rand_p = rand01<float>();
std::pair<int, int> sample_idx;
if (rand_p <= std::get<0>(env_light_alias_table_p[rand_n]))
{
    sample_idx = std::get<0>(env_light_alias_table_idx[rand_n]);
}
else sample_idx = std::get<1>(env_light_alias_table_idx[rand_n]);
  • 环境贴图像素坐标与入射光线向量之间的转换
    转换过程:环境贴图像素坐标
    环境贴图纹理坐标
    球面角坐标
    入射光线向量

    关系为: (也是来自深入理解 PBR/基于图像照明 (IBL)这篇文章)

50febfac89bb3e2bcbd4b49b8613f74d.png

3. 测试结果
为验证环境贴图重要性采样的效果,进行了两组对比实验。
没有进行环境光重要性采样(对照组)

3ffa8fd6742004c464b2e2e9204d33c9.png

进行了环境光重要性采样(实验组)

245d2ce69ec7ef4dde8a27404669d997.png


夜晚环境中,去掉顶光源的环境:
没有进行环境光重要性采样(对照组)

745a9de05ac30c06f00ac0ba7a573802.png

进行了环境光重要性采样(实验组)

166ce5e09c556803b18044390fabc9fb.png

可以看到,相比于对环境光进行均匀采样,进行了环境光重要性采样后的画面某些地方比原来更暗(个人对原因的解释:由于环境光源分布的不均,这些地方确实应该更暗一些。但是可能这个解释是不对的,存疑)。

补充对面光源进行重要性采样 & 多重重要性采样
实际中仅使用BRDF采样或仅使用对光源的重要性采样结果都不能达到完美的效果,如下图所示。(图片来自文章浅谈PBR:从光学原理到基于物理的渲染)

2cdd972cdb94834b10bb046a6db60fb8.png


通过多重重要性采样可以解决上面的问题,如下图所示。本次作业由于时间关系没有进行相关的对比实验。

17c4f056bf956c967a4fcc04dbeae172.png

四. 参考文献

  1. 基于物理着色:BRDF https://zhuanlan.zhihu.com/p/21376124
  2. 《Real-Time Rendering 3rd》 提炼总结 第九章 · 全局光照:光线追踪、路径追踪与GI技术进化编年史 https://zhuanlan.zhihu.com/p/29418992
  3. 详解球面环境映射 - Spherical Environment Mapping https://zhuanlan.zhihu.com/p/84494845
  4. 深入理解 PBR/基于图像照明 (IBL) https://zhuanlan.zhihu.com/p/66518450
  5. 金属,塑料,傻傻分不清楚 https://zhuanlan.zhihu.com/p/21961722
  6. 一篇光线追踪的入门 https://zhuanlan.zhihu.com/p/41269520
  7. 时间复杂度O(1)的离散采样算法—— Alias method/别名采样方法 https://blog.csdn.net/haolexiao/article/details/65157026
  8. 多重重要性采样(很好的blog) https://airguanz.github.io/2018/10/15/multiple-importance-sampling.html
  9. 光线追踪器Ray Tracer:进阶篇(很好的图形学博主) https://yangwc.com/2019/05/23/RayTracer-Advance/
  10. 浅谈PBR:从光学原理到基于物理的渲染

因初学图形学,才疏学浅,文章中的任何问题还请大家指出,谢谢。

最后感谢助教 @Ubp.a 和课程老师刘利刚老师的倾情指导与课程同学们的帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值