您的位置:首页 > 移动开发 > Unity3D

unity shader学习笔记(十三)——Unity中的复杂光照之渲染路径

2017-12-04 23:06 429 查看

Unity的渲染路径

  渲染路径(Rendering Path)决定了光照是如何应用到shader中的。因此在shader中需要计算光照时就需要为每个Pass指定它使用的渲染路径。Unity中支持多种渲染路径,Unity5.0之前主要有3种:前向渲染路径(Forward Rendering Path)、延迟渲染路径(Deferred Rendering Path)顶点照明渲染路径(Vertex Lit Rendering Path)。Unity5.0之后的版本中,顶点照明渲染路径被Unity抛弃,也用新的延迟渲染路径代替了原来的延迟渲染路径。

  在Unity中,可以在项目设置中选择渲染路径,也可以在摄像机中设置渲染路径,完成设置之后还可以在每个Pass中设置渲染路径,通过Pass的LightMode标签来实现。以下是Pass中的LightMode标签支持的渲染路径选项:

  

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

  前向渲染路径是传统的渲染方式,每进行一次前向渲染,需要渲染对应的渲染图元,计算两个缓冲区的信息,一个是颜色缓冲区,一个是深度缓冲区,利用深度缓冲来决定片元是否可见。对每个逐像素的光源都会进行这样的计算,如果有N逐像素光源,M个物体,那就需要N*M个Pass来执行,每个物体要使用多个Pass对应多个光源。那么,如果有大量光源,需要执行的Pass数目会非常大。因此,渲染引擎通常会限制每个物体的逐像素光源数目。

  Unity中的前向渲染路径有三种处理光照方式:逐顶点处理逐像素处理球谐函数(Spherical Harmonics, SH)处理。而具体使用哪种模式是由光源的类型和渲染模式决定的。光源类型指的是平行光还是其他类型的光,而渲染模式是值该光源是否是重要的(Important)。在渲染物体时,Unity会跟根据各个光源的设置以及光源对物体的影响程度(如距离物体的距离、光源强度等)对光源进行重要度排序。一定数目的Important光源会按照逐像素处理,剩下的可以按SH方式处理。具体规则如下:

场景中最亮的平行光总是按照逐像素处理;

渲染模式被设置成Not Important的光源按照逐顶点或者SH处理;

渲染模式被设置成Important的光源,会按照逐像素处理;

如果以上得到的逐像素光源数量小于Quality Setting中设置的逐像素光源数量,会有更多的光源按照逐像素的方式处理。

  前向渲染有两种Pass:Base Pase 和 Additional Pass。它们的设置及计算如下:

渲染方式标签光照计算可实现的光照效果
Base PassTags{“LightMode”=”ForwardBase”}
#pragma multi_complie_fwdbase
一个逐像素的平行光以及所有的逐顶点和SH光源光照纹理、环境光、自发光、阴影(平行光的阴影)
Additional PaseTags{“LightMode”=”ForwardAdd”}
Blend One One
#pragma multi_complie_fwdadd
其他影响该物体的逐像素光源每个光源执行一次Pass默认情况不支持阴影,但可以通过使用#pragma multi_complie_fwdadd
  需要注意的几点:

#pragma multi_complie_fwdbase和#pragma multi_complie_fwdadd在官方文档中没有说明,但只有使用这俩个编译指令,才可以在相关的Pass中得到一些正确的光照变量,如光照衰减值。

Base Pass中可以访问光照纹理(lightmap)。

Base Pass中渲染的平行光是默认支持阴影的,而Additional Pass中默认是没有阴影效果的,可以使用#pragma multi_complie_fwdadd_fullshadows代替#pragma multi_complie_fwdadd编译指令,为点光源和聚光灯开启阴影效果,但这需要在Unity内部使用更多的Shader变种。

环境光和自发光在Base Pass中是计算的,如果咋Additional Pass中也计算,就会造成叠加多次的计算。

在Additional Pass中,需要开启和设置混合模式,因为Additional Pass的渲染结果会覆盖掉之前的渲染效果,看起来该物体只收该光源的影响,通常设置为Blend One One

对于前向渲染,一个Unity Shader通常定义一个Base Pass以及一个Additional Pass。通常一个Base Pass只执行一次(定义了多个Base Pass除外,如双面渲染),而一个Additional Pass会根据影响该物体的逐像素光源数目被多次调用。

下面给出了在Unity中对于前向渲染可以在shader中访问到的光照变量:

名称类型描述
_LightColor0float4该Pass处理的逐像素光源颜色
_WorldSpaceLightPos0float4_WorldSpaceLightPos0.xyz是该Pass处理的逐像素光源的位置,如果是平行光,那_WorldSpaceLightPos0.w为0,其他光源为1
_LightMatrix0float4X4从世界空间到光源空间的变换矩阵。可以用于采样cookie和光强衰减纹理
unity_4LightPosX0, unity_4LightPosY0,unity_4LightPosZ0float4仅用于Base Pase。前4个非重要的点光源在世界空间中的位置
unity_4LightAtten0float4仅用于Base Pase。存储了前4个非重要的点光源的衰减因子
unity_LightColorhalf4[4]仅用于Base Pase。存储了前4个非重要的点光源的颜色
前向渲染可以在shader中可以使用的内置光照函数:

函数名描述
float3 WorldSpaceLightDir(float4 V)仅用于前向渲染中,输入一个模型空间中的顶点位置,返回世界空间中从该点到光源的光照方向。内部实现使用了UnityWolrdSpaceLightDir函数,没有归一。
float3 UnityWorldSpaceLightDir(float4 V)仅用于前向渲染中,输入一个世界空间中的顶点位置,返回世界空间中从该点到光源的光照方向。没有归一。
float3 ObjSpaceLightDir(float4 V)仅用于前向渲染中,输入一个模型空间中的顶点位置,返回模型空间中从该点到光源的光照方向。没有归一。
float3 Shade4PointLights(…)仅用于前向渲染中,计算四个点光源的光照,它的参数是已经打包进矢量的光照数据。如unity_4LightPosX0、unity_4LightPosY0、unity_4LightPosZ0、unity_lightColor和unity_4LightAtten0等。前向渲染通常会使用这个函数来计算逐顶点关照
顶点照明渲染路径

  顶点照明渲染路径要求少,性能高,但是效果差,它不支持逐像素才能实现的效果,如阴影、法线映射、高精度的高光反射等。而且它能实现的功能在前向渲染路径中都能完成。它只是用逐顶点的方式计算光照。

  Unity中顶点照明渲染路径通常在一个Pass中完成。在这个Pass中所有的光源对该物体的照明都按照逐顶点处理,是最快速的渲染路径。在Unity中,我们在一个顶点照明的Pass中最多可以访问8个逐顶点光照,如果影响该物体的光源数目小于8,那么数组中剩下的光源颜色会设置为黑色。下面是顶点照明渲染路径在Unity中可以使用的内置变量:

名称类型描述
unity_LightColorhald[4]光源颜色
unity_LightPositionfloat[4]xyz分量是视角空间中的光源位置。如果光源是平行光,那么z分量为0.其他光源类型z分量值为1
untiy_LightAttenhalf[4]光衰减因子,如果光源是聚光灯,x分量是cos(spotAngle/2),y分量是1/cos(spotAngle/4);如果是其他类型的光源,x分量是-1,y分量是1。z分量是衰减的平方,w分量是光源范围开根号的结果
unity_SpotDirectionfloat4[8]如果光源是聚光灯,值为视角空间的聚光灯的位置,否则,值为(0,0,1,0)
Unity中的顶点照明渲染路径可以是用的内置函数如下:

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

  当场景中包含大量光源时,前向渲染会对场景中的每个物体执行多个Pass来计算不同光源对物体的光照,因此会造成性能的急速下降。并且很多计算都是重复的。

  而延迟渲染是一种更加古老的渲染方式,除了会使用颜色缓冲和深度缓冲之外,还会利用额外的缓冲区,就是G缓冲(G-Buffer),G缓冲存储了离蛇形迹最近的表面的信息,如该表面的法线、位置、用于光照计算的材质属性等。

  延迟渲染路径主要包含两个Pass,在第一个Pass中,不进行光照计算,只计算哪些片元是可见的,主要通过深度缓冲来实现,对于可见的片元会将相关的信息存储到G缓冲中,然后在第二个Pass中利用G缓冲中的信息进行光照计算。

  延迟渲染的好处就是,渲染使用的Pass就两个,和场景中的光源数目没有关系。

  在Unity中有两种延迟渲染路径,是旧的延迟渲染路径,一种是Unity5中使用的延迟渲染路径,它们之间的差别很小,如旧的延迟渲染不支持Unity5基于物理的Standard Shader。

  一般当游戏中使用了大量的实时光照时,会选择延迟渲染路径。就是说,当场景中光源数目较多,使用前向渲染会造成性能瓶颈的情况下会使用。而且延迟渲染中的每个光源都可以按逐像素的方式处理。但延迟渲染也有一些确定:

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

不能处理半透明物体;

对显卡有一定的要求,必须支持MRT(Multiple Render Tragets)、Shader Mode3.0及以上、深度渲染纹理以及双面的模版缓冲。

  在Untiy中使用延迟渲染时,需要两个Pass:

第一个Pass用于渲染G缓冲。这个Pass中,我们把物体的漫反射颜色、高光反射颜色、平滑度、法线、自发光和深度等信息渲染到屏幕空间中的G缓冲中,对每个物体这个Pass只执行一次;

第二Pass用于计算真正的光照模型。使用上一个Pass中渲染的数据来计算最终光照,再存储到帧缓冲中。

  默认的G缓冲包含以下几个渲染纹理:

RT0:格式是ARGB32,RGB通道用于存储漫反射颜色,A通道没有被使用;

RT1:格式是ARGB32,RGB通道用于存储高光反射颜色,A通道用于存储高光反射的指数部分;

RT2:格式是ARGB2101010,RGB用于存储法线,A通道没有被使用;

RT3:格式是ARGB32(非HDR)或ARGBHalf(HDR),用于存储自发光+lightmap+反射探针;

深度缓冲和模版缓冲。

  当在第二个Pass中计算光照时,默认情况下仅可以使用Unity内置的Standard光照模型,如果要使用其它光照模型,要替换掉原有的Internal-DeferredShading.shader文件。下面是延迟渲染路径中可以使用的内置变量:

名称类型描述
_LightColorfloat4光源颜色
_LightMatrix0float4x4从世界空间到光源空间的变换矩阵。可用于采样cookie和光强度衰减纹理
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  unity