Shadow Map是用于在Realtime Shadow的一种算法,这里简单的描述了该算法的过程,以及多光源的Shadow Map的使用。
对于一个光源(Spot Light或者Directional Light),如果以光源这个点作为视点,在可视的范围内,每一条视线都有一个可见的最远的深度值。如果将这个深度值记下来,存在一个Texture中,那么这个Texture就是这个Light的Shadow Map,他代表了这个Light的可达到的最大深度。
有了这个Shadow Map过后,在渲染实际场景的时候,以上面光源的Project Matrix对该场景进行投影(计算出Shadow Map Texture的UV),并计算出以光源作为视点的深度值。这个过程是基于Per-Vertex的,对于一个Triangle,内部Pixel将会通过插值得到对应的UV和深度值。
后面在Per-Pixel过程,就要根据该Pixel的UV在Shadow Map中取出该Light的最大深度值,和该Pixel本身的深度值进行比较。如果该Pixel的深度值大于在Shadow Map中的深度值,那么表示这个Pixel应该处于Shadow中,否则该Pixel应该处于光照中。
上述就是Shadow Map的大致过程。
Depth Map:
Rendered By Render Monkey with R32F format render target.
Result with Shadow:
Rendered By Render Monkey with Shadow Map 512X512
对于多光源情况,单个光源之间的过程是独立的,对于每一个光源产生的结果进行叠加便是多光源的结果。
One Light:
two light:
Shadow Map中的问题:
1:Shadow Acne
因为在Shadow Map Texture中每一个像素点的浮点精度问题,在Shadow Depth比较的时候,对于理论上应该严格相等的地方,实际上并不总是成立。所以会出现下面的结果:
Rendered with Shadow Map with A8R8G8B8, 也就是8-Bit的浮点数
Rendered with Shadow Map with R32F, 也就是32-Bit的浮点数
解决方法:
一种是使用Bias,在Pixel上的Light-Depth减去一个小的Bias,这样对于表面应该相等的,就变成了严格的小,就不会有上面的情况。但这个会造成另一个问题,Shadow会平移,产生不正确的Shadow。因此这个Bias要选择恰当。
另一种是在生成Shadow Map的时候,不采用CCW,而是使用CW。也就是用Back Faces去生成Shadow Map,这样在渲染场景的时候,对于Light的Front Faces就必然小于Shadow Map中的值,从而避免了上面Shadow Acne的产生。
2:Shadow锯齿
如果使用的Shadow Map Texture的尺寸比较小,在Shadow Map中的一个Texel就代表了很多的像素的深度。
Shadow Map with 64X64
与使用Shadow Map 512X512相比,这里有很明显的锯齿。
因此使用的Shadow Map的尺寸越大,锯齿就越不明显。
另外,在Render Monkey自带的Sampler中有一个也是Shadow Map的实现,它里面有使用Multi-Pass来实现Soft Shadow Edge.
Reference:
Michal Valient, "Shadow Mapping with Direct3D 9" in ShaderX2
阅读(1315) | 评论(0) | 转发(0) |