unity的渲染路径(前向渲染路径、延迟渲染路径、顶点照明渲染路径)

unity的渲染路径

在unity中,渲染路径决定了光照是如何应用到unity shader中的,如果要和光源打交道,我们需要为每个Pass指定它使用的渲染路径,只有为shader正确地选择和设置了需要的渲染路径,该shader的光照计算才能被正确的执行。

在unity5.0版本之前,主要有三种渲染路径:前向渲染路径、延迟渲染路径、顶点照明渲染路径。在unity5.0版本之后,unity做了很多修改和变化:顶点照明渲染路径被抛弃(但还被兼容),新的渲染路径代替了原来的延迟渲染路径。

一般情况下,一个项目只使用一种渲染路径,但有时我们希望可以使用多个渲染路径,例如摄像机A渲染的物体使用前向渲染,B使用延迟渲染,我们可以在每个摄像机的渲染路径设置中设置该摄像机的渲染路径。注意,如果当前显卡不支持所选择的渲染路径,unity会自动使用更低一级的渲染路径。例如一个GPU不支持延迟渲染,unity会使用前向渲染。

完成上面设置后,我们可以在每个Pass中使用标签来指定该Pass使用的渲染路径。通过设置LightMode标签实现的,不同的渲染路径可能包含多种标签的设置。

例如:

Pass{   Tags{ “LightMode”=“ForwardBase” }  }

下面是LightMode标签支持的渲染路径设置选项:

Always  //不管使用哪种渲染路径,该Pass总会被渲染,但是不会计算任何光照
ForwardBase //用于前向渲染,该Pass会计算环境光,最重要的的平行光,逐顶点/SH光源和Lightmaps
ForwardAdd  //用于前向渲染,该Pass会计算额外的逐像素光源,每个Pass对应一个光源
Deferred    //用于延迟渲染,该Pass会渲染G缓冲(G_buffer)
ShadowCaster    //把物体的深度信息渲染到阴影映射纹理或一张深度纹理中
PrepassBase //用于遗留延迟渲染,该Pass会渲染法线和高光反射的指数部分
PrepassFinal    //用于遗留延迟渲染,该Pass通过合并纹理、光照、自发光来渲染得到的最后的颜色
Vertex,VertexLMRGBM和VertexLM    //用于遗留的顶点照明渲染

指定渲染路径是我们和unity底层渲染引擎的一次重要沟通,如果为Pass设置了前向渲染路径标签,相当于告诉unity我准备前向渲染了,把那些光照属性按前向渲染的流程准备好,一会要用。随后通过unity提供的内置变量访问这些属性,如果没有指定任何渲染路径(实际上在unity5.X版本中如果使用了前向渲染又没有为pass指定任何前向渲染适合的标签,就会被当成一个顶点照明渲染路径等同的Pass,那么一些光照变量很可能不会被正确的赋值,我们计算的效果也有可能是错误的)

 一、前向渲染路径

这是最常用的渲染路径,我们首先概括前向渲染路径的原理,再给出unity对于前向渲染路径的实现细节和要求,最后给出unity shader中哪些内置变量是用于前向渲染路径的。

1. 前向渲染路径的原理

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

如果没有通过深度测试,说明该片元是不可见的->如果可见进行光照计算->更新帧缓冲

对于每个逐像素光源,我们都需要进行上面一次完整的渲染流程。如果一个物体在多个逐像素光源的影响区域内,那么该物体就需要执行多个Pass,每个Pass计算一个逐像素光源的光照结果,然后在帧缓冲把这些光照结果混合起来得到最终的颜色值。如何有大量追像素光照,那么需要执行的Pass数目也会很大,因此渲染引擎通常会限制每个物体的逐像素光照的数目。

2. Unity中的前向渲染

事实上一个Pass不仅仅可以用来计算逐像素光照,也可以用来计算逐顶点等其他光照。在unity中,前向渲染有三种处理光照(即照亮物体)的方式:逐顶点处理、逐像素处理、球谐函数(SH)处理,而决定光源使用哪种处理模式取决于它的类型和渲染模式。光源类型指是平行光还是其他类型光源,光源的渲染模式指的是光源是否是重要的。如果我们把一个光照的模式设置为important,意味着告诉unity这个光源很重要,希望可以认真对待它,把他当成一个逐像素光源来处理。

在前向渲染中,当我们渲染一个物体时,unity会根据场景中各光源的设置以及这些光源对物体的影响程度(例如距离该物体的远近,光照强度等)对这些光源进行一个重要排序,一定数目的光源会按照逐像素的方式处理,然后最多有4个光源按照逐顶点的方式处理,剩下的光源可以按SH方式处理。

Unity使用的判断规则如下:
  •   场景中最亮的平行光总是按照逐像素处理的。
  •   渲染模式被设置成Not important的光源会按照逐顶点或者SH处理。
  •   渲染模式被设置成important的光源,会按照逐像素处理。

如果根据以上规则得到的逐像素光源数量小于Quality Setting中的逐像素光源数量,会有更多的光源以逐像素的方式进行渲染。

Base Pass 可实现的光照效果:光照纹理、环境光、自发光、阴影(平行光的阴影)。 光照计算:一个逐像素的平行光以及所有逐顶点和SH光源。

Additional Pass 可实现的光照效果:默认情况下不支持阴影,可以通过#ragma multi_compile_fwdadd_fullshadows编译指令来开启阴影。 光照计算:其他影响该物体的逐像素光源,每个光源执行一次Pass。

以上有几点说明的地方:

(1)首先,渲染设置中,我们除了设置了Pass标签外,还使用了#pragma multi_compile_fwdbase这样的编译指令。这些编译指令会保证Unity可以为相应类型的Pass生成所需要的Shader变种,这些变种会处理不同条件下单渲染逻辑,例如是否使用光照贴图、当前处理哪种光源类型、是否开启了阴影等。同时Unity也会在背后声明相关的内置变量(如_LightColor0)传递到Shader中,通俗来讲,只有分别为Base Pass和Addtional Pass使用这两个编译指令,我们才可以在相关的Pass中得到一些正确的光照变量,例如光照衰减值等等。

(2)Base Pass中渲染的平行光默认是支持阴影的(如果开启了光源的阴影功能),而Additional Pass中渲染的光源在默认情况下是没有阴影效果的,即便我们在它的Light组件中设置了有阴影的Shadow_Type。但我们可以在Additional Pass中使用#pragma_multi_compile_fwdadd_fullshadows代替#pragma_multi_compile_fwdadd编译指令,为点光源和聚光灯开启阴影效果,但这需要更多的Shader变种。

(3)环境光和自发光也是在Base Pass中计算的,这是因为对于一个物体来说,环境光和自发光我们只希望计算一次即可,而如果我们在Addtional Pass中计算这两种光照,就会造成叠加多次环境光和自发光,这不是我们想要的。

(4)在Additional Pass的渲染设置中,我们还开启和设置了混合模式。这是因为,我们希望每个Additional Pass可以与上一次的光照结果在帧缓存中进行叠加,从而得到最终的有多个光照的渲染效果。我们如果没有开启和设置混合模式,那么Additional Pass的渲染结果会覆盖掉之前的渲染结果,看起来就好像该物体只受该光源的影响。通常情况下,我们选择的混合模式是Blend One One。

(5)对于前向渲染来说,一个Unity Shader通常会定义一个Base Pass(Base Pass也可以定义多次,例如需要双面渲染等情况)以及一个Additional Pass。一个Base Pass仅会执行一次,而一个Addtional Pass会根据影响该物体的其他逐像素光源的数目被多次调用,即每个逐像素光源会执行一次Additional Pass。

3. 内置的光照变量和函数

前面说过,根据我们使用的渲染路径,Unity会把不同的光照变量传递给shader。

在unity5中,对于前向渲染,我们可以在shader中访问到的光照变量如下:

名称 类型 描述
_LightColor0 float4 该Pass处理的逐像素光源的颜色
_WorldSpaceLightPos0  float4 _WorldSpaceLightPos0.xyz,是该Pass处理的逐像素光源的位置。如果该光源是平行光,那么_WorldSPaceLightPos.w是0,其他光源类型是1。
_LightMatrix0 float4X4 从世界空间到光源空间的变换矩阵。可以用于采样cookie和光强衰减(attenuation)纹理
unity_4LightPosX0,unity_4LightPosY0,unity_4LightPosZ0  float4 仅用于Base Pass。前4个非重要点光源在世界空间中的位置。
unity_4LightAtten0 float4  仅用于Base Pass。存储了前4个非重要点光源你的衰减因子。
unity_LightColor  half4[4]  仅用于Base Pass。存储了前4个非重要的点光源的颜色。

 
前向渲染可以使用的内置光照函数如下:

函数名 描述
float3 WorldSpaceLightDir(float4 v) 仅可用于前向渲染。输入一个模型空间中的顶点位置,返回世界空间中从该点到光源的光照方向。内部实现使用了UnityWorldSpaceLightDir函数。没有被归一化。
float3 UnityWorldSpaceLightDir(float4 v)  仅可用于前向渲染。输入一个世界空间中的顶点位置,返回世界空间中从该点到光源的光照方向。
float3 ObjSpaceLightDir(float4 v)  仅可用于前向渲染中。输入一个模型空间中的顶点位置,返回模型空间中从该点到光源的光照方向
float3 Shade4PointLights(…) 仅可用于前向渲染中。计算四个点光源的光照,它的参数是已经打包进矢量的光照数据,通常就是上表中的内置变量。前向渲染通常会使用这个函数来计算逐顶点光照。

二、顶点照明的渲染路径

顶点照明渲染路径是对硬件配置要求最少,运算性能最高,但同时也是得到的效果最差的一种类型,它不支持那些逐像素才能得到的效果,例如阴影、法线映射、高精度的高光反射等。实际上它仅仅是前向渲染路径的一个子集,也就是说所有可以在顶点照明渲染路径实现的功能都可以在前向渲染路径中完成,顶点照明路径只是使用了逐顶点的方式来计算光照。但如果选择使用顶点照片渲染路径,那么unity会只填充那些逐顶点相关的光源变量,意味着我们不可以使用一些逐像素光照变量。

1. unity中的顶点照明渲染

顶点照明渲染路径通常在一个Pass中就可以完成对物体的渲染,这个pass中我们会计算我们关心的所有光源对该物体的照明,并且这个计算是按逐顶点处理的。

2. 可访问的内置变量和函数

在Unity中,我们可以在一个定点照明的Pass中最多访问到8个逐顶点光源。定点照明渲染路径中可以使用的内置变量:

名称  类型 描述
unity_LightColor half4[8] 光源颜色
Unity_LightPosition float4[8] xyz分量是视角空间中的光源位置。如果光源是平行光,w分量为0,其他光源类型z分量值为1。
unity_LightAtten half4[8] 光源衰减因子。如果光源是聚光灯,x分量是cos(spotAngle/2),y分量是1/cos(spotAngle/4);如果是其他类型的光源,x分量是-1,y分量是1。z分量是衰减的平方,w分量是光源范围开根号的结果。
unity_SpotDirection float4[8] 如果光源是聚光灯的话,值为视角空间的聚光灯位置;如果是其他类型的光源,值为(0,0,1,0)

定点照明渲染路径可以使用的内置函数:

函数名 描述
float3 ShadeVertexLights(float4 vertex,float3 normal)  输入模型空间中的顶点位置和法线,计算四个逐顶点光源的光照以及环境光。内部实现实际上调用了ShadeVertexLightsFull函数
float3 ShadeVertexLightsFull(float4 vertex,float3 normal,int lightCount,bool spotLight)  输入模型空间中的顶点位置和法线,计算lightCount个光源的光照以及环境光。如果spotLight值为true,那么这些光源会被当成聚光灯来处理,虽然结果更加精确,但计算更加耗时;否则,按点光源处理。

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

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

1. 延迟渲染的原理

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

可以看出延迟渲染使用的Pass数目通常就是两个,这跟场景中包含的光源数目是没有关系的。换句话说,延迟渲染的效率不依赖于场景的复杂度,而是和我们使用的屏幕空间的大小有关。这是因为,我们需要的信息都存储在缓冲区中,而这些缓冲区可以理解成是一张张2D图像,我们的计算实际上就是在这些图像空间中进行的。

2. Unity中的延迟渲染

如果游戏中使用了大量的实时光照。我们可能希望选择延迟渲染路径,但这种路径需要一定的硬件支持。延迟渲染路径中的每个光源都可以按照逐像素的方式处理。

但是延迟渲染也有缺点:

不支持真正的抗锯齿功能。

不能处理半透明物体。

对显卡有一定要求。

当使用延迟渲染时,unity要求我们提供两个pass。

(1)第一个pass用于渲染G缓冲。在这个pass中,我们会把物体的漫反射颜色、高光反射颜色、平滑度、法线、自发光和深度等信息渲染到屏幕空间的G缓冲区中,对于每个物体来说,这个pass仅会执行一次。

(2)第二个pass用于计算真正的光照模型,这个pass会使用上一个pass中渲染的数据来计算最终的光照颜色,再存储到帧缓冲中。

默认的G缓冲区包含了以下几个渲染纹理:(注意Unity版本不同可能储存内容会不同)
RT0:格式是ARGB32,RGB通道用于存储漫反射颜色,A通道没有被使用。
RT1:格式是ARGB32,RGB通道用于存储高光反射颜色,A通道用于存储高光反射的指数部分。
RT2:格式是ARGB2101010,RGB通道用于存储法线,A通道没有被使用。
RT3:格式是ARGB32(非HDR)或ARGBHalf(HDR),用于存储自发光+lightmap+反射探针。
深度缓冲和模板缓冲。

在第二个Pass中计算光照时,默认情况下只可以使用Unity内置的Standard光照模型。

延迟渲染路径中可以使用的内置变量:

名称 类型 描述
LightColor float4 光源颜色
_LightMatrix0 float4x4 从世界空间到光源空间的变换矩阵。可以用于采样cookie和光强衰减纹理。

版权声明:本文为CSDN博主「小橙子0」的原创文章,
遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cgy56191948/article/details/100171694

最新文章