现在的位置: 首页 > 综合 > 正文

GLSL实现ShadowMap及阴影软化

2013年08月27日 ⁄ 综合 ⁄ 共 1578字 ⁄ 字号 评论关闭

Shadow Map是一种成熟的实时阴影生成技术,由于其实现较为简单、方便,而且因为是基于图像空间的,故而与场景的空间复杂度关联度不大,故而在游戏中广为使用。

原理:

1.       生成深度纹理。将场景的深度值在光源所在的投影坐标系下进行生成并存储,进而得到深度纹理。

2.       正常渲染场景并查询深度纹理进行阴影判断。在片断着色器中将当前片段变换到光源矩阵中,得到该片段在此坐标系下的深度值及深度纹理坐标s,t并读取该位置上的纹理值,得到的即为阴影图中当前位置所对应的深度度,这样只需将当前片段的深度值与这一深度值作判断即可确实当前片段是否在阴影之中。

ShadowMap的问题:

1.       阴影边缘的锯齿。产生这个问题的原因主要受限于深度纹理的分辨率。

解决:对边缘进行平均采样来进行平滑。在片段着色器中作深度判断时在目标纹理的周围进行采样,使用采样得到的深度平均值来与当前片段进行深度比较。

2.       阴影的硬边缘。产生这个问题主要是阴影图模拟的是聚光灯效果。

解决:对于阴影硬边缘的软化可以采用图像过滤的方法来对深度判断得到的阴影因子进行操作。这一步的操作是在深度值比较之后得到当前屏幕空间的阴影因子之后进行,不能在深度判断时进行,因为深度判断得到的结果是二值的,这样就算是进行过滤之后得到的仍然是硬化的二值边缘,无法得到具有灰度渐进变化的软化效果。

 

 

  Smooth SM       Blur SM

              

                 原始阴影因子图                 过滤后的阴影因子图

上面是进行一次高斯过滤后的软化效果,从图中可以看中,虽然有软化效果,但并不明显,故而要想得到很理想的效果需要进行多次过滤,但这样就由于图像采样操作的频繁进行而大大影响了效率,因此如可想在游戏中采用这种方法来产生软阴影特效还是需要好好考量其可行性。而且当前ShadowMap的改进算法也很多,即能提高效率又有平滑与软化效果,比如PSM , VSM等。

 

 

    Final Effect

             最终阴影效果

 

 

调试中遇到的一些问题:

1.       GLSL中的内置变量gl_ModelViewMatrixOpenGL程序中通过调用glGetFloatv(GL_MODELVIEW_MATRIX,…)得到的两个模型视图矩阵不一致。

原因:产生这个问题的原因主要在于欲在GLSL中使用自定义传入的模型视图矩阵,而且传入的ModelViewMatrix是在绘制之前调用glGetFloatv()得到的,但是在绘制模型时对于几何图元会有glTranslateglRotateglScale等操作,故而导致与顶点绑定的gl_ModelViewMatrix就与此前已经得到的ModelViewMatrix不同,这就要求向着色器中传入ModelView时在其它地方不能再使用改变图元ModelViewMatrix的操作,否则就会导致不一致。

2.       自定义的Z值变换

若要改变投影变换时的Z值曲线可以使用自定义的深度值计算:

float zDiff;

zDiff = zFar - zNear;       

float interpolatedDepth = fragPosition.w / fragPosition.z * zFar * zNear / zDiff +

                        0.5 * (zFar + zNear) / zDiff + 0.5;

float fragDepth = pow(interpolatedDepth , 8.0);

(可参见http://blog.csdn.net/cremesino/archive/2009/04/28/4132088.aspx)

采用自定义的变换可以改变Z值曲线的在某些位置的斜率,从而减少这些位置上的Z-fighting,但这样就降低了着色器的效率。

抱歉!评论已关闭.