《星际争霸II 效果与技术》中的延迟渲染

《星际争霸II 效果与技术》中的延迟渲染

原文链接:

本来想使用英文原版的标题,可惜超出了知乎的标题最大长度。


这篇文章发表在SIGGRAPH2008,算是一篇比较老的文章了。里面主要提到在星际争霸II(以下简称星际2)这款游戏中用的的延迟渲染(Deferred rendering)、屏幕空间环境光遮蔽(SSAO)、景深效果(DOF)和阴影贴图(Shadow map)等技术。

本文主要讨论其中用到的延迟渲染技术。目前,延迟渲染在PC和主机游戏上得到了大量的应用,但是手机游戏上还鲜有出现,不过已经听说有一些手游团队正在应用这项技术,相信短期之内就会看到它在手机上广泛使用。本文仅作为延迟渲染技术的入门资料,更多更深的技术还需要大家一起探讨。


在星际2的故事模式中,有丰富的灯光环境。而在前向渲染中,这样会增加大量的draw call和Overdraw。于是星际2使用了延迟渲染来解决这个问题。

前向渲染中,我们需要在片元着色器中计算光照,这也就意味着当我们使用多个灯光的时候,就会产生大量的Overdraw,虽然有遮挡剔除(Occlusion culling)和early-z来减少overdraw,但是对于GPU运算的消耗依然有指数增长的趋势。

前向渲染

而延迟渲染中,会将相关信息渲染到延迟缓存(deferred buffer,又称为G-buffer)中,例如深度、法线、颜色等。当所有片元都渲染完毕,再进行光照着色。因为延迟缓存的尺寸是固定的,随着灯光数量的增加,GPU运算的消耗只是线性增长。同时,延迟渲染也带来了问题,因为要有延迟缓存,所以会增加显存和带宽,以及对缓存重采样的消耗。

延迟渲染还有一些其他的问题:

  1. 不支持半透明物体,所以在使用延迟渲染绘制完不透明物体后,还需要使用前向渲染绘制半透明物体。
  2. 延迟渲染不支持MSAA(参考文献4中阐述一种AGAA的方法)。
  3. 对于有多重材质的情况,需要一些额外的处理。(参考文献3)

对于不透明物体来讲,前向渲染和延迟渲染的渲染结果是等同的,只不过对于复杂的光照环境(点光源和聚光灯),延迟渲染更加高效。

需要注意的是,这里平行光会使用前向渲染管线,在片元着色器中正常渲染,而只有点光源和聚光灯才会使用延迟渲染。因为平行光会影响所有的模型,所以使用延迟渲染的意义不是很大。而对于点光源和聚光灯,则需要计算它们的影响区域(形状)来加速渲染,后面会介绍到。

在星际2中,暴雪使用了MRT(多重渲染对象)来填充延迟缓存,这里使用了4个MRT。

MRT0:

  • RGB通道存储不受局部光源(点光源和聚光灯)影响的色彩值:自发光,以及环境贴图和平行光。
  • Alpha通道未使用。

MRT1:

  • RGB通道存储法线,用于SSAO。
  • Alpha通道存储深度,用于光照计算、体积雾、SSAO、位移(smart displacement)、景深、投影、边缘检测和厚度测量。

MRT2:

  • RGB通道存储漫反射,用于光照计算。
  • Alpha通道存储环境光遮蔽,如果SSAO启用,这个通道将被忽略。

MRT3:

  • RGB通道存储高光,用于光照计算。
  • Alpha通道未使用。

在星际2中重度使用了HDR(高动态范围),所以这些延迟缓存都是4通道16bit浮点格式,这样不仅可以回避精度问题,还可以节省在片元着色器中为了解码缓存所需要的指令。同时,这也带来一个问题,因为API的限制,所有的缓存(渲染对象)都需要相同的精度,所以最后输出的对象是每像素24个字节,浪费了大量带宽(不过在暴雪看来都是值得的)。

对比前向渲染,延迟渲染只不过是把(局部)光照的计算往后挪了一个阶段,这些光照的计算其实就相当于一个后处理,即光照着色器。

在光照着色器中重建像素位置:SM3.0支持VPOS语义(semantic),将这个值归一化到[-1,1],再乘以深度,就可以获取像素的观察空间坐标。

而对于SM2.0,需要使用一个效率稍微低一点的替代方案。

使用Early-Z和Early-Stencil:使用Early-Z可以将灯光影响区域之前的像素丢弃掉,而使用Early-Stencil可以将灯光影响区域之后的像素丢弃掉。这样就可以大量的节省光照计算的消耗。

为了使用这两项技术,光照着色器会渲染两个pass。第一个pass(pre-lighting),只是将灯光形状画出来,而不写入色彩值(和深度)。而第二个pass,才会真正的计算光照。

文中未提及灯光的具体形状,不过聚光灯一般是椎体,而点光源一般是球体:

聚光灯形状

需要注意的是,在第一个pass里,只有灯光的形状(非真实)会被渲染,场景中真实的物体都已经在延迟缓存里了,会在第二个pass里参与光照计算。

如果观察相机在灯光的外部,只需要渲染出形状的正面。

如果观察相机在灯光的内部,就需要渲染出形状的反面中未通过深度测试的部分。

最后补充一点,什么情况下可以考虑使用延迟渲染?

  • 有很多局部光源的情况,最好是这些光源没有重叠,尤其在有很多动态光照的夜晚场景或较小的封闭空间,可以得到很好的效果。
  • 平行光太多的时候,并不适用,因为这些光会影响所有的片元,所以并不比前向渲染高效。在白天的户外开阔场景中,也不太适用。

参考文献:

  1. developer.amd.com/wordp
  2. download.nvidia.com/dev
  3. advances.realtimerendering.com
  4. advances.realtimerendering.com
  5. twvideo01.ubm-us.net/o1
  6. Forward Rendering vs. Deferred Rendering
编辑于 2018-08-17 18:20

文章被以下专栏收录