UnityShader入门精要——Unity的渲染路径

大多数情况下,一个项目只使用一种渲染路径,因此我们可以为整个项目设置渲染时的渲染路径。我们可以通过在Unity的Edit→Project Settings - 十Player →Other Settings→Rendering Path中选择项目所需的渲染路径。默认情况下,该设置选择的是前向渲染路径,如图9.1所示。但有时,我们希望可以使用多个渲染路径,例如摄像机A渲染的物体使用前向渲染路径,而摄像机B渲染的物体使用延迟渲染路径。这时,我们可以在每个摄像机的渲染路径设置中设置该摄像机使用的渲染路径,以覆盖Project Settings中的设置,如图9.2所示。

在上面的设置中,如果选择了Use Player Settings,那么这个摄像机会使用Project Settings中的设置;否则就会覆盖掉Project Settings 中的设置。需要注意的是,如果当前的显卡并不支持所选择的渲染路径,Unity会自动使用更低一-级的渲染路径。例如,如果一个GPU不支持延迟渲染,那么Unity就会使用前向渲染。

在Unity中通过LightMode标签来指定Pass使用的渲染路径。

LightMode 标签支持的渲染路径设置选项

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

如果我们没有指定任何渲染路径, 那么一些光照变量很可能不会被正确赋值,我们计算出的效果也就很有可能是错误的。

        1. 前向渲染路径

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

Unity中的前向渲染:在Unity中,前向渲染路径有3种处理光照(即照亮物体)的方式:逐顶点处理、逐像素处理,球谐函数(Spherical Harmonics, SH)处理。而决定一个光源使用哪种处理模式取决于它的类型和渲染模式。光源类型指的是该光源是平行光还是其他类型的光源,而光源的渲染模式指的是该光源是否是重要的(Important)。在前向渲染中,当我们渲染一个物体时,Unity 会根据场景中各个光源的设置以及这些光源对物体的影响程度(例如,距离该物体的远近、光源强度等)对这些光源进行一个重要度排序。其中,一定数目的光源会按逐像素的方式处理,然后最多有4个光源按逐顶点的方式处理,剩下的光源可以按SH方式处理。Unity使用的判断规则如下:

●场景中最亮的平行光总是按逐像素处理的。
●渲染模式被设置成Not Important的光源,会按逐顶点或者SH处理。
●渲染模式被设置成Important的光源,会按逐像素处理。
●如果根据以上规则得到的逐像素光源数量小于QualitySetting中的逐像素光源数量(Pixel LightCount),会有更多的光源以逐像素的方式进行渲染。

前向渲染的两种Pass:Base Pass和Additional Pass:

●首先,可以发现在渲染设置中,我们除了设置了Pass的标签外,还使用了#pragma multi_compile_ fwdbase 这样的编译指令。虽然#pragma multi_ compile_ fwdbase#pragma multi_ compile_ fwdadd在官方文档中还没有给出相关说明,但实验表明,只有分别为Bass Pass和Additional Pass 使用这两个编译指令,我们才可以在相关的Pass中得到一些正确的光照变量,例如光照衰减值等。
●Base Pass中渲染的平行光默认是支持阴影的(如果开启了光源的阴影功能),而Additional Pass中渲染的光源在默认情况下是没有阴影效果的,即便我们在它的Light组件中设置了有阴影的Shadow Type。 但我们可以在Additional Pass 中使用#pragma multi_ compile_fwdadd_ fullshadows 代替#pragma multi_ compile_ fwdadd 编译指令,为点光源和聚光灯开启阴影效果,但这需要Unity在内部使用更多的Shader变种。
●环境光和自发光也是在 Base Pass中计算的。这是因为,对于一个物体来说,环境光和自发光我们只希望计算一次即可, 而如果我们在Additional Pass中计算这两种光照,就会造成叠加多次环境光和自发光,这不是我们想要的。
●在Additional Pass的渲染设置中,我们还开启和设置了混合模式。这是因为,我们希望每个Additional Pass可以与上一次的光照结果在帧缓存中进行叠加,从而得到最终的有多个光照的渲染效果。如果我们没有开启和设置混合模式,那么Additional Pass的渲染结果会覆盖掉之前的渲染结果,看起来就好像该物体只受该光源的影响。通常情况下,我们选择的混合模式是Blend One One。
●对于 前向渲染来说,一个Unity Shader 通常会定义一个 Base Pass (Base Pass也可以定义多次,例如需要双面渲染等情况)以及一个Additional Pass。 一个Base Pass 仅会执行一次(定义了多个Base Pass的情况除外),而一个Additional Pass会根据影响该物体的其他逐像素光源的数目被多次调用,即每个逐像素光源会执行一次Additional Pass。

内置的光照变量和函数:

前向渲染可以使用的内置光照变量

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

前向渲染可使用的内置光照函数

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

        2. 顶点照明渲染路径

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

Unity中的顶点照明渲染:顶点照明渲染路径通常在一个Pass 中就可以完成对物体的渲染。在这个Pass中,我们会计算我们关心的所有光源对该物体的照明,并且这个计算是按逐顶点处理的。这是Unity中最快速的渲染路径,并且具有最广泛的硬件支持(但是游戏机上并不支持这种路径)。

可访问的内置变量和函数:




        3. 延迟渲染路径

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

原理:延迟渲染主要包含了两个Pass。在第一个Pass中,我们不进行任何光照计算,而是仅仅计算哪些片元是可见的,这主要是通过深度缓冲技术来实现,当发现一个片元是可见的,我们就把它的相关信息存储到G缓冲区中,(该Pass只执行一次)。然后,在第二个Pass中,我们利用G缓冲区的各个片元信息,例如表面法线、视角方向、漫反射系数等,进行真正的光照计算。延迟渲染使用的Pass数目通常就是两个,这跟场景中包含的光源数目是没有关系的。换句话说,延迟渲染的效率不依赖于场景的复杂度,而是和我们使用的屏幕空间的大小有关。这是因为,我们需要的信息都存储在缓冲区中,而这些缓冲区可以理解成是一张张2D图像,我们的计算实际上就是在这些图像空间中进行的。

Unity中的延迟渲染:对于延迟渲染路径来说,它最适合在场景中光源数目很多、如果使用前向渲染会造成性能瓶颈的情况下使用。而且,延迟渲染路径中的每个光源都可以按逐像素的方式处理。但是,延迟渲染也有一些缺点:
●不支持真正的抗锯齿(anti-aliasing) 功能。
●不能处理半透明物体。
●对显卡有一定要求。如果要使用延迟渲染的话,显卡必须支持MRT ( Multiple Render Targets)、Shader Mode 3.0及以上、深度渲染纹理以及双面的模板缓冲。

可访问的内置变量和函数;

注:本节内容很多,Unity Shader入门精要中也主要以前向渲染为主。 

猜你喜欢

转载自blog.csdn.net/weixin_51327051/article/details/123457642