一、基于图像的阴影技术(Shadow Map)
什么是阴影
当来自光源的至少一个点在空间中被遮挡时,就产生了阴影区域。
阴影的前提
- 直接光照
- 不透明物体
阴影的实现方式
阴影体(Shadow Volumes)——空间中黑暗部分的几何
阴影映射(Shadow Map)——在场景中没有背光源找到的地方
阴影映射(Shadow Map)
绿色的点没有阴影。
红色点在摄像机视角看是在阴影里,但在光源的视角看在盒子后面,不代表一定在阴影中(例如多光源情况)。
深度比较
相机视角渲染的深度图与光照视角渲染的深度图进行比较。
阴影映射-问题
- 低分辨率的阴影映射会导致有锯齿的阴影。
- 当比较深度时,为了避免表面自阴影,需要设置容错阈值(深度偏移)。
二、基于图像的阴影技术的优化
深度偏移(Depth Bias)
传统Shadow Map因为自阴影出现的问题——阴影失真(Shadow Acne)
红色斜着的区域对应了ShadowMap上每块片段(像素)的位置,因为阴影贴图受限于分辨率,在距离光源比较远的情况下,多个片段可能从ShadowMap的同一个值中去采样。图片每个斜坡代表ShadowMap一个单独的纹理像素。你可以看到,多个片段从同一个深度值进行采样。有些在地板上面,有些在地板下面;在和屏幕空间深度做比较的时候就会出现误差,这样我们所得到的阴影就有了差异。
我们可以用一个叫做阴影偏移(shadow bias)的技巧来解决这个问题,我们简单的对表面的深度(或深度贴图)应用一个偏移量,这样片段就不会被错误地认为在表面之下了。
举个栗子:
这四个fragment分别取名a,b,c,d,由于光源的位置会导致求得的四个距离会不一样,先求a到光源的距离假设为9.8<10,b到光源的距离是11.6 >10,c到光源的距离12.1>10,c到光源的距离9.5<10,。就会导致a和d亮,b和c暗,他们本来应该都是亮的才对。然后整个光源视椎体下各个片段都会出现这个问题,就会出现上面的明暗交错的条纹。
如果Bias值过大也会导致一个问题——悬浮
Unity中的自阴影优化
与OpenGL优化原理差不多。
阴影映射的走样
人们期望,正常情况下Shadow Map上的像素可以对应上屏幕空间的像素,这种情况仅在目标Light垂直于目标物体(平面)时才会出现,但通常并不会满足上述条件。
因此人们开始针对此类问题进行优化,主要优化点如下:
- 初始采样(渲染阴影映射)
- 重采样:从摄像机视角对采样信号(阴影映射)重采样
- 重建:使用滤波函数(例如PCF)
主要误差类型
解决方案:
- 初始采样——透视走样
- 光空间透视阴影映射 (LiSPSM)
- Z分割(级联阴影映射)
- 视口采样映射
小结
- 只能使用一张阴影映射时,LiSPSM是最好的选择,尤其是户外顶部光照
- 允许多张阴影映射时,最好的方案是Z分割(CSM等)
- 自分割可以同时处理透视与投影走样,RMSM与现值虚拟纹理的方案很类似,支持虚拟纹理的引擎很容易实现
- 无走样方案可以达到像素级别的精度,应该会是未来的趋势
硬阴影的滤波
- 主要用于减少重采样的误差(使用阴影映射进行渲染时产生)
- 滤波
-
- 图像处理中可以通过滤波强调一些特征或者去除图像中一些不需要的部分
- 滤波是一个邻域操作算子,利用给定像素周围的像素的值决定此像素的最终的输出值
- 任何使用一部分阴影映射采样点来计算某个指定View采样点的最终阴影结果的方法
PCF(Pencentage close Filtering)滤波
- 可以模糊来隐藏欠采样的瑕疵;
- 得到类似软阴影的效果,不少游戏引擎称之为“软阴影”;
- 一个重要缺陷是较大的滤波核会导致更多采样点的计算,性能消耗大;
- 无法进行预计算,因为滤波依赖于
- PCF 虽然能对阴影进行平滑模糊,但毕竟无法模拟基于物理的软阴影, PCSS(Percentage Closer Soft Shadows)能够设置变化的滤波核来改进这一点。
三、基于图片的软阴影算法
流程:
1.查找遮挡体
2.半影宽度的计算
Wpenumbra=P sz- Zavg / Zavg * Wlight
Wlight为光源的大小
3.滤波
Wf = Znear / PSZ *Wpernumbra