1 实时阴影:光栅化
参考实时阴影技术总结
光栅化算法由于缺少全局信息,每个fragment不清楚全局光照的情况,因此实时阴影大部分是基于Shadow Map实现的。
需要预渲染储存深度信息,从光源渲染场景中每个点的深度值储存在深度纹理中,再从Camera视角出发开始渲染场景,与深度纹理一一比对以判断当前渲染fragment是否在阴影中。
简单的ShadowMap仅仅是硬阴影,实现软阴影就需要进一步对阴影边缘进行处理,按照最后的处理效果分类的话可以分为:
一致性:PCF、CSM、VSM、ESM距离相关(半影):PCSS
按处理方式可分为,
过滤(filtering)阴影贴图:PCF、PCSS预过滤(pre-filtering)阴影贴图:VSM、ESM屏幕空间模糊
我们已知基础ShadowMap的算法表达式:
其中,为渲染时物体在光源空间下的深度,为物体在Shadowmap采样获得的深度(遮挡物位置深度)。
1.1 PCF
Percentage-Closer Filtering,即百分比渐进过滤,这是一种很朴素的思想。不是要实现阴影边缘模糊吗?好,直接buff拉最大——直接对多重采样,即对进行滤波处理,阴影的质量通过控制采样数量(sample num)和采样步长(stride)决定。
缺点就很明显了,
硬上多重采样,算法效率低,场景复杂度和滤波核大小十分影响性能(当然如果是PC端的话用PCF就绰绰有余了,对性能不会有太大的限制)不支持预滤波处理——不能mipmap
但是,性能上的不足目前硬件越来越厉害了,其实是慢慢在优化的,其次PCF与之后提到的CSM、VSM那些相比不需要其他的纹理格式,至今还是最通用的软阴影解决方案。
1.2 CSM
Convolution Shadow Mapping,即卷积阴影,注意此CSM非彼CSM(Cascaded SM)。
基础Shadowmap记录的深度值要么是0要么是1对吧?0到1的突变就是造成阴影“硬”的原因,实现软阴影本质上就是让0到1突变别那么明显。CSM就是对的公式做了个fft(快速傅里叶变换),重构了shadowmap(截图自实时渲染中的软阴影技术):
注意:截图中的和跟本文前面提到的中的和意义是反过来的。
M要选择大于4的数,且M越大整个曲线将越接近原始样子,效果越好。
CSM与PCF相比优点在于,
由于只是对Shadowmap进行预处理,因此场景复杂度并不影响性能支持预滤波,即可以在CSM之前对depth map做一个预blur
不足在于,M越大,阴影质量越高,但同时效率也越低。
1.3VSM
Variance Shadow Map,即结合深度概率分布和切比雪夫不等式的方差阴影贴图,将阴影遮挡看作概率问题,使用单边切比雪夫不等式求得概率作为阴影值。
单边切比雪夫不等式
参考切比雪夫不等式到底是个什么概念?问题中各路大神的回答,简单总结一下:
切比雪夫不等式是刻画事物偏离它本质的偏离程度的大小的概率,将随机变量的期望和方差联系在了一起,不需要知道变量的整个分布,只需要知道标准差就可以估计出结果,其基本形式为,
式中,方差
是X的期望,是X的分布函数。
(单边的切比雪夫不等式)描述了大于某一常数的概率,
VSM计算思路
Shadowmap中2个通道分别储存和:跟ShadowMap思路相同,需要先从光源处做一次光栅化,但不同的是不仅需要储存深度还要储存深度平方对Shadowmap进行滤波:对和通道进行模糊处理(参考文章用的是box filter),获得期望深度和深度平方采样当前着色点在Shadowmap中对应的期望深度和深度平方,并计算方差,求出光源空间下的深度:比较和,如果更小,那么无遮挡,直接返回1,如果更大,进行下一步对应式中就是求出深度偏移期望深度的概率,令,则优点:效率高
与PCF相比,但效率更高(通过方差避免暴力采样
与CSM相比,VSM开销更小
问题:漏光、内存大
关于这个漏光问题,参考影子传说——三种Shadowmap改进算法的原理与在Unity中的实现,解决方案是重映射,然而映射越厉害,阴影就越硬,相反漏光减少。
由于VSM需要真用更多的内存——需要MRT额外储存,需要两个float通道储存数据,移动端应该吃不消
补充:
1.4ESM
Exponental Shadow Mapping,即用指数函数替换掉原表达式中简单的相减,实现方法如下(截图自参考):
与CSM相比ESM表达式更加简洁,效率也更高,如下图(截图自参考),该作者验证了32位float下ESM的c取80效果最佳,且生成阴影比M=16的CSM生成的阴影更好:
pre-filtering
基础的ESM仅仅解决了软硬问题,,而边缘锯齿依然存在!因此肯定要额外进行滤波的,那么到底是pre-filtering还是filtering?该参考文章的作者又进行如下证明:
对于使用指数表达式的ESM来说,ESM处理后再对结果进行filtering和pre-filtering完全是等价的,而pre-filtering(MSAA、高斯模糊效果都不错)当然比filtering要好得多!
优点
支持pre-filtering,与VSM相比ESM只需要一个float通道,无需MRT。
问题
ESM的不足在于,虽然锯齿问题完全可以pre-filtering解决,但软阴影质量极大受到c值影响:c值小了会漏光,c值太大“软”得不明显。PC端完全可以直接PCF,而移动端32位float精度又不够,具体可以看看这篇文章有带图片分析。
1.5 EVSM*
Exponential Variance Shadow Mapping,EVSM是对VSM的一种改进,具体可以看影子传说——三种Shadowmap改进算法的原理与在Unity中的实现
1.6 动态阴影:PCSS
Percentage-Closer Soft Shadow,即百分比接近软阴影。这个与上面几种方法解决的问题不一样,这个解决的是阴影的一致性问题,即实现动态阴影。
具体实现方法可以参考之前我的202作业记录:
GAMES202作业1-实现过程详细步骤,放上一张最后的效果:
2 实时阴影:光线追踪
2.1 ShadowMap的不足
我发现很多人在比较ShadowMap和光追阴影的时候总喜欢拿软阴影来说事,虽然传统的ShadowMap无法实现软阴影,但PCF、PCSS可以呀!感觉硬伤其实是在精度问题:无论用何种算法,只要是基于ShadowMap思想的一定逃不过需要图来储存深度信息(VSM甚至需要两张),而图,就一定会涉及到分辨率问题——低了看不清,高了性能上不去;还有抗锯齿问题。
随着场景越来越大,ShadowMap这种问题一定会越来越明显。
2.2 考虑遮挡
光线追踪算法的单一代码路径可以处理所有的阴影场景,只需在视线发出光线与场景中物体求交时,做一个距离上的对比,挡住则不计算光强,拿101光追框架看,光追代码中如何考虑遮挡:
实时光线追踪技术:业界发展近况与未来挑战文中的3.4就介绍了Ray Tracing Shadow。
关于rtShadow就先谈到这里,后面有机会会继续补充。
参考
实时渲染中的软阴影技术 - 知乎 ()
影子传说——三种Shadowmap改进算法的原理与在Unity中的实现 - 知乎 ()
【论文复现】Variance Shadow Maps - 知乎 ()
单边切比雪夫不等式的总结 - 知乎 ()
实时光线追踪技术:业界发展近况与未来挑战 - 知乎 ()
RayTracingShadow/ScreenSpaceDenoiser 实现概述 - 知乎 ()