在实时阴影渲染中shadowmap是一种颇具潜力的算法,相比shadow volume算法具有实现简单、适应范围广的优点,而且shadowmap是基于图像的算法,在实现软阴影上具有先天优势,下面我们就来看看如何用shadowmap实现软阴影效果。
通常的shadowmap算法产生的阴影具有生硬的边缘,而且由于shadowmap是基于图像的算法,在shadowmap分辨率不足时会产生比较严重的混淆,也就是锯齿现象。在3D空间中,我们可以任意的贴近观察阴影,因此无论怎么提高shadowmap的分辨率,都很难消除难看的锯齿。对于由图像拉伸产生的锯齿,我们很容易想到使用插值过滤的方法来进行平滑过渡,但是遗憾的是直接对shadowmap采用插值过滤并没有意义,因为shadowmap中保存的是一个深度值,对深度做插值得到的还是一个深度值,而不能得到真正有用的灰度级。
让我们再看一下shadowmap的工作原理:首先在light空间中渲染一张shadowmap深度图,然后在相机空间中渲染灯光pass,将当前渲染的象素点变换到shadowmap空间中进行深度比较,因此实际上我们得到的是一张象素是否在阴影中的二值图像,因此我们只需要对比较后的二值结果进行插值就可以得到具有灰度级过渡的软阴影。具体的做法是在shadowmap上取相邻4个点,对深度的比较结果做双线性插值计算,得到的结果如下:
通常的shadowmap算法产生的阴影具有生硬的边缘,而且由于shadowmap是基于图像的算法,在shadowmap分辨率不足时会产生比较严重的混淆,也就是锯齿现象。在3D空间中,我们可以任意的贴近观察阴影,因此无论怎么提高shadowmap的分辨率,都很难消除难看的锯齿。对于由图像拉伸产生的锯齿,我们很容易想到使用插值过滤的方法来进行平滑过渡,但是遗憾的是直接对shadowmap采用插值过滤并没有意义,因为shadowmap中保存的是一个深度值,对深度做插值得到的还是一个深度值,而不能得到真正有用的灰度级。
让我们再看一下shadowmap的工作原理:首先在light空间中渲染一张shadowmap深度图,然后在相机空间中渲染灯光pass,将当前渲染的象素点变换到shadowmap空间中进行深度比较,因此实际上我们得到的是一张象素是否在阴影中的二值图像,因此我们只需要对比较后的二值结果进行插值就可以得到具有灰度级过渡的软阴影。具体的做法是在shadowmap上取相邻4个点,对深度的比较结果做双线性插值计算,得到的结果如下:
可以看到尽管做了插值得到了一些灰度级,但是总体效果并不理想,阴影的锯齿依然很严重,这个结果是可以预先的,因为实际上我们只是对一张二值图像进行插值,由于二值图像不具有灰度,直接插值得到的还是一个较硬的边缘。如果想要得到具有平滑过渡的边缘我们可以采取超采样或者Blur对阴影边缘进行柔化。超采样的代价有点太高,于是我试验了一个3*3的Blur模板,得到结果如下:
Blur之后阴影带上了灰度级,但是还没有经过插值,效果还不是很理想,在对相邻四个采样点经过同样的blur计算之后再进行双线插值,产生结果如下:
这个渲染结果已经足以令人感动了,得到了很高质量的软阴影效果。但是不幸的是,这个算法还是具有严重的缺陷,当阴影移动起来的时候,可以明显注意到阴影的抖动现象,这并不是由blur和插值引起的,而是由于shadowmap自身分辨率不足造成。要解决这个问题,我们必须保证足够的shadowmap分辨率,并且用更大范围的blur模板来进行大范围blur,但是由于blur计算过程中不能保存中间图像,因此无法把二维的blur计算转化为1维的计算,计算量会随blur区域大小平方倍的增加,最终的计算量太大而难以在实时应用中承受。
最后再总结一下本算法的优缺点:
优点:
1、完全消除了shadowmap在分辨率不足时产生的锯齿现象,因此对于shadowmap的分辨率要求降低了,即使在较低分辨率上也可得到高质量的阴影。
2、使用低分辨率shadowmap可以得到高质量的软阴影效果,对于静止的光源尤其适合。
缺点:
1、每个象素渲染需要对shadowmap进行36次采样(经过优化后的计算量为16次采样和比较,28次加法,二次线性插值),PS的计算开销比较大。但由于都是相邻象素,纹理cache的命中率会很高,因此问题并不是很大。
2、低分辨率时移动的阴影会有难看的抖动现象,只能靠提高shadowmap分辨率来解决问题,当然那样就得不到非常柔化的阴影边缘了。
1、每个象素渲染需要对shadowmap进行36次采样(经过优化后的计算量为16次采样和比较,28次加法,二次线性插值),PS的计算开销比较大。但由于都是相邻象素,纹理cache的命中率会很高,因此问题并不是很大。
2、低分辨率时移动的阴影会有难看的抖动现象,只能靠提高shadowmap分辨率来解决问题,当然那样就得不到非常柔化的阴影边缘了。