前向渲染与延迟渲染

最后更新于 2022-11-22 740 次阅读


主要参考:《Real-Time Rendering 3rd》 提炼总结——毛星云

原文

前向渲染

每进行一次前向渲染,会渲染该对象的图元 ,并计算颜色、深度缓冲的值 深度缓冲将决定一个片元是否可见 如果可见 将颜色缓冲中的值更新。 前向渲染是在fragement shader之后做深度测试所以,所有物体就算被遮挡也要走一遍fs,如果多光源就要走光源,所有物体像素次fs(也可以通过 early depth test来解决这个问题)。

举个例子:场景有X个物体 每个物体又被Y个光影响 那渲染整个场景需要多少个Pass呢? 答案是XY个。 如果有很多个物体扎堆 这性能消耗是非常巨大的。

不过 unity也对这种情况进行了限制 那先说下前向渲染中的照明方式吧 分别是 :逐顶点、逐像素和球谐函数(SH)。而在unity中 逐像素若干:平行光为逐像素 其他逐像素可通过开发者通过修改渲染模式(import)进行控制 逐顶点*4 其余用SH方式处理。

前向渲染的优缺点

优:

  • 支持半透明
  • 支持多Pass
  • 支持自定义管线

缺:

  • 光照数量多起来,计算复杂度巨大
  • 访问深度等数据需要额外计算

前向实时剔除

前向渲染的缺点有一些解决办法比如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需要渲染的几何体数量,适用于细碎且数量多的模型。

延迟渲染

前向渲染也说到了 X*Y在面对处理大量实时光源是对性能消耗非常恐怖的 于是延迟渲染就出现了 延迟渲染与前向渲染的最大区别是:额外有一个几何缓冲区(G-buffer)

可以将延迟渲染理解为两个 Pass 的过程:

  1. 几何处理阶段(Geometry Pass)。这个阶段中,我们获取对象的各种几何信息,并将第二步 所需的各种数据储存(也就是渲染)到多个 G-buffer 中;
  2. 光照处理阶段(Lighting Pass)。在这个 pass 中,我们只需渲染出一个屏幕大小的二维矩形, 使用第一步在 G-buffer 中存储的数据对此矩阵的每一个片段计算场景的光照;光照计算的过 程还是和正向渲染以前一样,只是现在我们需要从对应的 G-buffer 而不是顶点着色器(和一些 uniform 变量)那里获取输入变量了。
延迟着色的整个过程

延迟渲染方法一个很大的好处就是能保证在 G-buffer 中的片段和在屏幕上呈现的像素所包含 的片段信息是一样的,因为深度测试已经最终将这里的片段信息作为最顶层的片段。这样保 证了对于在光照处理阶段中处理的每一个像素都只处理一次,所以我们能够省下很多无用的 渲染调用。除此之外,延迟渲染还允许我们做更多的优化,从而渲染更多的光源。

在几何处理阶段中填充 G-buffer 非常高效,因为我们直接储存位置,颜色,法线等对象信息 到帧缓冲中,这个过程几乎不消耗处理时间。 而在此基础上使用多渲染目标(Multiple Render Targets, MRT)技术,我们可以在一个 Pass 之内 完成所有渲染工作。

  1. 几何处理阶段:渲染所有的几何/颜色数据到 G-buffer
  2. 光照处理阶段:使用 G-buffer 计算场景的光照。

延迟渲染就是把光照计算延迟到深度测试之后的渲染方式,延迟着色适合在场景中实时光照很多的情况下使用,而且延迟着色可以对每个光源都按逐像素的方式计算。那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渲染的引擎来说,至少会有深度、模板、颜色、法线、世界位置、金属、粗糙、高光这些信息。

举个例子:延迟渲染主要包含了两个pass,第一个pass不进行任何光照计算,仅计算哪些片元是可见的。(主要通过深度缓冲区实现)。当发现一个片元可见就把它相关信息记录到G缓冲区。在第二个pass中利用G缓冲区的信息如物体表面法线,漫反射系数等进行真正的光照计算。

延迟渲染的优缺点

优:

  • 不依赖场景复杂度,反正所有的信息都存储于几何缓冲区,这个缓冲区可以理解为一张张2D图像光照计算
  • 实际上是在图像空间进行
  • 对后处理支持良好
  • 用更少的shader

缺:

  • 对MSAA支持不友好
  • 透明物体渲染存在问题
  • 占用大量的显存带宽