一.什么是Shader
Shader(著色器),是在GPU運行的程序,通過數學計算將各種屬性(顏色,光照,陰影,紋理等)渲染成物體表面可見的狀態(tài)。在shader里我們可以自定義顯卡渲染畫面的算法,使畫面達到我們想要的效果(放兩張圖)。
二.Shader語言
Shader是GPU運行的程序,目前實現Shader?的語言主要有三種:
基于OpenGL的OpenGL Shader Language,簡稱GLSL。
基于DirectX的High?Level Shader Language,簡稱HLSL。
還有NVIDIA公司的高級圖形編程語言C?for?Graphic,簡稱CG。
三.Shader分類
頂點 shader:3D圖形由一個個三角面組成,頂點shader就是處理這些三角面上的頂點,做頂點變換,傳遞數據到fragment shader。
片元 shader:渲染實現的核心,以片元為單位,計算光照,陰影,著色等一序列渲染算法。
在DX中頂點shader叫做Vertex Shader,片元shader叫做Pixel Shader;在Opengl中頂點shader也叫Vertex Shader,但片元shader叫做Fragment Shader。
四.Unity Shader
圖形API有Opengl,Vulkan,Metal,DX等,顯卡有Intel,NVIDIA,ATI,Amd等 ,Shader編程語言有GLSL,HLSL,Cg等。
Unity為了跨平臺,封裝了一下圖形,顯卡,Shader的差異性,開發(fā)了一種便于書寫的Shader,又稱為ShaderLab。
Unity Shader有三種不同寫法:
Surface Shader表面著色器;Vertext/Fragment Shader ?頂點/片段著色器;
Fixed Function Shader固定管線著色器(已廢棄)。
Surface Shader是Unity對Vertex/Fragment Shader的又一層封裝,簡化開發(fā)邏輯,用少量的代碼來完成不同的光照計算以及跨平臺邏輯。但Surface Shader的局限性也很明顯,Vertex/Fragment Shader能實現的功能,Surface Shader不一定能實現,Vertex/Fragment Shader針對細節(jié)上更細膩的處理,比較底層,也符合標準shader的寫法。并且Unity2018推出的Shader Graph,生成的代碼也是Vertex/Fragment Shader形式,所以我覺得Vertex/Fragment Shader是Unity Shader最主流的寫法,本節(jié)重點介紹Vertex/Fragment Shader的開發(fā)過程。
五.Unity Shader結構
Unity?Vertex/Fragment Shader的標準結構如下圖所示

結構可拆分為以下幾個部分:Shader ?“name”;Properties;SubShader;FallBack
Shader "name"
Shader是一個關鍵字,后面"name"的內容表示具體路徑和名稱

Properties 屬性
材質球Inspector面板上需要顯示的參數,紋理等都是在Properties中進行定義設置。如果不需要顯示參數可以將Properties{}全部去掉,Properties是個可選項,盡管Properties{}刪除了但是不影響C#對shader的參數傳遞。
SubShaders?
Unity Shader是由一個或者多個SubShader組成的,當渲染模型時,會在Unity Shader中尋找在當前顯卡上運行效果最好的SubShader來使用,例如做了一個效果很好的Shader,但是只能在高配顯卡上才能有較好的渲染表現,中低端機上渲染比較吃力,這時我們可以在Shader內做三個SubShader,分別對應于高,中,低不同的顯卡配置。
SubShaders架構細分包括Tags,渲染設置項,一個或多個Pass。
Tags用來控制引擎如何以及何時被渲染,Unity預定義Tag有以下幾種:
RenderType Tag;Quene Tag;DisableBatching Tag;ForceNoShadowCasting Tag;IgnoreProjector Tag;?CanUseSpriteAtlas Tag;PreviewType Tag
RenderType Tag
RenderType標簽將shader分類為若干個預定以組,Unity可以在運行時替換符合特定RenerType的所有shader。這個功能主要通過Camera.RenderWithShader或Camera.SetReplacementShader函數實現,這兩個函數都以1個shader和1個replacementTag作為參數,shader是給定的替換著色器,replacementTag是被替換著色器的標簽值。
Quene Tag
Quene 標簽用來確定對象的繪制順序。在shader中決定其渲染對象屬于哪個渲染隊列,提高渲染效率,保證渲染正確性。
在Unity中有5個預定以的渲染隊列,分別如下表示:

Unity在內部使用一些整數索引來表示每個渲染隊列,索引號越小越早被渲染,遵循3D渲染的基本規(guī)則:
1.先渲染不透明物體,把不透明物體按與攝像機距離的遠近進行排序,從前往后渲染,并開啟深度測試和深度寫入。
2.半透明物體按與攝像機距離的遠近進行排序,然后從后往前渲染,并開啟深度測試,關閉深度寫入。
DisableBatching Tag
禁用批處理,有三個標簽設置值: True,False(默認值),LODFading(當LOD衰減活動時禁用批處理)
ForceNoShadowCasting Tag
強制沒有陰影投射,有兩個標簽設置值:True,False(默認值)?
IgnoreProjector Tag
忽略投影機,有兩個標簽設置值:True,False(默認值)?
CanUseSpriteAtlas Tag
是否可以使用Sprite圖集,有兩個標簽設置值:True,False(默認值)?
PreviewType Tag
在inspector面板視圖下材質球的預覽樣式,有三個標簽值:
Spheres(默認值,材質顯示為球體),Plane(材質顯示為平面),Skybox(材質顯示為天空球)
渲染狀態(tài)(可寫Pass外,也可寫Pass內)
culling階段
Cull控制渲染的哪一面被剔除,有三個不同設置:Back(默認,剔除渲染模型的背面),Front(剔除渲染模型的正面),Off(背面和正面都渲染)
深度測試階段
ZWrite
控制是否寫入深度緩沖,有兩個不同設置:On(默認,寫入深度緩沖),Off(不寫入深度緩沖)
ZTest 控制深度測試邏輯,有以下幾個不同設置:
Greater , GEqual , Less , LEqual(默認值) , Equal , NotEqual , Always , Off。ZTest Off 等同于 ZTest Always
Blending階段
渲染圖形時,在片元通過深度測試和模版測試后,像素將寫入到屏幕。這些像素與已有像素的混合合方式由 Blend 命令控制,Blending主要用于生成透明對象。Blend有幾個以下不同設置:
Blend Off:默認值,關閉混合:
Blend SrcFactor DstFactor: 生成顏色乘以SrcFactor,緩沖區(qū)中的顏色乘以DstFactor,然后將這個值相加
Blend SrcFactor DstFactor, SrcFactorA DstFactorA:功能同上,SrcFactor DstFactor控制RGB混合系數,SrcFactorA DstFactorA控制Alpha(A)通道的混合系數。
BlendOp Op:執(zhí)行不同的混合操作,Op為不同的混合運算,
Op運算語法有以下5個:
Add(默認值,將源顏色和目標顏色相加),Sub(將源顏色減去目標顏色色),RevSub(將目標顏色減去源顏色),Min(源顏色和目標顏色取小值),Max(源顏色和目標顏色取大值)
BlendOp?OpColor, OpAlpha:功能同上。
Unity內部自定義了一些常見的混合類型:
Blend SrcAlpha OneMinusSrcAlpha (傳統(tǒng)透明度);
Blend One OneMinusSrcAlpha (預乘透明度);
Blend One One ( 加法);
Blend OneMinusDstColor One ( 軟加法);
Blend DstColor Zero (乘法);
Blend DstColor SrcColor ( 2x 乘法)
對應的系數說明如下:

Stencil測試階段:
如果開啟模版測試,GPU會首先讀取模版緩沖區(qū)里該片元位置的模版值,然后將該值和讀取到的參考值進行比較,若沒有通過測試,該片元就會被舍棄掉,但不管片元有沒有通過測試,我們都可以根據模版測試的結果來修改模版緩沖區(qū),常用的模版緩沖配置如下:

其Comparison Function如下:

Stencil Operation如下

Pass
pass是Unity Shader中最重要的組成部分,包含渲染的核心——著色器。
pass的結構包含以下:
? ?Name "name": 定義Pass名稱(可選項),便于以后在其他SubShader當中復用該Pass。
? ?Tags:同SubShader Tag;但有些Unity內置的Tag不能放在SubShader里,只能放在Pass內才能生效,比如:
? ? LightMode,PassFlags,RequireOptions。
? ? LightMode:支持以下幾種Tag設置
? ? ?Always(總是渲染,不處理光照);
? ? ?ForwardBase(在前向渲染管線中使用,處理環(huán)境光,直射光,頂點光照)
? ? ?ForwardAdd(在前向渲染管線中使用,處理像素光源)
? ? ?Deferred(在延遲渲染管線中使用,G—Buffer渲染)
?渲染狀態(tài):同SubShader 渲染狀態(tài)。
頂點/片元著色器
用“CGPROGRAM和ENDCG或者HLSLPROGRAM和ENDHLSL”指令包圍的代碼即頂點著色器和片元著色器,代碼結構如下

#pragma vertex聲明頂點著色器函數
#pragma fragment聲明片元著色器函數
a2v結構體表示應用程序傳遞到頂點著色器的模型數據,數據類型如下:

v2f結構體表示把頂點著色器的數據傳遞到片元著色器

vert函數主要作用是頂點變換,把模型坐標從本地空間轉到裁剪空間,并將頂點數據傳遞到片元著色器。
frag函數主要作用是做片元映射和著色,自定義光照模型,根據uv讀取紋理貼圖顏色,并輸出片元數據到測試階段。