概述
GAMES101 13-16笔记 光线追踪
- 光线追踪(基于AABBs)
- 光和表面的交叉点
- 隐式表面
- 三角形
- 光和表面交叉点的加速计算
- 统一的空间分区
- 网格法
- 空间划分和KD-Tree
- 物体划分和BVH(Bounding Volume Hierarchy)
- 辐射度量学
- 相关概念
- BRDF
- Rendering Equation
- 概率论
- 相关概念
- Monte Carlo Integration 蒙特卡洛积分
- Path Tracing 路径追踪
- 其他相关
光栅化(Rasterization)速度快但质量差,光追则相反。
光有三条规律(虽然有些其实是错的):
- 光沿直线传播
- 光线相互交叉也不会碰撞
- 光线从光源进入眼睛(但物理上根据可逆性其实也可以反着理解)
1968年 Appel提出,两个步骤:
- 通过每像素投射一条射线生成一幅图像(从眼睛过像素到物体做一条线,对应物体上的一点)
- 通过向光线中发射光线来检查阴影(检查该点到光源的线段上有没有遮挡,没有的话就形成了完整的一条光路)
光线追踪(基于AABBs)
如上图,一般只取第一个接触到的点(mindistance),但若能折射反射继续与光源形成路径,这说明该第一个点的颜色构成是受所有后续点(一般能量已受到损耗)的影响的,因而也需要计算进去。
光和表面的交叉点
光线的表达式由一个点和一个向量表示:
隐式表面
以物体表面为球面(方程为:P: (P - c)2 - R2 = 0)为例,两者的交叉情况如下:
其他隐函数表现也同理。
三角形
如果和所有三角形计算有无交叉点(0个或1个)的话会非常慢。
将三角形延伸到一个面,也可以由面上一点和法向量来表示面上所有点(方程为:P: (P - P’)·N = 0)。
结合光线本身的方程,与上述表面同理可得:
还有一种更快的方法:
Moller-Trumbore 算法
通过三个点重心坐标方程相关来组成一组线性方程组(三行三个未知数),求解即可,判断条件是 t, b1, b2, 1-b1-b2 都大于等于0。
其中t是tNear。
光和表面交叉点的加速计算
用bounding box:
AABB所谓的轴对齐是指这六个面都是跟坐标轴对齐的,而不是斜着的。
使用AABB最关键的点在于不同视图(不同pair的面)的进入和出去时间点,以下图为例:
前两张图分别是两个视角下与一对面的两个面的接触情况,两者取交集就得到第三张图即准确的与bounding box的接触时间差。
更具体的来说,与bounding box第一次接触(进入)的时间是三对进入时间中的最晚者,第二次接触(出去)的时间是三对出去时间中的最早者,因而:
tenter = max{tmin},texit = min{tmax}
若 tenter < texit ,则说明光线在bounding box中停留了一段时间。
此时我们进行分类讨论:
- texit < 0 说明box在光线后面,也就是没有交点
- texit >= 0 且 tenter < 0 说明光源就在box里面,也就是必有交点
综上所述,光线和AABB当且仅当以下情况才会有交点:
tenter < texit && texit > 0
回过头看,AABB的优点也就在于其位置比较规整,比如判断垂直于x轴的一对面时只需要考虑光线方程的x部分即可:
极大的简化了计算。
统一的空间分区
网格法
- 找到Bounding Box
- 建立网格
- 存储每个物体(表面)所占的网格
- 对光线经过的每个网格进行测试:如果该网格是物体表面(可以不止一个物体)所占,则进行判断是否在物体内部:
)
空间划分和KD-Tree
针对网格太繁琐,因而对bounding box分块进行判断。
- 八叉树
- KD-Tree
- BSP-Tree
其中重点是KD-Tree,其结构如下:
实际上不一定要像图中这样完全1/2这样划分下一个块,而且可以横着或竖着,但因为平行/垂直划分, 所以整体上划分的边界也肯定是平行/垂直于坐标轴的。
中间节点是不存储任何物体的,实际上是完全划分的一个完全二叉树,只有叶节点才存储物体列表。
当判断一条线(光线)与物体是否有交叉时:
从上往下首先判断大块(第一个块是bounding box)是否交叉,若有,则对其下划两个块判断有无交叉,直至叶节点(叶块),此时对这些有交叉的叶节点中的所有物体判断和该光线是否有交集(即光与物体表面是否有交点)。
过程中若需要求出最近的交点只需要一直记录最小值即可。
KD-Tree已不再实用,主要有两个原因:
- 关于判断物体是否在某块内部或与某块有接触(即需要判断光线与物体是否有交点)的算法很难设计(比如三角形面内有一个立方体小块,这时候依赖三个点去判断三角形面是否在块内就很不方便)。
- 一个物体可能存在多个块(叶子结点)中,并不直观和方便。
物体划分和BVH(Bounding Volume Hierarchy)
应尽量选择交叠部分小的分发,这方面也有很多相关研究。
具体如何划分:
- 首先要选择一个维度去划分(x, y, z)
- 划分方法1:选择最长的轴去划分,比如整体上是长条形,长的方向是x轴,就按x轴划分
- 划分方法2:用中间的物体去划分(尽量使得两边分的块比较均衡),可以采用某条轴中位数等方法,最快可以快速排序O(n)。
- 终止的判断是当节点(块)中只含有几个元素(三角形面)时可以终止,比如只有5个三角形面。
存储上和上面KT-Tree一样,只存储在叶子结点。
光线和BVH的交点判断算法:
辐射度量学
建议参考Real-time Rendering的这篇笔记):
相关概念
-
Radiant energy
辐射能量,Q表示,单位为瓦 -
Radiant Energy and Flux(Power) 辐射通量
单位时间通过的辐射能量
-
Radiant Intensity 辐射强度
每单位立体角的辐射通量
其中ω代表立体角,其定义如下:
这个sinθ说明微分立体角对整个球面并不是均匀划分的。
对整个球面的单位立体角积分可得面积为4π。 -
Irradiance 辐照度/辉度
入射表面的辐射通量,即单位时间内到达单位面积的辐射能量,或到达单位面积的辐射通量,也就是辐射通量对于面积的密度。
-
Radiance 辐射率
每单位投影面积和单位立体角上的辐射通量,单位是W·sr−1·m−2,瓦特每球面度每平方米。在光学中,光源的辐射率,是描述非点光源时光源单位面积强度的物理量,定义为在指定方向上的单位立体角和垂直此方向的单位面积上的辐射通量
将辐射率理解为物体表面的微面元所接收的来自于某方向光源的单位面积的光通量,因此截面选用垂直于该方向的截面,其面积按阴影面积技术计算。
结合上面所提的Irradiance(辐照度)和Intensity(辐射强度),其关系为:
辐照度(Irradiance)和辐射度(Radiance)其实就差了一个微分立体角。
两者其实不代表一定前者是入射后者是出射,所以译名纯属记忆,最好记英文。
这些关系带来公式的体现:
和Irradianc:
和Intensity:
Radiance和Irradiance的关系也可以用这个公式来表示(和上面的反过来):
BRDF
BRDF描述光如何从给定的两个方向(入射光方向l和出射方向v)在表面进行反射,精确定义是出射辐射率的微分和入射辐照度的微分之比。
因此可得反射等式:
某出射方向(摄像机)的辐射率等于所有入射方向的辐照度和BRDF相乘的积分。
该公式的难点在于入射的辐照度不好计算,因为实际情况中往往不止有来自光源的辐照度,还有其他表面反射过来的光。
渲染方程:
在BRDF的基础上加上本身自己可能发射的光。
Rendering Equation
对上述渲染方程,具体分阶段理解:
- 只有一个光源时:
- 多个光源时就用求和
- 是面光源时
4.是其他表面反射过来的光时
作为一个整体方程可以间写为:
进一步简写:
这样之后就可以进行数学处理求出L:
对比起来,光栅化(Rasterization)其实只能反映前两项(本身的光和直接光照)
实际例子中考虑的反弹照明越多画面越亮(但是会收敛到某个亮度)
概率论
相关概念
随机变量和其概率值(一般是离散的)
在连续情况下分析:
某点x对应的y(纵坐标)不代表概率,x周围很小的范围往上和y形成的对应的区域才是概率。
如果不是随机变量,而是随机变量的一个函数,则其定义和期望如下:
Monte Carlo Integration 蒙特卡洛积分
直观解释是:
对于一个定积分且其解析式很难表达出来,则在积分域内随机采样作为整个积分域的y(即假设成一个长方形的形状),多次采样取平均值。
正式定义如下:
基本的蒙特卡洛估计(假设概率完全均匀):
推广开来就是上上图的通用公式,从而只需要在积分域之间以一种方式采样且知道该方式的X ~ p(x) 也就是probability density function (PDF)即可。
它有一些规律:
- 采样越多、偏差越小。
- 对x采样的话,也要对x积分(虽然看起来是废话)
Path Tracing 路径追踪
起因:Whitted-Style Ray Tracing 是不完全对的:
- 总是进行镜面反射/折射
- 遇到漫反射就直接停止了
但是光线追踪中渲染等式是肯定正确的:
但是关于它的两个问题比较难: - 对整个半球面的积分
- 递归执行的问题
自然而言的,既然涉及到积分,就可以用蒙特卡洛(在不考虑自身反射项的情况下):
所以渲染等式就等于:
伪代码:
在P点wo方向上的着色结果等于:
注意到这是一个递归算法。
但是这样会导致光线爆炸,Nbounce 次,所以把N改成1就可以避免:
这也就是所谓的路线追踪。
但只取一个方向的话会很noisy,解决办法就是对每个像素追踪更多的路线然后再取平均,伪代码如下:
自此,渲染方程两个问题的第一个搞定了。只剩递归问题,因为上述算法是不会停止的,现实世界其实也是不会停的,所以到底光线反弹几次就算停止是一个问题(太少会造成能量损耗严重)。解决方案是Russian Roulette(RR):
- 自己设定一个概率P(0 < P < 1)
- 在概率P的时候,射出光线并使得着色结果(下次出射)为:Lo / P(更大了)
- 在概率1-P的时候,不射出光线也就是得到0
- 所以期望为:E = P * (Lo / P) + (1 - P) * 0 = Lo
也就是总体期望其实和原本是一样的,但会有一定概率不射出光线,从而解决永远反弹下去的问题。
伪代码如下:
自此两个问题都被解决,得到正确的Path Tracing算法。
其仍有问题,效率不够高:若SPP(sample per pixel)不够高效果就会比较差。
原因之一是对方向随机取样时完全随机,从而大部分光线射不到光源(尤其当光源本身比较小时),造成浪费。
因为蒙特卡洛本身就不要求一定要随机平均采样,所以事实上可以选择只对能射到光源的方向采样。
那么如果直接使得式子对光源积分,则蒙特卡洛这边好算,pdf = 1 / A (A是光源面积),但原积分是对立体角积分的,所以需要把这点转换到光源上去,从而使得dω变成dA:
这种情况下,原积分就变成了:
且PDF = 1 / A
综上,我们可以从两个角度考虑辐射度(Radiance):
- 光源(直接的,不需要RR的)
- 其他反射体(间接的,需要RR)
因而算法就变成了:
再加上一点点修改(因为直接到光源的情况有可能中间被其他物体遮挡,所以需要判断一下):
自此Path Tracing完全解决。
其他相关
以前Ray tracing一般就指Whitted-style ray tracing,但现在已经不同了,有更现代的方法:
一些path tracing的细节还没理解和讨论:
很喜欢闫老师的一句话:《我学了近十年渲染,到现在发现我什么都不会》
最后
以上就是斯文香菇为你收集整理的GAMES101 13-16笔记 光线追踪光线追踪(基于AABBs)光和表面的交叉点光和表面交叉点的加速计算统一的空间分区辐射度量学概率论其他相关的全部内容,希望文章能够帮你解决GAMES101 13-16笔记 光线追踪光线追踪(基于AABBs)光和表面的交叉点光和表面交叉点的加速计算统一的空间分区辐射度量学概率论其他相关所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复