Unity渲染路径(Unity’s Rendering Path)详解1——前向渲染(Forward Rendering path)

Unity渲染路径详解1

渲染路径概述

渲染路径可以理解为应用层信息在着色器当中接收怎样的数据输入,经过怎样的处理,最后以怎样的方式呈现在二维平面上。
不同的渲染路径要求的信息不同,计算方式不用,计算量不同,消耗便不同。针对不同的平台,不同的硬件环境,应该选择适合的渲染路径。

在Unity中不同的渲染路径对应着不同光照信息的输入,不同光照的处理方式,不同的阴影生成方式,最后不同的呈现方式等等。
在Unity当中通过为Pass定义特殊Tag和特定的编译指令来控制使用的渲染路径,获得对应的光照信息。

前向渲染、正向渲染(Forward Rendering Path)

在Unity当中使用定义LightMode为ForwardBase和ForwardAdd 两种Tag的两个Pass来实现前向渲染:

Pass{
Tags{ "LightMode" = "ForwardBase" }

}
Pass{
Tags{ "LightMode" = "ForwardAdd" }
}

在场景当中,一个物体可能会受到多个光源的影响,如果对这些光源都采取同一种光照计算方式(比如逐像素光照),那么对应的消耗可能会很高,而且一些光源本身对物体的影响并不大,所以应该根据光源的特质分类进行计算,达到可以令人满意的效果。

在前向渲染当中,采用的分类策略为:
最亮的光源采取逐像素光照;而后最多4个光源采用逐顶点光照;其余的光源采用SH光照(球谐函数光照,计算速度更快,这里暂时不做详解)

那么根据什么来对一个光源进行分类呢?主要有以下要求:
1. Render Mode设置为Not Important的光源总是采用逐顶点光照或者SH光照
2. 最亮的directional light总是逐像素光照
3. Render Mode设置为Important的光源总是采用逐像素光照
4. 如果以上分类之后逐像素光照的光源还是小于Quality Setting当中的Pixel Light Count ,则更多的光源按照逐像素光照进行处理,按照亮度排序。

将所有的光源分类之后,要在哪里进行处理呢?同样也有要求:
ForwardBase Pass处理一个逐像素directional light和所有的逐顶点/SH光源。
ForwardAdd Pass处理其余的逐像素光照。

其余实现细节

Bass Pass中一般还会实现光照贴图、环境光以及自发光效果。在这个Pass当中的directional light可以实现阴影。另外使用光照贴图的物体不会受到SH光源的影响。

如果使用OnlyDirectional Tag,那么Bass Pass将只会处理directional light、光照贴图、环境光以及自发光效果,不会处理SH光源和逐顶点光源。

Addtional Pass对每一个逐像素光源都处理一次,在这个Pass当中默认处理光源是没有阴影的,除非定义multi_compile_fwdadd_fullshadows指令。

Unity 具体代码实现

我找到一份比较有参考价值的Unity Forward Rendering 的代码实现(Unity Stander Shader内部实现),这里给到工程的链接
我们只截取和前向渲染相关的部分:

Pass
        {
            Name "FORWARD"
            Tags { "LightMode" = "ForwardBase" }

            Blend [_SrcBlend] [_DstBlend]
            ZWrite [_ZWrite]

            CGPROGRAM
            #pragma target 3.0

            // -------------------------------------

            #pragma shader_feature _NORMALMAP
            #pragma shader_feature_local _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
            #pragma shader_feature _EMISSION
            #pragma shader_feature_local _METALLICGLOSSMAP
            #pragma shader_feature_local _DETAIL_MULX2
            #pragma shader_feature_local _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
            #pragma shader_feature_local _SPECULARHIGHLIGHTS_OFF
            #pragma shader_feature_local _GLOSSYREFLECTIONS_OFF
            #pragma shader_feature_local _PARALLAXMAP

            #pragma multi_compile_fwdbase
            #pragma multi_compile_fog
            #pragma multi_compile_instancing
            // Uncomment the following line to enable dithering LOD crossfade. Note: there are more in the file to uncomment for other passes.
            //#pragma multi_compile _ LOD_FADE_CROSSFADE

            #pragma vertex vertBase
            #pragma fragment fragBase
            #include "UnityStandardCoreForward.cginc"

            ENDCG
        }
        // ------------------------------------------------------------------
        //  Additive forward pass (one light per pass)
        Pass
        {
            Name "FORWARD_DELTA"
            Tags { "LightMode" = "ForwardAdd" }
            Blend [_SrcBlend] One
            Fog { Color (0,0,0,0) } // in additive pass fog should be black
            ZWrite Off
            ZTest LEqual

            CGPROGRAM
            #pragma target 3.0

            // -------------------------------------


            #pragma shader_feature _NORMALMAP
            #pragma shader_feature_local _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
            #pragma shader_feature_local _METALLICGLOSSMAP
            #pragma shader_feature_local _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
            #pragma shader_feature_local _SPECULARHIGHLIGHTS_OFF
            #pragma shader_feature_local _DETAIL_MULX2
            #pragma shader_feature_local _PARALLAXMAP

            #pragma multi_compile_fwdadd_fullshadows
            #pragma multi_compile_fog
            // Uncomment the following line to enable dithering LOD crossfade. Note: there are more in the file to uncomment for other passes.
            //#pragma multi_compile _ LOD_FADE_CROSSFADE

            #pragma vertex vertAdd
            #pragma fragment fragAdd
            #include "UnityStandardCoreForward.cginc"

            ENDCG
        }
        // ------------------------------------------------------------------
        //  Shadow rendering pass
        Pass {
            Name "ShadowCaster"
            Tags { "LightMode" = "ShadowCaster" }

            ZWrite On ZTest LEqual

            CGPROGRAM
            #pragma target 3.0

            // -------------------------------------


            #pragma shader_feature_local _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
            #pragma shader_feature_local _METALLICGLOSSMAP
            #pragma shader_feature_local _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
            #pragma shader_feature_local _PARALLAXMAP
            #pragma multi_compile_shadowcaster
            #pragma multi_compile_instancing
            // Uncomment the following line to enable dithering LOD crossfade. Note: there are more in the file to uncomment for other passes.
            //#pragma multi_compile _ LOD_FADE_CROSSFADE

            #pragma vertex vertShadowCaster
            #pragma fragment fragShadowCaster

            #include "UnityStandardShadow.cginc"

            ENDCG
        }

可以看到各个Pass当中除了特性关键字定义之外,顶点着色器和片段着色器都是以文件引用的方式存在的,可以在Unity编辑器文件夹里的CGIncludes文件夹里面找到这些文件,这里我们只讨论Forward Base Pass和Forward Add Pass,它们引用的都是UnityStandardCoreForward.cginc这个文件,源码为:

#ifndef UNITY_STANDARD_CORE_FORWARD_INCLUDED
#define UNITY_STANDARD_CORE_FORWARD_INCLUDED

#if defined(UNITY_NO_FULL_STANDARD_SHADER)
#   define UNITY_STANDARD_SIMPLE 1
#endif

#include "UnityStandardConfig.cginc"

#if UNITY_STANDARD_SIMPLE
    #include "UnityStandardCoreForwardSimple.cginc"
    VertexOutputBaseSimple vertBase (VertexInput v) { return vertForwardBaseSimple(v); }
    VertexOutputForwardAddSimple vertAdd (VertexInput v) { return vertForwardAddSimple(v); }
    half4 fragBase (VertexOutputBaseSimple i) : SV_Target { return fragForwardBaseSimpleInternal(i); }
    half4 fragAdd (VertexOutputForwardAddSimple i) : SV_Target { return fragForwardAddSimpleInternal(i); }
#else
    #include "UnityStandardCore.cginc"
    VertexOutputForwardBase vertBase (VertexInput v) { return vertForwardBase(v); }
    VertexOutputForwardAdd vertAdd (VertexInput v) { return vertForwardAdd(v); }
    half4 fragBase (VertexOutputForwardBase i) : SV_Target { return fragForwardBaseInternal(i); }
    half4 fragAdd (VertexOutputForwardAdd i) : SV_Target { return fragForwardAddInternal(i); }
#endif

#endif // UNITY_STANDARD_CORE_FORWARD_INCLUDED

可以看到用宏实现了两个分支,其中UNITY_STANDARD_SIMPLE分支采用的是简化着色器,官方文档的解释为采用简化BRDF3(具体应该是光照处理的数学公式不同,如果理解错误望指点)时定义UNITY_NO_FULL_STANDARD_SHADER宏,即采用简化版本。我们这里只看下面的标准版。

四个函数明显对应着两个Pass的VS和PS,函数当中调用UnityStandardCore.cginc文件当中的另一个函数。再到UnityStandardCore.cginc文件当中找到对应的函数。

具体的函数解析涉及到的知识较为复杂,我个人还没有完全理解,不敢妄言,这里推荐找到的一篇参考文章,比较详细的解释各个函数的内部实现:【Unity】Standard Shader实现分析

参考

Unity官方文档
【Unity】Standard Shader实现分析
球谐光照(spherical harmonic lighting)解析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章