前向渲染
先计算光照再裁剪。
前向渲染是现在最基础,也是最多引擎使用的标准。前向渲染的流程是给定一个几何体,引擎对其进行从顶点到像素着色器的一系列计算,然后输出到最终的图像缓冲区。场景中有多个几何体时,引擎就是对其挨个进行渲染,完成一个再继续下一个。
前向渲染的问题
前向渲染有一个问题就是无效渲染太多,比如场景中有四个物体,互相之间存在叠压关系,按照前向渲染的流程,先前渲染了一个物体之后,它的一部分被后一个渲染的物体挡住了,那么被挡住的这部分就是无效的计算,毕竟我们在屏幕上是看不到这部分的。
另一个问题在于难以支持过多的光源,对于每个需要逐像素计算的光源,渲染一个几何体的时候需要逐个做一次光照计算。如果有一个场景,其中有10个几何体需要进行渲染,有四个光源对整个场景产生影响,那么渲染整个场景需要进行40次光照计算。而且其中还有很多的计算被挡住了。
前向渲染的优点
如果需要在场景中使用多个着色模型,甚至是每个几何体都使用不同的着色模型和渲染技术,前向渲染是可以很好的支持的。
另外,因为前向渲染这种逐个渲染的特点,它非常适合渲染半透明物体。
上面图中就是简单的前向着色的示意图。一个几何模型,进入渲染流程中,首先就是要经过顶点着色器的计算,顶点着色器一般会对顶点做空间变换,有的还有顶点位移和UV偏移等计算。经过顶点着色器之后就到了曲面细分着色器,这个阶段是可选的,用来给模型的表面增加顶点数,并且产生更细微的凹凸,当然游戏中的大部分模型是不会做这个阶段的计算的,一般用在高质量游戏的渲染。几何着色器也是可选的,一般会用来做基于模型表面的顶点渲染出一片草丛等功能。像素着色器当然是大部分模型都要经历的,用来做采样贴图和计算光照等计算。
前向渲染优化策略——前向实时剔除
前向渲染的缺点这么明显,难道没有人想办法解决一下吗?当然有一些解决办法的,比如Early-z 、Z-Prepass 、 Hi-Z 等剔除方法。
Early-z : 传统的渲染管线中深度测试是在计算完光照之后才进行的,但是计算完光照再进行深度剔除,被挡到的部分就白计算了。在光栅化阶段,每个模型的深度就已经写入深度缓存了,为什么不先进行深度剔除再计算光照呢?这就是Early-z功能,这也是现在所有硬件和渲染管线都有的功能。
Z-Prepass :上面说的Early-Z优化虽然是在每个模型计算光照之前就进行了深度剔除,但是还是不能完全避免无效光照计算的问题。比如第一个模型渲染的时候,深度通道是没有信息的,这时哪怕这个模型会被后来渲染的模型完全遮挡,它也会计算光照。Z-Prepass就可以解决这个问题,首先把整个场景中的模型都渲染一遍,全部都写入Z-Buffer ,这次渲染除了Z-Buffer其他的信息都不计算。然后再渲染一遍场景,这次渲染关闭深度写入,每个像素和Z-Buffer中已经存在的深度信息进行对比,只有通过测试的像素才会计算光照。这种方法虽然避免了无效的光照计算,但是却执行了两次顶点着色器,所以最好是在场景中物体的光照计算非常复杂但是顶点数量却不是很多的情况下使用。
Hi-Z :上面两种方法都是在GPU段进行的,而Hi-Z这种方法是在CPU端进行的,在几何体被提交到GPU之前会进行遮挡测试,如果几何体被别的物体遮挡了就不会提交到GPU 。这种方法直接减少了GPU需要渲染的几何体数量,适用于细碎且数量多的模型。
延迟渲染
先裁剪再计算光照。
延迟渲染可以支持大量的实时光照,所以现在的大型电脑游戏基本都已经是延迟渲染了。
延迟渲染就是把光照计算延迟到深度测试之后的渲染方式,延迟着色适合在场景中实时光照很多的情况下使用,而且延迟着色可以对每个光源都按逐像素的方式计算。那Z-prepass也是先渲染出深度缓存进行深度测试后再计算光照的,和延迟渲染有什么不同?最大的不同点就在于G-buffer ,Z-prepass在深度测试后也还是按照一个几何体渲染完再进行下一个这种方式来渲染的 ,延迟渲染是几何体的信息传递到G -buffer之后就和几何体没多大关系了,接下来的操作都是对G-buffer进行的。
如果说前向渲染中几何体数量N和光照数量M产生的计算量是N*M的话延迟渲染产生的计算量就是N+M 。这是因为延迟渲染的思路就是先把几何体的信息都渲染到二维空间中(G-Buffer),然后把G-Buffer整体进行光照计算,G-Buffer中存在的信息都是会最终呈现在屏幕上的,不会有无效计算。
那么G-Buffer中到底会储存哪些信息?不同的引擎对于G-Buffer的处理也是不一样的,对于一个针对PBR渲染的引擎来说,至少会有深度、模板、颜色、法线、世界位置、金属、粗糙、高光这些信息。
延迟渲染的优点
当场景中所有几何体都写入G-Buffer之后,光照计算就和场景中有多少物体,多少三角形没有关系了。
只渲染可见的像素,不会有无效的计算。
延迟渲染的缺点
延迟渲染要求场景中要使用更少的Shader种类,毕竟如果每个模型的每个材质都使用不同的Shader ,那到底应该在G-Buffer中储存哪些信息呢?这对于程序和项目管理上来说可以说是个巨大的优点,可以节省性能,优化流程什么的。但是对美术来说却是个大缺点,因为限制了美术的表现效果。
前向渲染只需要深度缓冲和最终的颜色缓冲就够了,延迟渲染需要缓存的信息实在太多了,这造成了带宽的开销大幅增加。这也是为什么现在的手机游戏基本都是前向渲染的原因,毕竟手机带宽太小,延迟渲染虽然看起来很美好,但是用不了。
对于半透明无能为力,所有半透明的物体都需要等待不透明物体以延迟渲染完成之后,在用前向渲染的方式渲染半透明物体。
分块延迟渲染
虽然延迟渲染已经把光照数量和三角形数量不再关联了,但是当场景中的光源数量不断上升之后,就算是延迟渲染也有点撑不住。
这时就需要分块延迟渲染,它的思路是把G-Buffer分成很多个小块,分析每个小块受到哪些光源的影响,然后逐个分块进行着色,那些受光源影响的数量少的小块就不需要那么多的光照计算了。这种渲染方式在光源数量少的情况下效果不明显,但是光源数量越多它优化的性能也就越多。