[toc]
# 全局光照的定义
一次弹射是直接光,多次弹射是间接光,直接光加上所有的间接光的和就是全局光照 其中积分形式的渲染方程定义如下
# 概率密度函数 PDF (Probability Density Function)
# 蒙特卡洛解复杂积分 (数值)
定积分
- 如果一个积分表达式很难得到解析式,那我们如何得到积分的值呢?
这就用到了蒙特卡洛方法。
# 黎曼积分
黎曼积分是将函数划分为无数小的矩形 (二维) 来进行累加和求解积分数值
# 蒙特卡洛方法
使用概率的方式采样曲线,在积分域内不断采样 x, 设采样的 y 值为积分域内的高,面积为积分的值,采样无数次之后平均就是积分的值 如果均匀的采样,则每次采样的蒙特卡洛积分的 PDF 是一个常数 蒙特卡洛积分的公式如下: 即每次采样的值除以 PDF 的累加和的平均就是数值解 由上述我们知道,二维下的蒙特卡洛的 PDF 实际上就是 1/b-a 假设采样的结果是每次采样的和 (也可以理解为积分),则最后积分的数值解就是: 上面的式子就可以用一个很直观的方式来理解,即每次采样区间是 b-a/N, 采样值是目标点的函数值,对应的矩形就是采样区间内的数值近似解
# Path Tracing
如何使用蒙特卡洛方法来解决全局光照渲染方程对球面积分的问题? 答:
在球面上选择几个方向,进行蒙特卡洛近似计算即可 最简单的 PDF 采样方法 (均匀): 半球面积是 2Π 蒙特卡洛渲染方程: 直接打到一个光源伪代码:
1 | shade(p,wo) |
因为全局光照等于直接光 + 所有间接光的和,如下图,所以当我们计算射线打到物体表面时的光照应该是一个递归表达式 递归伪代码如下:
1 | shade(p,wo) |
上述代码有一个很明显的问题,就是如果每次射线都随机选取 N 个方向,则当递归次数躲起来的时候,就是指数级的爆炸,而路径追踪是选取 N=1 来防止递归爆炸的问题的
1 | shade(p,wo) |
pathTracing 即从像素打出到物体无穷递归,优化到从一像素点打出很多路径求平均 如上图的红、黑、蓝就是三个路径 pathTracing 的上层函数伪代码如下:
1 | ray_generation(camPos,pixel) |
其中 shade函数是N=1的rayTracing
但是上述代码还有一个问题,就是我们无法确定 RayTracing 什么时候会停下来,最坏的方式是限制递归的层数 (即反弹的次数)
那么怎样能使我们即可以让最后的结果期望值为 L0 (正确的 Lumen),
这就引出了下一个方法: 俄罗斯轮盘赌
可以发现,这个公式最后的期望就是 LO 即自己拟定一个固定的概率 P
对于每次发出射线,都使用概率判断是否发射,如果允许,则返回 LO (当前的 LO)/P, 否则返回 0 但最后结果就是可能对的 = = 改善后伪代码如下:
1 | shade(p,wo) |
但是上述代码并不是很高效和有效的,因为返回正确流明完全看运气 = = 改善的方式是: 修改采样用的 PDF 核 如:直接在光源上采样,但是由于现在的积分是在物体半球上的积分,所以需要将蒙特卡洛方程改写成在光源上面积微分的积分 如上图,光源是 luminaire
球面表面的立体角微分和 dA 的转换关系就是 dw 是光源 dA 照到球面的面积对应的立体角 即先把 dA 对准球体,然后计算出立体角即可 这个公式就是这个意思,如此一来,就可以改写成对光源的积分
# 对物体球面积分改写成对光源积分的形式
因为改写成了对光源的积分,所以我们可以将对物体表面的光的贡献拆分
1. 光源对表面的贡献,使用上面的对光源的积分公式 (不需要俄罗斯轮盘赌)
2. 其他物体对表面的贡献,还是使用之前的方式 (需要俄罗斯轮盘赌) 伪代码如下:
1 | shade(p,wo) |
在计算光照的时候,需要判断下光源是否被挡住了,如果挡住了,就直接返回 0 就行