Deferred Lighting (延迟光照) + Light Pre-Pass 实现多光源动态阴影
发表于2015-08-06
一直以来,不明白多光源动态阴影应该如何高效的实现,直到在网上查阅了大量的文章,得知虚幻3引擎里用到了 Deferred Lighting (延迟光照)渲染系统,实现了多光源的动态光照和阴影,我猜其它游戏中的实现方法也一定是大同小异了。
于是我花了两个星期研究,有了现在的结果。延迟光照来源于一种基本的思想,三维变二维的处理方式,不过它变的很完整,除了把整个视口的颜色信息画到纹理,还把几何数据(坐标,法线)也分别画到了纹理上,使每个像素都有了完整的顶点信息。这一组纹理被称作“G-Buffer”,我一直觉得不知道怎样翻译比较好,“几何纹理缓存”?总之每帧的渲染中几何模型只画了一次,剩下的都是图像处理了,每加一个光照,就用G-Buffer的信息处理一次光照效果,累加到最终的输出。如果有一盏以上的灯光,最终场景的颜色亮度会超过1.0,所以必须用浮点纹理,也必须进行后期HDR效果处理,整个渲染过程变的十分完美。在这些步骤中,控制各种特效的开和关变的很容易,我觉得这是一大好处,虽然它本身的实现过程很麻烦。
在具体的实现中,要解决的问题还是很多的,主要是画半透明物体的问题。网上很多文章都是大概雷同的解释,就是对半透明物体依然使用传统的方法绘制,单独走一个管线。我觉得这是个最差的设计。画出那么多G-Buffer就是为了方便进行各种后期效果处理的,加上必须要做HDR处理,已经消耗很多时间了,中间要做一次传统方法的绘制岂不是严重效率低下?而且效果是要统一的,所有的材质效果,光照效果,阴影效果,无论是否半透明都要在两套渲染过程中做的一模一样,实在不是一件容易的事。于是我想了一个把半透明物体也画到G-Buffer中的办法,对于半透明的物体,显示的颜色是自身和后方物体颜色的混合值,但是对坐标纹理和法线纹理的渲染就要有复杂一点的处理,当物体透明度较高时,不足以产生阴影了,就认为所在像素的深度是后方物体的深度,如果半透明度比较低,希望产生阴影,则像素的深度是半透明物体自身的深度,所以只要在HLSL中加上这样的判断,决定是否输出坐标纹理和法线纹理的像素后,把相应像素的w值置为0或1即可。
在设计过程中,对光照阶段的处理加入了 Light Pre-Pass。Light Pre-Pass 在网上有一些争论,我觉得不必非要和 Deferred Lighting 一较高下,最好是结合起来一起发挥功效。Light Pre-Pass不是直接用G-Buffer和每个灯光画出最终效果,而是把光照信息单独放到一个纹理处理(所谓的 Light Pre-Pass 过程),然后预先算出所有光源的照射亮度,累加到一个浮点纹理上(数值已经HDR了),最后和G-Buffer中的数据计算物体的最终颜色,可以得到一个HDR颜色数值的场景,然后做HDR效果处理。本来希望这样处理可以节约一些时间,但是从实际来看效率提高不多,可能是逐像素光照计算太耗时了,这一步无论如何都逃不掉,不过可以使程序逻辑更为清晰。
性能评价:
在整个流程中,只运行了一次VS,剩下的所有处理都靠PS,而且每个PS都相当复杂,每帧要反复十几次图像处理。所以对显卡的性能要求还是有点高了。理论上讲,只要显卡足够强,可以画出任意多个光源的光照和阴影,但是前提是每个光源都要预先渲染一个自己的ShadowMap,画ShadowMap是根据光照空间进行特殊的裁剪,而且光照空间一般都很大,由于这个原因,想画出多个光源的阴影仍然是个困难事。在GeForce-7300GT上,最多画4个光源,再多就变幻灯片了,而且模型数量不能太多。
缺点总结:
延迟光照的优点当然就不用说了,所以直接说它的缺点。主要就是消耗存储资源太多,现在的硬件在多目标渲染时要求所有目标缓存格式相同,为了保存坐标和法线,被迫要使用A32B32G32R32F这种大浮点纹理,结果G-Buffer的所有纹理,包括DiffuseMap和EmissiveMap都变成A32B32G32R32F了,实在是浪费空间,加上超大的ShadowMap(一般在2048x2048或更高)现在变成了N个,如果渲染的屏幕尺寸超过1600x1200,256MB显存都要变成最低要求了。如果希望DiffuseMap和EmissiveMap使用传统32位颜色来节约空间,生成G-Buffer就要渲染多次场景消耗更多时间,所以只能用空间代价换取时间。
展望一下未来:
如果希望照顾不支持 Shader Model 3.0 的显卡,延迟光照渲染在一段时间内还是无法普及的。但是无论怎样,这是一种比较先进的渲染方法,就支持多光源动态效果来说最合适不过了,算是真正的次时代图形技术:)尽管现在的硬件对它的实现很吃力,但这只是时间的问题,希望以后普及1GB显存时,显存空间不再成为一个约束。
连续两周每天睡5小时搞这个东西,终于可以告一段落,有建议请留言,我先睡觉去了。。。
于是我花了两个星期研究,有了现在的结果。延迟光照来源于一种基本的思想,三维变二维的处理方式,不过它变的很完整,除了把整个视口的颜色信息画到纹理,还把几何数据(坐标,法线)也分别画到了纹理上,使每个像素都有了完整的顶点信息。这一组纹理被称作“G-Buffer”,我一直觉得不知道怎样翻译比较好,“几何纹理缓存”?总之每帧的渲染中几何模型只画了一次,剩下的都是图像处理了,每加一个光照,就用G-Buffer的信息处理一次光照效果,累加到最终的输出。如果有一盏以上的灯光,最终场景的颜色亮度会超过1.0,所以必须用浮点纹理,也必须进行后期HDR效果处理,整个渲染过程变的十分完美。在这些步骤中,控制各种特效的开和关变的很容易,我觉得这是一大好处,虽然它本身的实现过程很麻烦。
在具体的实现中,要解决的问题还是很多的,主要是画半透明物体的问题。网上很多文章都是大概雷同的解释,就是对半透明物体依然使用传统的方法绘制,单独走一个管线。我觉得这是个最差的设计。画出那么多G-Buffer就是为了方便进行各种后期效果处理的,加上必须要做HDR处理,已经消耗很多时间了,中间要做一次传统方法的绘制岂不是严重效率低下?而且效果是要统一的,所有的材质效果,光照效果,阴影效果,无论是否半透明都要在两套渲染过程中做的一模一样,实在不是一件容易的事。于是我想了一个把半透明物体也画到G-Buffer中的办法,对于半透明的物体,显示的颜色是自身和后方物体颜色的混合值,但是对坐标纹理和法线纹理的渲染就要有复杂一点的处理,当物体透明度较高时,不足以产生阴影了,就认为所在像素的深度是后方物体的深度,如果半透明度比较低,希望产生阴影,则像素的深度是半透明物体自身的深度,所以只要在HLSL中加上这样的判断,决定是否输出坐标纹理和法线纹理的像素后,把相应像素的w值置为0或1即可。
在设计过程中,对光照阶段的处理加入了 Light Pre-Pass。Light Pre-Pass 在网上有一些争论,我觉得不必非要和 Deferred Lighting 一较高下,最好是结合起来一起发挥功效。Light Pre-Pass不是直接用G-Buffer和每个灯光画出最终效果,而是把光照信息单独放到一个纹理处理(所谓的 Light Pre-Pass 过程),然后预先算出所有光源的照射亮度,累加到一个浮点纹理上(数值已经HDR了),最后和G-Buffer中的数据计算物体的最终颜色,可以得到一个HDR颜色数值的场景,然后做HDR效果处理。本来希望这样处理可以节约一些时间,但是从实际来看效率提高不多,可能是逐像素光照计算太耗时了,这一步无论如何都逃不掉,不过可以使程序逻辑更为清晰。
性能评价:
在整个流程中,只运行了一次VS,剩下的所有处理都靠PS,而且每个PS都相当复杂,每帧要反复十几次图像处理。所以对显卡的性能要求还是有点高了。理论上讲,只要显卡足够强,可以画出任意多个光源的光照和阴影,但是前提是每个光源都要预先渲染一个自己的ShadowMap,画ShadowMap是根据光照空间进行特殊的裁剪,而且光照空间一般都很大,由于这个原因,想画出多个光源的阴影仍然是个困难事。在GeForce-7300GT上,最多画4个光源,再多就变幻灯片了,而且模型数量不能太多。
缺点总结:
延迟光照的优点当然就不用说了,所以直接说它的缺点。主要就是消耗存储资源太多,现在的硬件在多目标渲染时要求所有目标缓存格式相同,为了保存坐标和法线,被迫要使用A32B32G32R32F这种大浮点纹理,结果G-Buffer的所有纹理,包括DiffuseMap和EmissiveMap都变成A32B32G32R32F了,实在是浪费空间,加上超大的ShadowMap(一般在2048x2048或更高)现在变成了N个,如果渲染的屏幕尺寸超过1600x1200,256MB显存都要变成最低要求了。如果希望DiffuseMap和EmissiveMap使用传统32位颜色来节约空间,生成G-Buffer就要渲染多次场景消耗更多时间,所以只能用空间代价换取时间。
展望一下未来:
如果希望照顾不支持 Shader Model 3.0 的显卡,延迟光照渲染在一段时间内还是无法普及的。但是无论怎样,这是一种比较先进的渲染方法,就支持多光源动态效果来说最合适不过了,算是真正的次时代图形技术:)尽管现在的硬件对它的实现很吃力,但这只是时间的问题,希望以后普及1GB显存时,显存空间不再成为一个约束。
连续两周每天睡5小时搞这个东西,终于可以告一段落,有建议请留言,我先睡觉去了。。。
附图:一组完整的G-Buffer和一张最终效果图
G-Buffer之一:世界空间坐标 A32B32G32R32F
G-Buffer之二:世界空间法线 A32B32G32R32F
G-Buffer之三:Diffuse Color Map A32B32G32R32F(太奢侈了)
G-Buffer之四:Emissive Color Map A32B32G32R32F(依然是很奢侈)
最终的效果:放了两个围绕世界中心旋转的平行光源 场景终于有了多个阴影:)