Forward Render VS Deferred Rendering详解

发表于2017-09-11
评论0 5k浏览

Forward Render

传统的渲染方式,你提供给显卡形状Mesh,它将其打散成一堆节点,接着经过一系列的变换和分割成为Fregment或者像素,在呈现在屏幕之前已经完成了所有的渲染处理。


这是相当线性的,每一个形状都会在生成完整图像之前经过流水线的每一个阶段。


Deferred Rendering

中文称为延迟渲染,渲染的工作被放在最后,直到所有的形状到都完成了前面的工作,一旦所有需要的缓冲建好,就直接被读进一种着色算法中,合并在一起从而得出最后的结果。 这样,着色一个场景所需的计算和内存的带宽被减少到了这些可见的部分中,从而降低了着色深度的复杂性。

G Buffer - 指Geometry Buffer,亦即“物体缓冲”。区别于普通的仅将颜色渲染到纹理中,G-Buffer指包含颜色、法线、世界空间坐标的缓冲区,亦即指包含颜色、法线、世界空间坐标的纹理。由于G-Buffer需要的向量长度超出通常纹理能包含的向量的长度,通常在游戏开发中,使用多渲染目标技术来生成G-Buffer,即在一次绘制中将颜色、法线、世界空间坐标分别渲染到三张浮点纹理中。

常见的做法是将颜色,深度和法线分别渲染到不同的buffer里面,在最后计算光照的时候的通过这三个buffer和光源的信息计算出最终pixel的颜色。


Color, Depth, and Normal buffer


Final lighting (shading) result generated using the three buffers


比较

在一个标准的Forward Rendering 的渲染管线里,对于每个光源,必须计算场景中每个顶点的光照(使用Vertex shading)。假设场景中有100个物体,每个物体有1000个顶点,那就差不多有100000个多边形,显卡处理这个量集的定点数是很随意的,但是当把这些多边形发送到fregment shader中进行处理, 在这里的光照计算会消耗大量的性能。


开发者总是试图将更多的光照计算放到Vertex Shader而不是放到fregment shader,这样能够节省很多性能。每一块可见的片段都会进行昂贵的光照的计算,不管它是不是被其他的片段遮挡,度过屏幕的像素是1024 * 768,那么将有将近800000个像素需要渲染,那么渲染的每一帧在都可能在在fregment shader 中进行数百万次,甚至那些经过深度测试已经被遮挡的像素也要被计算,这样将会造成很大的性能浪费。


更可怕的是,当你在场景中再加上一盏灯的时候,那么fragment shader又要为这个光源再重新计算一遍,想像一下一条满是灯光的街道...


Forward Rendering渲染的复杂度可以用O(num_geometry_fragments * num_lights)来表示,可以看出复杂度和集合体的面数还有光源的数量正相关。


一些引擎通过一些算法进行了优化,比如太远处的光源不参与计算,合并光源,或者使用light map(只能是静态),但是i如果想要实现动态多光源,那就需要一个更好的解决方案。


Deferred Rendering 就是一个很好的解决方案。它能够很好的减少渲染物体的数量,也就是渲染片段的数量,在进行光照计算的时候用的是屏幕上的像素数量,而不是所有片段像素的总和。它的光照渲染的时间复杂度可以用O(screen_resolution * num_lights)来表示,他和场景中物体的数量是无关的,只和光源数量有关。


如何抉择

简单的回答是,如果你使用的是很多动态光源,那么就使用Deferred Rendering,但是它也有一些缺点:

1.需要比较新的显卡,支持多目标渲染;

2.需要很大的显卡带宽,用来传递Buffer;

3.不用处理透明的对象(除非把Forward rendering 和 Deferred rendering结合起来);

4.没法用传统的抗锯齿方法, 比如MSAA,但是屏幕空间的FXAA是适用的;

5.只能使用一种材质,但是有一种解决方法是Deferred Lighting;

6.阴影的数量还是和光源的数量有关。


如果游戏中没有很多的光源并且需要兼容老的设备,那么最好选择Forward Rendering,然后采用静态的Light map,效果也很不错。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引