一、Unity5中的Standard Shader
Unity5中重點推出了一套基于物理的著色(Physically Based Shading,PBS)的多功能Shader,叫做標準著色器(Standard Shader)。這套Shader的設(shè)計初衷是化繁為簡。想用這樣的一個多功能Shader,來代替之前多種多樣的Shader各司其職,對于不同的材質(zhì)效果,需要不同的Shader的局面。
Unity 5中目前有兩個標準著色器,一個名為Standard,我們稱它為標準著色器的標準版,另一個名為Standard(Specular Setup),我們稱它為標準著色器的高光版,它們共同組成了一個完整的PBS光照明模型,且非常易于使用。其實這兩個Shader基本差不多,只是有細微的屬性參數(shù)上的區(qū)別。標準版這邊的_Metallic(金屬性)、_MetallicGlossMap(金屬光澤貼圖),被高光版的_SpecColor(高光顏色)、_SpecGlossMap(高光顏色法線貼圖)所代替。
標準著色器主要是針對硬質(zhì)表面(也就是建筑材質(zhì))而設(shè)計的,可以處理大多數(shù)現(xiàn)實世界的材質(zhì),例如石頭、陶瓷、銅器、銀器或橡膠等。同時,它也可以非常出色地處理一些非硬質(zhì)表面的材質(zhì),例如皮膚、頭發(fā)或布料等。
Unity5 Standard Shader的演示Demo
http://www.iqiyi.com/w_19rquxac31.html
1.1 Physically Based Shading(基于物理的著色)
基于物理的著色(Physically Based Shading,簡稱PBS)就是以某種方式模擬現(xiàn)實中材質(zhì)和光照的相互作用的一種著色方法。這種方法在需要光照和材質(zhì)更加直觀和逼真地協(xié)同工作的場合下優(yōu)勢非常明顯?;谖锢淼闹M了光線在現(xiàn)實中的行為,實現(xiàn)了在不同的光照條件下的逼真效果。為實現(xiàn)這種效果需要遵循各種物理原理,包括能量守恒(也就是物體反射出去的光量不可能超過所接收的光量),F(xiàn)resnel反射(所有表面反射在掠射角(grazing angles)處更加強烈),以及物體表面是如何自我遮擋等原理。
ps:另一篇筆記
1.2 如何使用標準著色器
可以在Unity5中任意材質(zhì)的Shader選項中的最前面看到兩個Standard Shader字樣的選項。其中第一個是普通版,第二個帶Specularsetup是高光版。
標準著色器標準版材質(zhì)界面:

標準著色器的高光版材質(zhì)界面:

Unity官方文檔:
http://docs.unity3d.com/Manual/shader-StandardShader.html
以及這里Unity5 Standard Shader的官方文檔論壇翻譯版:
http://forum.china.unity3d.com/thread-897-1-1.html
1.3 標準著色器的組成
Unity5中標準著色器的組成概括如下:
- 兩個Shader源文件
- 七個CG頭文件
- 一個腳本文件(用于自定義材質(zhì)編輯器UI)
1.3.1 兩個Shader源文件
Stardard.shader著色器源文件 - 標準著色器的標準版
StardardSpecular.shader著色器源文件 - 標準著色器的高光版
1.3.2 七個CG頭文件
- UnityStandardBRDF.cginc頭文件-用于存放標準著色器處理BRDF材質(zhì)屬性相關(guān)的函數(shù)與宏
- UnityStandardConfig.cginc頭文件-用于存放標準著色器配置相關(guān)的代碼(其實里面就幾個宏)
- UnityStandardCore.cginc頭文件-用于存放標準著色器的主要代碼(如頂點著色函數(shù)、片段著色函數(shù)等相關(guān)函數(shù))
- UnityStandardInput.cginc頭文件-用于存放標準著色器輸入結(jié)構(gòu)相關(guān)的工具函數(shù)與宏
- UnityStandardMeta.cginc頭文件-用于存放標準著色器meta通道中會用到的工具函數(shù)與宏
- UnityStandardShadow.cginc頭文件-用于存放標準著色器陰影貼圖采樣相關(guān)的工具函數(shù)與宏
- UnityStandardUtils.cginc頭文件-用于存放標準著色器共用的一些工具函數(shù)
1.3.3 一個腳本文件
StandardShaderGUI.cs腳本文件——定義了特定的自定義編輯器UI界面
標準著色器對應(yīng)材質(zhì)的編輯器外觀不同于一般的Shader,就是因為在Shader末尾書寫了如下的代碼:
//使用特定的自定義編輯器UI界面
CustomEditor "StandardShaderGUI"
二、Unity5標準著色器源代碼剖析:架構(gòu)分析
源碼如下:
//-----------------------------------------------【Shader說明】---------------------------
// Unity5.2.1 Built-in Standard Shader
//--------------------------------------------------------------------------------------------
Shader "Standard"
{
//------------------------------------【屬性值】------------------------------------
Properties
{
//主顏色
_Color("Color", Color) = (1,1,1,1)
//主紋理
_MainTex("Albedo", 2D) = "white" {}
//Alpha剔除值
_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
//平滑、光澤度
_Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5
//金屬性
[Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
//金屬光澤紋理圖
_MetallicGlossMap("Metallic", 2D) = "white" {}
//凹凸的尺度
_BumpScale("Scale", Float) = 1.0
//法線貼圖
_BumpMap("Normal Map", 2D) = "bump" {}
//高度縮放尺度
_Parallax ("Height Scale", Range (0.005, 0.08)) = 0.02
//高度紋理圖
_ParallaxMap ("Height Map", 2D) = "black" {}
//遮擋強度
_OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
//遮擋紋理圖
_OcclusionMap("Occlusion", 2D) = "white" {}
//自發(fā)光顏色
_EmissionColor("Color", Color) = (0,0,0)
//自發(fā)光紋理圖
_EmissionMap("Emission", 2D) = "white" {}
//細節(jié)掩膜圖
_DetailMask("Detail Mask", 2D) = "white" {}
//細節(jié)紋理圖
_DetailAlbedoMap("Detail Albedo x2", 2D) = "grey" {}
//細節(jié)法線貼圖尺度
_DetailNormalMapScale("Scale", Float) = 1.0
//細節(jié)法線貼圖
_DetailNormalMap("Normal Map", 2D) = "bump" {}
//二級紋理的UV設(shè)置
[Enum(UV0,0,UV1,1)] _UVSec ("UV Set for secondary textures", Float) = 0
//混合狀態(tài)的定義
[HideInInspector] _Mode ("__mode", Float) = 0.0
[HideInInspector] _SrcBlend ("__src", Float) = 1.0
[HideInInspector] _DstBlend ("__dst", Float) = 0.0
[HideInInspector] _ZWrite ("__zw", Float) = 1.0
}
//===========開始CG著色器語言編寫模塊===========
CGINCLUDE
//BRDF相關(guān)的一個宏
#define UNITY_SETUP_BRDF_INPUT MetallicSetup
//===========結(jié)束CG著色器語言編寫模塊===========
ENDCG
//------------------------------------【子著色器1】------------------------------------
// 此子著色器用于Shader Model 3.0
//----------------------------------------------------------------------------------------
SubShader
{
//渲染類型設(shè)置:不透明
Tags { "RenderType"="Opaque" "PerformanceChecks"="False" }
//細節(jié)層次設(shè)為:300
LOD 300
//--------------------------------通道1-------------------------------
// 正向基礎(chǔ)渲染通道(Base forward pass)
// 處理方向光,自發(fā)光,光照貼圖等 ...
Pass
{
//設(shè)置通道名稱
Name "FORWARD"
//于通道標簽中設(shè)置光照模型為ForwardBase,正向渲染基礎(chǔ)通道
Tags { "LightMode" = "ForwardBase" }
//混合操作:源混合乘以目標混合
Blend [_SrcBlend] [_DstBlend]
// 根據(jù)_ZWrite參數(shù),設(shè)置深度寫入模式開關(guān)與否
ZWrite [_ZWrite]
//===========開啟CG著色器語言編寫模塊===========
CGPROGRAM
//著色器編譯目標:Model 3.0
#pragma target 3.0
//編譯指令:不使用GLES渲染器編譯
#pragma exclude_renderers gles
// ---------編譯指令:著色器編譯多樣化--------
#pragma shader_feature _NORMALMAP
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature ___ _DETAIL_MULX2
#pragma shader_feature _PARALLAXMAP
//--------著色器編譯多樣化快捷指令------------
//編譯指令:編譯正向渲染基礎(chǔ)通道(用于正向渲染中,應(yīng)用環(huán)境光照、主方向光照和頂點/球面調(diào)和光照)所需的所有變體。
//這些變體用于處理不同的光照貼圖類型、主要方向光源的陰影選項的開關(guān)與否
#pragma multi_compile_fwdbase
//編譯指令:編譯幾個不同變種來處理不同類型的霧效(關(guān)閉/線性/指數(shù)/二階指數(shù)/)
#pragma multi_compile_fog
//編譯指令:告知編譯器頂點和片段著色函數(shù)的名稱
#pragma vertex vertForwardBase
#pragma fragment fragForwardBase
//包含輔助CG頭文件
#include "UnityStandardCore.cginc"
//===========結(jié)束CG著色器語言編寫模塊===========
ENDCG
}
//--------------------------------通道2-------------------------------
// 正向附加渲染通道(Additive forward pass)
// 以每個光照一個通道的方式應(yīng)用附加的逐像素光照
Pass
{
//設(shè)置通道名稱
Name "FORWARD_DELTA"
//于通道標簽中設(shè)置光照模型為ForwardAdd,正向渲染附加通道
Tags { "LightMode" = "ForwardAdd" }
//混合操作:源混合乘以1
Blend [_SrcBlend] One
//附加通道中的霧效應(yīng)該為黑色
Fog { Color (0,0,0,0) }
//關(guān)閉深度寫入模式
ZWrite Off
//設(shè)置深度測試模式:小于等于
ZTest LEqual
//===========開啟CG著色器語言編寫模塊===========
CGPROGRAM
//著色器編譯目標:Model 3.0
#pragma target 3.0
//編譯指令:不使用GLES渲染器編譯
#pragma exclude_renderers gles
// ---------編譯指令:著色器編譯多樣化--------
#pragma shader_feature _NORMALMAP
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature ___ _DETAIL_MULX2
#pragma shader_feature _PARALLAXMAP
//--------使用Unity內(nèi)置的著色器編譯多樣化快捷指令------------
//編譯指令:編譯正向渲染基礎(chǔ)通道所需的所有變體,但同時為上述通道的處理賦予了光照實時陰影的能力。
#pragma multi_compile_fwdadd_fullshadows
//編譯指令:編譯幾個不同變種來處理不同類型的霧效(關(guān)閉/線性/指數(shù)/二階指數(shù)/)
#pragma multi_compile_fog
//編譯指令:告知編譯器頂點和片段著色函數(shù)的名稱
#pragma vertex vertForwardAdd
#pragma fragment fragForwardAdd
//包含輔助CG頭文件
#include "UnityStandardCore.cginc"
//===========結(jié)束CG著色器語言編寫模塊===========
ENDCG
}
// --------------------------------通道3-------------------------------
// 陰影渲染通道(Shadow Caster pass)
// 將將物體的深度渲染到陰影貼圖或深度紋理中
Pass
{
//設(shè)置通道名稱
Name "ShadowCaster"
//于通道標簽中設(shè)置光照模型為ShadowCaster。
//此光照模型代表著將物體的深度渲染到陰影貼圖或深度紋理。
Tags { "LightMode" = "ShadowCaster" }
//開啟深入寫入模式
ZWrite On
//設(shè)置深度測試模式:小于等于
ZTest LEqual
//===========開啟CG著色器語言編寫模塊===========
CGPROGRAM
//著色器編譯目標:Model 3.0
#pragma target 3.0
//編譯指令:不使用GLES渲染器編譯
#pragma exclude_renderers gles
// ---------編譯指令:著色器編譯多樣化--------
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
//--------著色器編譯多樣化快捷指令------------
//進行陰影投射相關(guān)的多著色器變體的編譯
#pragma multi_compile_shadowcaster
//編譯指令:告知編譯器頂點和片段著色函數(shù)的名稱
#pragma vertex vertShadowCaster
#pragma fragment fragShadowCaster
//包含輔助CG頭文件
#include "UnityStandardShadow.cginc"
//===========結(jié)束CG著色器語言編寫模塊===========
ENDCG
}
// --------------------------------通道4-------------------------------
// 延遲渲染通道(Deferred Render Pass)
Pass
{
//設(shè)置通道名稱
Name "DEFERRED"
//于通道標簽中設(shè)置光照模型為Deferred,延遲渲染通道
Tags { "LightMode" = "Deferred" }
CGPROGRAM
#pragma target 3.0
// TEMPORARY: GLES2.0 temporarily disabled to prevent errors spam on devices without textureCubeLodEXT
#pragma exclude_renderers nomrt gles
//---------編譯指令:著色器編譯多樣化(shader_feature)--------
#pragma shader_feature _NORMALMAP
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature ___ _DETAIL_MULX2
#pragma shader_feature _PARALLAXMAP
//---------編譯指令:著色器編譯多樣化(multi_compile)--------
#pragma multi_compile ___ UNITY_HDR_ON
#pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON
#pragma multi_compile DIRLIGHTMAP_OFF DIRLIGHTMAP_COMBINED DIRLIGHTMAP_SEPARATE
#pragma multi_compile DYNAMICLIGHTMAP_OFF DYNAMICLIGHTMAP_ON
//編譯指令:告知編譯器頂點和片段著色函數(shù)的名稱
#pragma vertex vertDeferred
#pragma fragment fragDeferred
//包含輔助CG頭文件
#include "UnityStandardCore.cginc"
//===========結(jié)束CG著色器語言編寫模塊===========
ENDCG
}
// --------------------------------通道5-------------------------------
//元通道(Meta Pass)
//為全局光照(GI),光照貼圖等技術(shù)提取相關(guān)參數(shù),如(emission, albedo等參數(shù)值)
//此通道并不在常規(guī)的渲染過程中使用
Pass
{
//設(shè)置通道名稱
Name "META"
//于通道標簽中設(shè)置光照模型為Meta
//(截止2015年10月22日,Unity 5.2.1的官方文檔中并沒有收錄此光照模型,應(yīng)該是Unity官方的疏漏)
Tags { "LightMode"="Meta" }
//關(guān)閉剔除操作
Cull Off
//===========開啟CG著色器語言編寫模塊===========
CGPROGRAM
//編譯指令:告知編譯器頂點和片段著色函數(shù)的名稱
#pragma vertex vert_meta
#pragma fragment frag_meta
//---------編譯指令:著色器編譯多樣化--------
#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature ___ _DETAIL_MULX2
//包含輔助CG頭文件
#include "UnityStandardMeta.cginc"
//===========結(jié)束CG著色器語言編寫模塊===========
ENDCG
}
}
//------------------------------------【子著色器2】-----------------------------------
// 此子著色器用于Shader Model 2.0
//----------------------------------------------------------------------------------------
SubShader
{
//渲染類型設(shè)置:不透明
Tags { "RenderType"="Opaque" "PerformanceChecks"="False" }
//細節(jié)層次設(shè)為:150
LOD 150
//--------------------------------通道1-------------------------------
// 正向基礎(chǔ)渲染通道(Base forward pass)
// 處理方向光,自發(fā)光,光照貼圖等 ...
Pass
{
//設(shè)置通道名稱
Name "FORWARD"
//于通道標簽中設(shè)置光照模型為ForwardBase,正向渲染基礎(chǔ)通道
Tags { "LightMode" = "ForwardBase" }
//混合操作:源混合乘以目標混合,即結(jié)果為兩者的混合
Blend [_SrcBlend] [_DstBlend]
// 根據(jù)_ZWrite參數(shù),設(shè)置深度寫入模式開關(guān)與否
ZWrite [_ZWrite]
//===========開啟CG著色器語言編寫模塊===========
CGPROGRAM
//著色器編譯目標:Model 2.0
#pragma target 2.0
// ---------編譯指令:著色器編譯多樣化--------
#pragma shader_feature _NORMALMAP
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature ___ _DETAIL_MULX2
// SM2.0: NOT SUPPORTED shader_feature _PARALLAXMAP
//跳過如下變體的編譯,簡化編譯過程
#pragma skip_variants SHADOWS_SOFT DIRLIGHTMAP_COMBINED DIRLIGHTMAP_SEPARATE
//--------著色器編譯多樣化快捷指令------------
#pragma multi_compile_fwdbase
#pragma multi_compile_fog
//編譯指令:告知編譯器頂點和片段著色函數(shù)的名稱
#pragma vertex vertForwardBase
#pragma fragment fragForwardBase
//包含輔助CG頭文件
#include "UnityStandardCore.cginc"
//===========結(jié)束CG著色器語言編寫模塊===========
ENDCG
}
//--------------------------------通道2-------------------------------
// 正向附加渲染通道(Additive forward pass)
// 以每個光照一個通道的方式應(yīng)用附加的逐像素光照
Pass
{
//設(shè)置通道名稱
Name "FORWARD_DELTA"
//于通道標簽中設(shè)置光照模型為ForwardAdd,正向渲染附加通道
Tags { "LightMode" = "ForwardAdd" }
//混合操作:源混合乘以1
Blend [_SrcBlend] One
//附加通道中的霧效應(yīng)該為黑色
Fog { Color (0,0,0,0) }
//關(guān)閉深度寫入模式
ZWrite Off
//設(shè)置深度測試模式:小于等于
ZTest LEqual
//===========開啟CG著色器語言編寫模塊===========
CGPROGRAM
//著色器編譯目標:Model 2.0
#pragma target 2.0
// ---------編譯指令:著色器編譯多樣化--------
#pragma shader_feature _NORMALMAP
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature ___ _DETAIL_MULX2
//跳過一些變體的編譯
// SM2.0: NOT SUPPORTED shader_feature _PARALLAXMAP
#pragma skip_variants SHADOWS_SOFT
//--------使用Unity內(nèi)置的著色器編譯多樣化快捷指令------------
//編譯指令:編譯正向渲染基礎(chǔ)通道所需的所有變體,但同時為上述通道的處理賦予了光照實時陰影的能力。
#pragma multi_compile_fwdadd_fullshadows
//編譯指令:編譯幾個不同變種來處理不同類型的霧效(關(guān)閉/線性/指數(shù)/二階指數(shù)/)
#pragma multi_compile_fog
//編譯指令:告知編譯器頂點和片段著色函數(shù)的名稱
#pragma vertex vertForwardAdd
#pragma fragment fragForwardAdd
//包含輔助CG頭文件
#include "UnityStandardCore.cginc"
//===========結(jié)束CG著色器語言編寫模塊===========
ENDCG
}
// --------------------------------通道3-------------------------------
// 陰影渲染通道(Shadow Caster pass)
// 將將物體的深度渲染到陰影貼圖或深度紋理中
Pass
{
//設(shè)置通道名稱
Name "ShadowCaster"
//于通道標簽中設(shè)置光照模型為ShadowCaster。
//此光照模型代表著將物體的深度渲染到陰影貼圖或深度紋理。
Tags { "LightMode" = "ShadowCaster" }
//開啟深入寫入模式
ZWrite On
//設(shè)置深度測試模式:小于等于
ZTest LEqual
//===========開啟CG著色器語言編寫模塊===========
CGPROGRAM
//著色器編譯目標:Model 2.0
#pragma target 2.0
//---------編譯指令:著色器編譯多樣化(shader_feature)--------
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
//編譯指令:跳過某些變體的編譯
#pragma skip_variants SHADOWS_SOFT
//快捷編譯指令:進行陰影投射相關(guān)的多著色器變體的編譯
#pragma multi_compile_shadowcaster
//編譯指令:告知編譯器頂點和片段著色函數(shù)的名稱
#pragma vertex vertShadowCaster
#pragma fragment fragShadowCaster
//包含輔助CG頭文件
#include "UnityStandardShadow.cginc"
//===========結(jié)束CG著色器語言編寫模塊===========
ENDCG
}
// --------------------------------通道4-------------------------------
//元通道(Meta Pass)
//為全局光照(GI),光照貼圖等技術(shù)提取相關(guān)參數(shù),如(emission, albedo等參數(shù)值)
//此通道并不在常規(guī)的渲染過程中使用
Pass
{
//設(shè)置通道名稱
Name "META"
//于通道標簽中設(shè)置光照模型為Meta
//(截止2015年10月22日,Unity 5.2.1的官方文檔中并沒有收錄此光照模型,應(yīng)該是Unity官方的疏漏)
Tags { "LightMode"="Meta" }
//關(guān)閉剔除操作
Cull Off
//===========開啟CG著色器語言編寫模塊===========
CGPROGRAM
//編譯指令:告知編譯器頂點和片段著色函數(shù)的名稱
#pragma vertex vert_meta
#pragma fragment frag_meta
//---------編譯指令:著色器編譯多樣化--------
#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature ___ _DETAIL_MULX2
//包含輔助CG頭文件
#include "UnityStandardMeta.cginc"
//===========結(jié)束CG著色器語言編寫模塊===========
ENDCG
}
}
//回退Shader為頂點光照Shader
FallBack "VertexLit"
//使用特定的自定義編輯器UI界面
CustomEditor "StandardShaderGUI"
}
標準著色器由兩個SubShader組成。第一個SubShader用于處理Shader Model 3.0,有5個通道,第二個SubShader用于處理Shader Model 2.0, 有4個通道
詳細的架構(gòu)如下圖:

三、Deferred Render(延遲渲染)
Deferred Render和Deferred Shading有一點細微的差別。
- Deferred Shading:將所有的Shading全部轉(zhuǎn)到Deferred階段進行。
- Deferred Render:只是有選擇地將Lighting轉(zhuǎn)到Deferred階段進行。
Unity中默認使用的是Deferred Shading。
而延遲渲染,一言以蔽之,就是將光照/渲染的計算延遲到第二步進行,避免多次渲染同一個像素,從而減少多余的計算操作,以提高渲染效率的一種先進的渲染方式。
延遲渲染最大的優(yōu)勢是可以實現(xiàn)同屏中數(shù)量眾多的動態(tài)光源(十幾到幾十個),這在傳統(tǒng)的渲染管線中是很難實現(xiàn)的。
更多延遲渲染的細節(jié),這邊不細說,只是提供一些鏈接,以作進一步了解延遲渲染的導(dǎo)論之用:
- https://zh.wikipedia.org/wiki/%E5%BB%B6%E6%9C%9F%E7%9D%80%E8%89%B2
- http://blog.csdn.net/noslopforever/article/details/3951273
- http://blog.sina.com.cn/s/blog_458f871201017i06.html
- http://www.cnblogs.com/lancidie/archive/2011/08/18/2144748.html
- http://blog.csdn.net/pizi0475/article/details/7932920
- http://www.cnblogs.com/wangchengfeng/p/3440097.html
- http://blog.csdn.net/bugrunner/article/details/7436600
四、著色器編譯多樣化
Unity5中使用了一種被稱為著色器編譯多樣化(Multiple shader program variants)的新技術(shù),常被稱為“megashaders”或“uber shaders”,并通過為每種情況提供不同的預(yù)處理指令來讓著色器代碼多次被編譯來實現(xiàn)。
在Unity中,這可以通過#pragmamulti_compile或者#pragma shader_feature指令來在著色器代碼段中實現(xiàn)。這種做法對表面著色器也可行。
在運行時,相應(yīng)的著色器變體是從材質(zhì)的關(guān)鍵詞中取得的(Material.EnableKeyword和 DisableKeyword),或者全局著色器關(guān)鍵詞(Shader.EnableKeyword和 DisableKeyword)。
4.1 multi_compile用法簡析
若我們定義如下指令:
#pragma multi_compile FANCY_STUFF_OFFFANCY_STUFF_ON
也就表示定義了兩個變體:FANCY_STUFF_OFF和FANCY_STUFF_ON。在運行時,其中的一個將被激活,根據(jù)材質(zhì)或者全局著色器關(guān)鍵詞(#ifdef FANCY_STUFF_OFF之類的宏命令也可以)來確定激活哪個。若兩個關(guān)鍵詞都沒有啟用,那么將默認使用前一個選項,也就是關(guān)閉(OFF)的選項FANCY_STUFF_OFF。
ps:也可以存在超過兩個關(guān)鍵字的multi_compile編譯選項
舉個栗子:如下代碼將產(chǎn)生4種著色器的變體:
#pragma multi_compile SIMPLE_SHADINGBETTER_SHADING GOOD_SHADING BEST_SHADING
當#pragma multi_compile中存在所有名字都是下劃線的一個指定段時,就表示需在沒有預(yù)處理宏的情況下產(chǎn)生一個空的著色器變種。這種做法在著色器編寫中比較常見,因為這樣可以在不影響使用的情況下,避免使用兩個關(guān)鍵詞,這樣就節(jié)省了一個變量個數(shù)的占用(下面會提到,Unity中關(guān)鍵詞個數(shù)是有129個的數(shù)量限制的)。
再舉個栗子:下面的指令將產(chǎn)生兩個著色器變體;第一個沒有定義,第二個定義為FOO_ON:
#pragma multi_compile __ FOO_ON
這樣就省去了一個本來需要定義出來的 FOO_OFF(FOO_OFF沒有定義,自然也不能使用),節(jié)省了一個關(guān)鍵詞個數(shù)的占用。
若Shader中有如上定義,則可以使用#ifdef來進行判斷:
#ifdef FOO_ON
//代碼段1
#endif
根據(jù)上面已經(jīng)定義過的FOO_ON,此#ifdef判斷的結(jié)果為真,代碼段1部分的代碼就會被執(zhí)行到。反之,若#pragma multi_compile __FOO_ON一句代碼沒有交代出來,那么代碼段1部分的代碼就不會被執(zhí)行。
4.2 shader_feature和multi_compile之間的區(qū)別
//#pragma shader_feature 和#pragma multi_compile非常相似,
//唯一的區(qū)別在于采用了#pragmashader_feature語義的shader,
//在遇到不被使用的變體的時候,就不會將其編譯到游戲中。
//所以,shader_feature中使得所有的設(shè)置到材質(zhì)中的關(guān)鍵詞都是有效的,
//而multi_compile指令將從全局代碼里設(shè)置關(guān)鍵詞。
ps:shader_feature還有一個僅僅含有一個關(guān)鍵字的快捷表達方式
舉個栗子:
#pragma shader_feature FANCY_STUFF
此為#pragma shader_feature _ FANCY_STUFF的一個簡寫形式,其擴展出了兩個著色器變體,第一種變體自然為不定此FANCY_STUFF變量(那么若在稍后的Shader代碼中進行#ifdef FANCY_STUFF的判斷,則結(jié)果為假),第二種變體為定義此FANCY_STUFF變量(此情況下#ifdef FANCY_STUFF的判斷結(jié)果為真)。
4.3 多個multi_compile連用會造成指數(shù)級增長
可以提供多個multi_compile流水線,然后著色器的結(jié)果可以被編譯為幾個流水線的排列組合
舉個栗子:
#pragma multi_compile A B C
#pragma multi_compile D E
第一行中有3種選項,第二行中有兩種選項,那么進行排列組合,總共就會有六種選項(A+D, B+D, C+D, A+E, B+E, C+E)。
容易想到,一般每以個multi_compile流水線,都控制著著色器中某一單一的特性。請注意,著色器總量的增長速度是非??斓?。
比如,10條包含兩個特性的multi_compil指令,會得到2的10次方,也就是1024種不同的著色器變體。
4.4 關(guān)于Unity中的關(guān)鍵詞限制Keyword limit
Unity中將關(guān)鍵詞的數(shù)量限制在了128個之內(nèi)(著色變量算作關(guān)鍵字),且其中有一些已經(jīng)被Unity內(nèi)置使用了,因此真正可以自定義使用關(guān)鍵詞的數(shù)量是小于128個的。同時,關(guān)鍵詞是在單個Unity項目中全局使用并計數(shù)的,所以在同一項目中存在的但沒用到Shader也要考慮在內(nèi),不要合起來在數(shù)量上超出Unity的關(guān)鍵詞數(shù)量限制。
4.5 Unity內(nèi)置的快捷multi_compile指令
如下有Unity內(nèi)置的幾個著色器變體的快捷多編譯指令,他們大多是應(yīng)對Unity中不同的光線,陰影和光照貼圖類型。詳情見rendering pipeline 。
multi_compile_fwdbase - 此指令表示,編譯正向基礎(chǔ)渲染通道(用于正向渲染中,應(yīng)用環(huán)境光照、主方向光照和頂點/球面調(diào)和光照(Spherical Harmonic Lighting))所需的所有變體。這些變體用于處理不同的光照貼圖類型、主要方向光源的陰影選項的開關(guān)與否。
multi_compile_fwdadd - 此指令表示, 編譯正向附加渲染通道(用于正向渲染中;以每個光照一個通道的方式應(yīng)用附加的逐像素光照)所需的所有變體。這些變體用于處理光源的類型(方向光源、聚光燈或者點光源),且這些變種都包含紋理cookie。
multi_compile_fwdadd_fullshadows – 此指令和上面的正向渲染附加通道基本一致,但同時為上述通道的處理賦予了光照實時陰影的能力。
multi_compile_fog - 此指令表示,編譯出幾個不同的Shader變體來處理不同類型的霧效(關(guān)閉/線性/指數(shù)/二階指數(shù))(off/linear/exp/exp2).
4.6 使用指令跳過某些變體的編譯
可以使用#pragmaskip_variants語句跳過其中一些的編譯。
舉個栗子:
#pragma multi_compile_fwdadd
// 將跳過所有使用"POINT"或 "POINT_COOKIE"的變體
#pragma skip_variants POINT POINT_COOKIE