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)解析

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