Shadow Mapping作为最简单的绘制实时阴影的算法, 入门比较容易,但难以做出较好的效果,而且初学者会发现大量的问题。
Shadow Mapping的使用方法本文最后有一大堆资料供参考。这里对会出现的问题做一些总结。
情况1:阴影位置错乱或缺失。
通常导致这种问题的原因之一是:渲染到纹理的时候,没有正确的设置视口。
比如,如果深度纹理的大小是1024*1024,那么渲染之前一定要保证设置:
glViewport(0, 0, 1024, 1024);
阴影缺失的可能原因是:灯光位置渲染的视锥远小于实际相机渲染的视锥,导致实际相机渲染的大部分顶点在深度图中查找不到,导致阴影丢失。
这种情况比较麻烦。想完美的解决需要精确的设置好灯光视角渲染的视锥,或者使用多次渲染得到足够完整的深度图。
当然最简单的办法就是。。。把灯光摆在一个好的位置和朝向。
情况2:光照处出现阴影。
这种情况是因为在灯光视角渲染到纹理时,没有剔除正面:
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
可以这样理解。从灯光的位置观察物体,是不可能看到阴影的,因为阴影都被物体正面挡住了。所以正面渲染与否是没有意义的。
情况3:Z-Fighting。
将2个深度值进行比较时,为避免浮点数误差导致的Z-Fighting,需要将深度图读到的深度增加一个很小的值e。
下表是使用不同的e的渲染结果。大家感受一下。
e=0.00001 | e=0.0001 | e=0.001 |
情况4:背光面的锯齿。
从上图可以看到。即使解决了上面的所有问题,背光面仍然会有亮斑锯齿。
这个是很难去除的。不过好在背光面本来就不需要使用阴影来着色,结合Phone光照模型即可。如下图:
情况5:闪烁与偏移
这个现象与上面的Z-Fighting有点类似,最重要的是这个现象在越平行于视线的地方越严重。这就是多边形偏移问题。在OpenGL中通过如下代码修改偏移值:
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(f, u);
其中f表示最大斜率(与视线垂直为0),u为偏移值。 具体用法请看文末参考文献。
这两个值如果设置太大了则出现阴影与本体分离的现象,学名叫做"Peter Panning“。
设置这个在Unity的手册中说的比较详细。这里说的设置f,u称为设置Bias。其中总结的很好:
The bias value for a light may need a bit of tweaking to make sure that neither shadow acne nor Peter Panning occur. It is generally easier to gauge the right value by eye rather than attempt to calculate it.
设置这个在Unity的手册中说的比较详细。这里说的设置f,u称为设置Bias。其中总结的很好:
The bias value for a light may need a bit of tweaking to make sure that neither shadow acne nor Peter Panning occur. It is generally easier to gauge the right value by eye rather than attempt to calculate it.
下面给出设置不同bias的渲染结果,大家感受下:
[f=0, u=0] | [f=0.5, u=1] | [f=10, u=1]或者[f=1, u=100] |
想要更好的效果?
简单的Shadow Mapping只能帮你到这儿了。想要更好的效果?
可以学习PCF来多点采样进行抗锯齿,或者使用VSM来通过概率采样抗锯齿。请查阅本文最后的参考文献。
参考文献:
Basic Shadow Mapping:
http://fabiensanglard.net/shadowmapping/index.php
Basic Shadow Mapping:
http://ogldev.atspace.co.uk/www/tutorial24/tutorial24.html