Jim's GameDev Blog

实现 Shadow Mapping

2016-6-15

以前介绍过了在移动设备如何做实时阴影,那篇文章中提到的方法其实是一种简化版的 Shadow Mapping。这里之所以要再次提起这个问题,主要是因为我想在角色的特写部分,给角色加上自阴影的效果,使整个画面更立体。

a:Unlit b:Mobile Shadow
img img
c:Shadow Mapping d:Shadow Mapping PCF
img img

其实 Unity 已经内置了影功能,为什么不使用呢。一是效率问题:当我把 Unity 内置的阴影质量设置为普通时,表现效果是不理想的,当设置为高质量时,表现效果勉强可以接受了,但是内存偏高。想要将低内存,只能把 Shadow Cascades 设置为最低,把 Shadow Distance 调小。二是表现效果的精确控制:无法直接控制阴影区域的亮度或颜色(需要修改 Surface Shader),以及阴影到非阴影的过度(经常会造成整个模型偏黑)。

img

Unity 内置的阴影,修改这两个值将内存降低,但是阴影覆盖范围变小了。


img

Unity 内置的阴影,当保证阴影覆盖范围和质量时,内存偏高。Shadow Cascades

所以这里我自己实现了一版 Shadow Mapping,效果见上图。下面说明下实现的步骤。

img

至此实现的效果如上图中的 c:Shadow Mapping。你也许会发现画面上有很多杂点或者黑色条状条纹,这主要是由于数字精度造成的。当我们把深度值作为颜色分量存储到 RenderTexture 时,就意味着精度的丢失。因为单个通道(R或G或B)的精度只能达到 1/255。所以 Unity 提供了一个技巧,EncodeFloatRGBA 将一个 0 到 1 的 float 值存储到 RGBA 四个通道中,这样就不会丢失 float 的精度。使用 DecodeFloatRGBA 将 RGBA 值解码为一个 0 到 1 的 float 值。所以使用这种方式得到的深度纹理是类似下面这样的。

img

最后,使用 PCF 方法实现软阴影。关于 Shadow Mapping 的软阴影,有一篇非常好的教程,其中介绍了各种解决方法。最终效果如图 d:Shadow Mapping PCF。