Forward Rendering Deferred Rendring

前向渲染和延迟渲染

Posted by Yoko on March 25, 2020

在Unity里,渲染路径(Rendering Path)决定了光照是如何应用到Unity Shader中的。Unity支持多种类型的渲染路径。主要有3种:前向渲染路径(Forward Rendering Path)、延迟渲染路径(Deferred Rendering Path)、和顶点照明渲染路径(Vertex Lit Rendering Path)。顶点照明渲染路径已被淘汰,仅讨论前向渲染和新的延迟渲染。

前向渲染

前向渲染的原理

每进行一次前向渲染,我们需要渲染该对象的渲染图元,并计算两个缓冲区的信息,一个是颜色缓冲区,一个是深度缓冲区。我们利用深度缓冲来决定一个片元是否可见,如果可见就更新颜色缓冲区中的颜色值。

我们可以利用下面的伪代码来描述前向渲染的大致过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Pass
{
  for(each primitive in this model)
  {
    for(each fragment covered by this primitive)
    {
      if(failed in depth test)
      {
        //如果没有通过深度测试,就说明改片元是不可见的
        discard;
      }
      else
      {
        //如果该片元可见
        //就进行光照计算
        float4 color = Shading(marerialInfo, pos, normal, lightDir, viewDir);
        //更新帧缓冲
        writeFrameBuffer(fragment, color);
      }
    }
  }
}

Unity中的前向渲染

在前向渲染路径中,若干个最亮的且能照亮每个物体的光源执行逐片元光照计算。剩下的光源中,最多4个点光源执行逐顶点光照计算,剩下的光源则以球谐函数(spherical harmonics)的方式计算光照。球谐函数的方式要快得多,但是一种近似的模拟。

向前渲染缺陷

当场景中包含大量实时光源时,向前渲染的性能会急速下降。例如,如果在场景中的某一块区域放置了多个光源,这些光源影响的区域相互重叠,那么为了得到最终的光照效果,我们就需要为该区域内的每个物体执行多个Pass来计算不同光源对该物体的光照结果,然后在颜色缓冲中把这些结果混合起来得到最终的光照效果。然而,没执行一个Pass都需要重新渲染一遍物体,但很多计算实际上是重复的。

延迟渲染

延迟渲染原理

延迟渲染除了前向渲染中使用的颜色缓冲和深度缓冲外,延迟渲染还会使用额外的缓冲区,这些缓冲区也被统称为G缓冲区(G-buffer),其中G是英文Geometry的缩写。G缓冲区存储了我们所关心的表面(通常指的是离摄像机最近的表面)的其他信息,例如该表面的法线、位置、用于光照计算的材质属性等。

延迟渲染的过程大致可以用下面的伪代码来描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Pass 1
{
  //第一个Pass不需要进行真正的光照计算
  //仅仅把光照计算需要的信息存储到G缓冲区中
  for(each primitive in this model)
  {
    for(each fragment covered by this primitive)
    {
      if(failed in depth test)
      {
        //如果没有通过深度测试,说明该片元是不可见的
        discard;
      }
      else
      {
        //如果该片元可见
        //就把需要的信息存储到G缓冲中
        whiteGBuffer(materialInfo, pos, normal, lightDir, viewDir);
      }
    }
  }
}
Pass 2
{
  //利用G缓冲中的信息进行真正的光照计算
  for(each pixel in the screen)
  {
    if(the pixel is valid)
    {
      //如果该像素是有效的
      //就读取它对应的G缓冲中的信息
      readGBuffer(pixel, materialInfor, pos, normal, lightDir, viewDir);
      //根据读取到的信息进行光照计算
      float4 color = Shading(materialInfo, pos, normal, lightDir, viewDir);
      whiteFrameBuffer(pixel, color);
    }
  }
}

Unity中的延迟渲染

延迟渲染主要包含两个Pass。在第一个Pass中不进行任何光照计算,而是仅仅计算哪些片元是可见的,这主要是通过深度缓冲技术来实现的,当发现一个片元是可见的,我们就把它的相关信息存储到G缓冲区中,然后,在第二个Pass中,我们利用G缓冲区的各个片元信息,例如表面法线、视角方向、漫反射系数等,进行真正的光照计算。

延迟渲染缺陷

1.不支持真正的抗锯齿(anti-aliasing)功能

2.不能处理半透明物体

3.对显卡有一定的要求。如果要使用延迟渲染的话,显卡必须支持 MRT(Multiple Render Targets)、Shader Mode 3.0及以上、深度渲染纹理以及双面的模板缓冲。

前向渲染和延迟渲染的区别

1.向前渲染是把场景中的每个像素渲染一遍输出结果,延迟渲染是把渲染的相关信息存储到G缓冲区进行光照计算再输出最终结果

2.延迟渲染会占用较高的显存带宽

3.前向渲染会执行多个Pass,BasePass执行一次,Additional Pass会根据影响该物体的其他逐像素光源的数目被多次调用;延迟渲染执行两个Pass,一个Pass渲染相关信息,一个Pass进行光照计算

Reference

《Unity入门精要》

《Unity3D内建着色器源码剖析》