Unity5新版Shader模板源碼解析&運(yùn)動模糊(徑向模糊)屏幕特效的實現(xiàn)

轉(zhuǎn)自: http://blog.csdn.net/poem_qianmo/article/details/49405909

原始場景效果圖:


開啟運(yùn)動模糊特效后的場景效果圖:

圖先就上這兩張启绰。文章末尾有更多的運(yùn)行截圖坎匿,并提供了源工程的下載丹鸿。先放出可運(yùn)行的exe下載,如下:

【可運(yùn)行的exe游戲場景請點擊這里下載試玩】

好的壹无,正文開始。

一、Unity5中新的Shader體系簡析

Unity5和之前的書寫模式有了一定的改變其骄。Unity5時代的Shader Reference官方文檔也進(jìn)一步地變得豐滿恐疲。
主要需要了解到的是腊满,在原來的Unity中,若想要新建一個Shader源文件培己,不考慮compute shader的話碳蛋,僅有一種Shader模板供選擇。而自從Unity5.1起(好像是Unity5.1)
想在Unity5.1之后的版本中新建Shader省咨,【右鍵在Project窗口中單擊】->【Create】肃弟,會出現(xiàn)如下的四個選項:


而由于暫時不考慮compute shader。所以零蓉,新版Unity中有三種基本的Shader模板分別為:

Standard Surface Shader標(biāo)準(zhǔn)表面著色器
Unlit Shader 無燈光著色器
Image Effect Shader 圖像特效著色器

二笤受、Unity5中新的Shader模板源碼解析

下面,對Unity5中三種基本Shader模板進(jìn)行逐行注釋與思路解析壁公。
可以點擊這里跳轉(zhuǎn)到Github感论,查看詳細(xì)注釋好的三種Shader模板的源碼。

2.1 標(biāo)準(zhǔn)表面著色器(Standard Surface Shader)模板源碼解析

在Unity中紊册,我們?nèi)粢獙崿F(xiàn)新的表面著色器時比肄,可以根據(jù)這個模板,進(jìn)行一步添加子著色器和新的參數(shù)與特性囊陡。
這個Shader模板的脈絡(luò)很清晰芳绩,先是定義一些屬性,然后在SubShader中設(shè)置渲染模式撞反,層次細(xì)節(jié)LOD的值妥色,然后開啟一個CG編程語言模塊,寫一些編譯指令#pragma遏片,聲明一下變量讓屬性值在CG塊中可見嘹害,定義輸入結(jié)構(gòu),然后填充一下表面著色函數(shù)即可吮便。注意:專門強(qiáng)調(diào)一句笔呀,SurfaceShader不能使用Pass,一使用就報錯髓需,我們直接在SubShader中實現(xiàn)和填充代碼就可以了许师。
Standard Surface Shader模板詳細(xì)注釋的Shader代碼如下:

[cpp] view plain copy

Shader "淺墨Shader編程/Volume8/Surface Shader模板"
{
//------------------------------------【屬性值】------------------------------------
Properties
{
//主顏色
_Color("Color", Color) = (1,1,1,1)
//主紋理
_MainTex("Albedo (RGB)", 2D) = "white" {}
//光澤度
_Glossiness("Smoothness", Range(0,1)) = 0.5
//金屬度
_Metallic("Metallic", Range(0,1)) = 0.0
}

   //------------------------------------【唯一的子著色器】------------------------------------  
   SubShader  
   {  
          //【注意:Surface Shader不能使用Pass,直接在SubShader中實現(xiàn)即可】  

          //渲染類型設(shè)置:不透明  
          Tags{"RenderType" = "Opaque" }  

          //細(xì)節(jié)層次設(shè)為:200  
          LOD200  

          //===========開啟CG著色器語言編寫模塊===========  
          CGPROGRAM  

          //編譯指令:告知編譯器表明著色函數(shù)的名稱為surf  
          //Standard表示光照模型為Unity標(biāo)準(zhǔn)版光照模型  
          //fullforwardshadows表示在正向渲染路徑中支持所有陰影類型  
          #pragma surface surf Standard fullforwardshadows  

          //編譯指令: 指定著色器編譯目標(biāo)為Shader Model 3.0  
          #pragma target 3.0  

          //變量的聲明  
          sampler2D _MainTex;  

          //表面輸入結(jié)構(gòu)體  
          struct Input  
          {  
                 float2 uv_MainTex;//紋理坐標(biāo)  
          };  

          //變量的聲明  
          half _Glossiness;  
          half _Metallic;  
          fixed4 _Color;  

          //--------------------------------【表面著色函數(shù)】-----------------------------  
          //輸入:表面輸入結(jié)構(gòu)體  
          //輸出:Unity內(nèi)置的SurfaceOutputStandard結(jié)構(gòu)體  
          //SurfaceOutputStandard原型如下:  
          /* 
                 struct SurfaceOutputStandard 
                 { 
                        fixed3 Albedo;                  // 漫反射顏色 
                        fixed3 Normal;                  // 切線空間法線 
                        half3 Emission;                 //自發(fā)光 
                        half Metallic;                           // 金屬度;取0為非金屬, 取1為金屬 
                        half Smoothness;             // 光澤度微渠;取0為非常粗糙, 取1為非常光滑 
                        half Occlusion;                 // 遮擋(默認(rèn)值為1) 
                        fixed Alpha;                      // 透明度 
                 }; 
          */  
          //---------------------------------------------------------------------------------  
          void surf(Input IN, inout SurfaceOutputStandard o)  
          {  
                 //【1】漫反射顏色為主紋理對應(yīng)的紋理坐標(biāo)搭幻,并乘以主顏色  
                 fixed4c = tex2D(_MainTex, IN.uv_MainTex) * _Color;  
                 //【2】將準(zhǔn)備好的顏色的rgb分量作為漫反射顏色  
                 o.Albedo= c.rgb;  
                 //【3】金屬度取自屬性值  
                 o.Metallic= _Metallic;  
                 //【4】光澤度也取自屬性值  
                 o.Smoothness= _Glossiness;  
                 //【5】將準(zhǔn)備好的顏色的alpha分量作為Alpha分量值  
                 o.Alpha= c.a;  
          }  

          //===========結(jié)束CG著色器語言編寫模塊===========  
          ENDCG  
   }  
   //備胎為漫反射  
   FallBack"Diffuse"  

}

接著來看Unity5的第二種Shader模板,無燈光著色器(Unlit Shader)模板逞盆。

2.2 無燈光著色器(Unlit Shader)模板源碼解析

Unlit Shader檀蹋,簡單來說,就是直接采用漫反射紋理纳击,不考慮場景中的任何燈光效果续扔。使用無燈光著色器的話,也就不能使用任何鏡面或者法線效果了焕数。Unlit系的Shader基本原理和其他Shader無異纱昧,但是計算量更小,更快速堡赔,更高效识脆。
而在Unity內(nèi)置的各種著色器中,有如下的四種是Unlit系的:


好的善已,已經(jīng)稍微解釋了下什么是Unlit Shader灼捂。下面一起看一下Unity為我們提供的無燈光著色器模板的代碼:

[cpp] view plain copy

Shader "淺墨Shader編程/Volume8/無燈光著色器(Unlit Shader)模板"
{
//------------------------------------【屬性值】------------------------------------
Properties
{
//主紋理
_MainTex("Texture", 2D) = "white" {}
}

   //------------------------------------【唯一的子著色器】------------------------------------  
   SubShader  
   {  
          //渲染類型設(shè)置:不透明  
          Tags{ "RenderType"="Opaque" }  

          //細(xì)節(jié)層次設(shè)為:100  
          LOD 100  

          //--------------------------------唯一的通道-------------------------------  
          Pass  
          {  
                 //===========開啟CG著色器語言編寫模塊===========  
                 CGPROGRAM  

                 //編譯指令:告知編譯器頂點和片段著色函數(shù)的名稱  
                 #pragma vertex vert  
                 #pragma fragment frag  

                 //著色器變體快捷編譯指令:霧效。編譯出幾個不同的Shader變體來處理不同類型的霧效(關(guān)閉/線性/指數(shù)/二階指數(shù))  
                 #pragma multi_compile_fog  

                 //包含頭文件  
                 #include"UnityCG.cginc"  

                 //頂點著色器輸入結(jié)構(gòu)  
                 struct appdata  
                 {  
                        float4 vertex : POSITION;//頂點位置  
                        float2 uv : TEXCOORD0;//紋理坐標(biāo)  
                 };  

                 //頂點著色器輸出結(jié)構(gòu)  
                 struct v2f  
                 {  
                        float2 uv : TEXCOORD0;//紋理坐標(biāo)  
                        UNITY_FOG_COORDS(1)//霧數(shù)據(jù)  
                        float4 vertex : SV_POSITION;//像素位置  
                 };  

                 //變量聲明  
                 sampler2D _MainTex;  
                 float4 _MainTex_ST;  
                  
                 //--------------------------------【頂點著色函數(shù)】-----------------------------  
                 //輸入:頂點輸入結(jié)構(gòu)體  
                 //輸出:頂點輸出結(jié)構(gòu)體  
                 //---------------------------------------------------------------------------------  
                 v2f vert (appdata v)  
                 {  
                        //【1】實例化一個輸入結(jié)構(gòu)體  
                        v2f o;  
                        //【2】填充此輸出結(jié)構(gòu)  
                        //輸出的頂點位置(像素位置)為模型視圖投影矩陣乘以頂點位置,也就是將三維空間中的坐標(biāo)投影到了二維窗口  
                        o.vertex= mul(UNITY_MATRIX_MVP, v.vertex);  
                        //【3】用UnityCG.cginc頭文件中內(nèi)置定義的宏,根據(jù)uv坐標(biāo)來計算真正的紋理上對應(yīng)的位置(按比例進(jìn)行二維變換)                   
                        o.uv= TRANSFORM_TEX(v.uv, _MainTex);  
                        //【4】用UnityCG.cginc頭文件中內(nèi)置定義的宏處理霧效换团,從頂點著色器中輸出霧效數(shù)據(jù)  
                        UNITY_TRANSFER_FOG(o,o.vertex);  

                        //【5】返回此輸出結(jié)構(gòu)對象  
                        return o;  
                 }  
                  
                 //--------------------------------【片段著色函數(shù)】-----------------------------  
                 //輸入:頂點輸出結(jié)構(gòu)體  
                 //輸出:float4型的像素顏色值  
                 //---------------------------------------------------------------------------------  
                 fixed4 frag (v2f i) : SV_Target  
                 {  
                        //【1】采樣主紋理在對應(yīng)坐標(biāo)下的顏色值  
                        fixed4 col = tex2D(_MainTex, i.uv);  

                        //【2】用UnityCG.cginc頭文件中內(nèi)置定義的宏啟用霧效  
                        UNITY_APPLY_FOG(i.fogCoord,col);             

                        //【3】返回最終的顏色值  
                        return col;  
                 }  

                 //===========結(jié)束CG著色器語言編寫模塊===========  
                 ENDCG  
          }  
   }  

}

不難分析得到悉稠,無燈光著色器是一種頂點&片段著色器,這邊模板給出的是單子著色器艘包,單通道的寫法的猛。

并且,無燈光著色器中使用了一些UnityCG.cginc頭文件中內(nèi)置的宏想虎,比如說TRANSFORM_TEX卦尊、UNITY_TRANSFER_FOG、UNITY_APPLY_FOG舌厨。接下來分別把這三個宏簡單解釋一下岂却。

2.2.1 TRANSFORM_TEX宏

TRANSFORM_TEX宏的定義為:

[cpp] view plain copy

define TRANSFORM_TEX(tex,name) (tex.xy *name##_ST.xy + name##_ST.zw)

其位于UnityCG.cginc(Unity5.2.1版本)的第266行。其可以根據(jù)uv坐標(biāo)來計算真正的紋理上對應(yīng)的位置(按比例進(jìn)行二維變換)裙椭,組合上上文中定義的float4 _MainTex_ST躏哩,便可以計算真正的紋理上對應(yīng)的位置。

2.2.2 UNITY_TRANSFER_FOG宏

UNITY_TRANSFER_FOG宏的作用是從頂點著色輸出霧數(shù)據(jù)揉燃。在UnityCG.cginc(Unity5.2.1版本)的第772行起震庭,具體定義如下:

[cpp] view plain copy

if (SHADER_TARGET < 30) ||defined(SHADER_API_MOBILE)

          //手機(jī)端或者Shader Mode 2.0: 計算每個頂點的霧效因子  
          #define UNITY_TRANSFER_FOG(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.fogCoord =unityFogFactor  
   #else  
          //Shader Mode 3.0和PC和游戲機(jī): 計算每像素的霧距離,和每像素的霧效因子  
          #define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord = (outpos).z  
   #endif  

2.2.3 UNITY_APPLY_FOG宏

UNITY_APPLY_FOG宏的定義稍微有些長你雌,從UnityCG.cginc(Unity 5.2.1版本)的第787行起:

[cpp] view plain copy

if defined(FOG_LINEAR) || defined(FOG_EXP)|| defined(FOG_EXP2)

   #if(SHADER_TARGET < 30) || defined(SHADER_API_MOBILE)  
          //mobile or SM2.0: fog factor was already calculated per-vertex, so just lerp thecolor  
          #defineUNITY_APPLY_FOG_COLOR(coord,col,fogCol) UNITY_FOG_LERP_COLOR(col,fogCol,coord)  
   #else  
          //SM3.0 and PC/console: calculate fog factor and lerp fog color  
          #define UNITY_APPLY_FOG_COLOR(coord,col,fogCol) UNITY_CALC_FOG_FACTOR(coord);UNITY_FOG_LERP_COLOR(col,fogCol,unityFogFactor)  
   #endif  

else

   #define UNITY_APPLY_FOG_COLOR(coord,col,fogCol)  

endif

ifdef UNITY_PASS_FORWARDADD

   #define UNITY_APPLY_FOG(coord,col) UNITY_APPLY_FOG_COLOR(coord,col,fixed4(0,0,0,0))  

else

   #define UNITY_APPLY_FOG(coord,col) UNITY_APPLY_FOG_COLOR(coord,col,unity_FogColor)  

endif

可以發(fā)現(xiàn),UNITY_APPLY_FOG宏的作用是從頂點著色器中輸出霧效數(shù)據(jù),將第二個參數(shù)中的顏色值作為霧效的顏色值婿崭,且在正向附加渲染通道(forward-additive pass)中拨拓,自動設(shè)置純黑色(fixed4(0,0,0,0))的霧效。其在定義中借助了UNITY_APPLY_FOG_COLOR宏氓栈,而我們也可以使用UNITY_APPLY_FOG_COLOR來指定特定顏色的霧效渣磷。

2.3 圖像特效著色器(Image Effect Shader) 模板源碼解析

這里的圖像特效一般指的就是屏幕圖像特效,在Camera加上各種濾鏡授瘦,比如說屏幕濺血醋界,像素化,色調(diào)的調(diào)整提完,畫面模糊等效果形纺。其也是一個頂點&片段著色器,且一般主要的操作集中在片段著色函數(shù)中徒欣。Unity為我們提供的模板逐样,經(jīng)過詳細(xì)注釋后的源碼如下:

[cpp] view plain copy

Shader "淺墨Shader編程/Volume8/圖像特效Shader模板"
{
//------------------------------------【屬性值】------------------------------------
Properties
{
//主紋理
_MainTex("Texture", 2D) = "white" {}
}

   //------------------------------------【唯一的子著色器】------------------------------------  
   SubShader  
   {  
          //關(guān)閉剔除操作  
          Cull Off  
          //關(guān)閉深度寫入模式  
          ZWrite Off  
          //設(shè)置深度測試模式:渲染所有像素.等同于關(guān)閉透明度測試(AlphaTestOff)  
          ZTest Always  

          //--------------------------------唯一的通道-------------------------------  
          Pass  
          {  
                 //===========開啟CG著色器語言編寫模塊===========  
                 CGPROGRAM  

                 //編譯指令:告知編譯器頂點和片段著色函數(shù)的名稱  
                 #pragma vertex vert  
                 #pragma fragment frag  
                  
                 //包含頭文件  
                 #include"UnityCG.cginc"  

                 //頂點著色器輸入結(jié)構(gòu)  
                 struct appdata  
                 {  
                        float4 vertex : POSITION;//頂點位置  
                        float2 uv : TEXCOORD0;//一級紋理坐標(biāo)  
                 };  

                 //頂點著色器輸出結(jié)構(gòu)(v2f,vertex to fragment)  
                 struct v2f  
                 {  
                        float2 uv : TEXCOORD0;//一級紋理坐標(biāo)  
                        float4 vertex : SV_POSITION;//像素位置  
                 };  

                 //--------------------------------【頂點著色函數(shù)】-----------------------------  
                 //輸入:頂點輸入結(jié)構(gòu)體  
                 //輸出:頂點輸出結(jié)構(gòu)體  
                 //---------------------------------------------------------------------------------  
                 //頂點著色函數(shù)  
                 v2f vert (appdata v)  
                 {  
                        //【1】實例化一個輸入結(jié)構(gòu)體  
                        v2f o;  

                        //【2】填充此輸出結(jié)構(gòu)  
                        //輸出的頂點位置(像素位置)為模型視圖投影矩陣乘以頂點位置打肝,也就是將三維空間中的坐標(biāo)投影到了二維窗口  
                        o.vertex= mul(UNITY_MATRIX_MVP, v.vertex);  
                        //輸入的UV紋理坐標(biāo)為頂點輸出的坐標(biāo)  
                        o.uv= v.uv;  

                        //【3】返回此輸出結(jié)構(gòu)對象  
                        return o;  
                 }  

                 //變量的聲明  
                 sampler2D _MainTex;  

                 //--------------------------------【片段著色函數(shù)】-----------------------------  
                 //輸入:頂點輸出結(jié)構(gòu)體  
                 //輸出:float4型的像素顏色值  
                 //---------------------------------------------------------------------------------  
                 fixed4 frag (v2f i) : SV_Target  
                 {  
                        //【1】采樣主紋理在對應(yīng)坐標(biāo)下的顏色值  
                        fixed4 col = tex2D(_MainTex, i.uv);  
                        //【2】將顏色值反向  
                        col= 1 - col;  

                        //【3】返回最終的顏色值  
                        return col;  
                 }  

                 //===========結(jié)束CG著色器語言編寫模塊===========  
                 ENDCG  
          }  
   }  

}

2.4 Shader模板中文注釋格式調(diào)整版替換

其實可以將Unity5中自帶的上述三個著色器模板脂新,替換成上文中貼出來的、經(jīng)過詳細(xì)注釋和格式調(diào)整的Shader模板粗梭,這樣在每次新建Shader時争便,就已經(jīng)得到了具有很高可讀性的Shader模板了,非常便捷断医。
一定要吐槽的是躲叼,Unity5.2.1自帶的三個Shader模板的縮進(jìn)和空格完全是混用的,導(dǎo)致在通過他們新建出來的Shader里面寫代碼的時候氢伟,格式非车胛茫混亂,十分影響新版Unity中Shader的編碼體驗躬窜。很明顯浇垦,準(zhǔn)備此Shader模板的Unity開發(fā)人員的編碼習(xí)慣有點欠缺,得在這里點名批評荣挨,輕噴一下男韧。

淺墨在一發(fā)現(xiàn)他們格式有問題的時候就馬上替換掉了,所以現(xiàn)在在Unity中寫Shader代碼的體驗是非常棒的默垄。這邊也教大家如何替換掉自帶的3個模板此虑。
Unity中Shader模板的位置是…Unity\Editor\Data\Resources\ScriptTemplates,比如說Unity安裝在D:\ProgramFiles\路徑下口锭,整體路徑就是:
D:\ProgramFiles\Unity\Editor\Data\Resources\ScriptTemplates朦前。

在此路徑下的3個txt介杆,即為對應(yīng)的三個Shader模板文件:

83-Shader__Standard SurfaceShader-NewSurfaceShader.shader.txt
84-Shader__UnlitShader-NewUnlitShader.shader.txt
85-Shader__Image EffectShader-NewImageEffectShader.shader

這邊已經(jīng)將調(diào)整好格式,詳細(xì)注釋的三種模板準(zhǔn)備好了韭寸,下載之后春哨,找到上面提到的…Unity\Editor\Data\Resources\ScriptTemplates目錄。替換掉對應(yīng)的3個txt文件即可恩伺。需要注意的是赴背,如果你想自己DIY Shader模板,需要將txt保存為UTF-8編碼格式晶渠,否則可能會出現(xiàn)亂碼凰荚。

替換的模板下載地址在這里:

【Unity5-Shader模板中文注釋格式調(diào)整版替換文件】下載

另外還有一個小細(xì)節(jié)可以提一下。如果你安裝了兩個或者兩個以上的Unity5.1之后版本的Unity褒脯,如果你替換你當(dāng)前使用的Unity路徑下的模板文件后便瑟,新建的模板文件沒有改變的話,你試著將所有的Unity5.1之后版本的路徑下的這三個模板文件都進(jìn)行替換憨颠,應(yīng)該就可以實現(xiàn)想要的替換效果胳徽。淺墨的機(jī)器上就是同時存在Unity5.2.1和Unity5.2.0,然后使用Unity5.2.1爽彤,替換掉Unity5.2.1路徑下的三個模板文件后养盗,并沒有發(fā)生變換。之后我按圖索驥适篙,替換了Unity 5.2.0版路徑下的三個模板文化往核,才使得替換的模板文件生效。這估計是Unity多版本共存時嚷节,自身的一個小bug聂儒。

三、運(yùn)動模糊屏幕特效的實現(xiàn)

關(guān)于運(yùn)動模糊特效硫痰,如果把握要要點的話衩婚,實現(xiàn)起來其實比較簡單,就是一個腳本文件配合一個Shader效斑,便可以實現(xiàn)較為出色的運(yùn)動模糊特效非春。而其中的腳本文件用于控制Shader中的外部參數(shù)。
也就是說一個屏幕特效通常分為兩部分來實現(xiàn):

Shader實現(xiàn)部分
腳本實現(xiàn)部分

下面我們對運(yùn)動模糊屏幕特效的實現(xiàn)分別進(jìn)行簡單的描述缓屠。
可以點擊這里跳轉(zhuǎn)到Github奇昙,查看詳細(xì)注釋好的運(yùn)動模糊屏幕特效的實現(xiàn)源碼。

3.1 Shader實現(xiàn)部分

先看一下Shader代碼的寫法敌完,因為基本上已經(jīng)逐行注釋储耐,就不花時間和筆墨仔細(xì)講解了,詳細(xì)注釋的代碼如下:

[cpp] view plain copy

Shader "淺墨Shader編程/Volume8/運(yùn)動模糊特效標(biāo)準(zhǔn)版"
{
//------------------------------------【屬性值】------------------------------------
Properties
{
_MainTex("主紋理 (RGB)", 2D) = "white" {}
_IterationNumber("迭代次數(shù)", Int)=16
}

//------------------------------------【唯一的子著色器】------------------------------------  
SubShader  
{     
    //--------------------------------唯一的通道-------------------------------  
    Pass  
    {  
        //設(shè)置深度測試模式:渲染所有像素.等同于關(guān)閉透明度測試(AlphaTest Off)  
        ZTest Always  

        //===========開啟CG著色器語言編寫模塊===========  
        CGPROGRAM  

        //編譯指令: 指定著色器編譯目標(biāo)為Shader Model 3.0  
        #pragma target 3.0  

        //編譯指令:告知編譯器頂點和片段著色函數(shù)的名稱  
        #pragma vertex vert  
        #pragma fragment frag  

        //包含輔助CG頭文件  
        #include "UnityCG.cginc"  

        //外部變量的聲明  
        uniform sampler2D _MainTex;  
        uniform float _Value;  
        uniform float _Value2;  
        uniform float _Value3;  
        uniform int _IterationNumber;  

        //頂點輸入結(jié)構(gòu)  
        struct vertexInput  
        {  
            float4 vertex : POSITION;//頂點位置  
            float4 color : COLOR;//顏色值  
            float2 texcoord : TEXCOORD0;//一級紋理坐標(biāo)  
        };  

        //頂點輸出結(jié)構(gòu)  
        struct vertexOutput  
        {  
            half2 texcoord : TEXCOORD0;//一級紋理坐標(biāo)  
            float4 vertex : SV_POSITION;//像素位置  
            fixed4 color : COLOR;//顏色值  
        };  


        //--------------------------------【頂點著色函數(shù)】-----------------------------  
        // 輸入:頂點輸入結(jié)構(gòu)體  
        // 輸出:頂點輸出結(jié)構(gòu)體  
        //---------------------------------------------------------------------------------  
        vertexOutput vert(vertexInput Input)  
        {  
            //【1】聲明一個輸出結(jié)構(gòu)對象  
            vertexOutput Output;  

            //【2】填充此輸出結(jié)構(gòu)  
            //輸出的頂點位置為模型視圖投影矩陣乘以頂點位置滨溉,也就是將三維空間中的坐標(biāo)投影到了二維窗口  
            Output.vertex = mul(UNITY_MATRIX_MVP, Input.vertex);  
            //輸出的紋理坐標(biāo)也就是輸入的紋理坐標(biāo)  
            Output.texcoord = Input.texcoord;  
            //輸出的顏色值也就是輸入的顏色值  
            Output.color = Input.color;  

            //【3】返回此輸出結(jié)構(gòu)對象  
            return Output;  
        }  

        //--------------------------------【片段著色函數(shù)】-----------------------------  
        // 輸入:頂點輸出結(jié)構(gòu)體  
        // 輸出:float4型的顏色值  
        //---------------------------------------------------------------------------------  
        float4 frag(vertexOutput i) : COLOR  
        {  
            //【1】設(shè)置中心坐標(biāo)  
            float2 center = float2(_Value2, _Value3);  
            //【2】獲取紋理坐標(biāo)的x什湘,y坐標(biāo)值  
            float2 uv = i.texcoord.xy;  
            //【3】紋理坐標(biāo)按照中心位置進(jìn)行一個偏移  
            uv -= center;  
            //【4】初始化一個顏色值  
            float4 color = float4(0.0, 0.0, 0.0, 0.0);  
            //【5】將Value乘以一個系數(shù)  
            _Value *= 0.085;  
            //【6】設(shè)置坐標(biāo)縮放比例的值  
            float scale = 1;  

            //【7】進(jìn)行紋理顏色的迭代  
            for (int j = 1; j < _IterationNumber; ++j)  
            {  
                //將主紋理在不同坐標(biāo)采樣下的顏色值進(jìn)行迭代累加  
                color += tex2D(_MainTex, uv * scale + center);  
                //坐標(biāo)縮放比例依據(jù)循環(huán)參數(shù)的改變而變化  
                scale = 1 + (float(j * _Value));  
            }  

            //【8】將最終的顏色值除以迭代次數(shù)长赞,取平均值  
            color /= (float)_IterationNumber;  

            //【9】返回最終的顏色值  
            return  color;  
        }  

        //===========結(jié)束CG著色器語言編寫模塊===========  
        ENDCG  
    }  
}  

}

可以發(fā)現(xiàn),這是一個單子著色器禽炬、單通道的頂點&片段著色器涧卵,頂點著色函數(shù)vert中基本上都是寫的比較中規(guī)中矩的代碼,精髓之處在于片段著色器frag中腹尖,用一個for
循環(huán),將像素顏色按照一條直線(uv * scale + center)進(jìn)行了迭代采樣累加伐脖,最終將采樣的顏色的總和除以采樣次數(shù)热幔,得到了想要實現(xiàn)的運(yùn)動模糊效果。

3.2 腳本實現(xiàn)部分

腳本文件的實現(xiàn)方面讼庇,如下的即個點是要提出來專門講一下的绎巨,即Shader文件的獲取方法和OnRenderImage函數(shù)、Blit函數(shù)蠕啄。

3.2.1 Shader文件的獲取

Shader文件的獲取可以使用Shader.Find函數(shù)實現(xiàn)场勤。需要注意,Shader.Find函數(shù)參數(shù)應(yīng)該和Shader代碼中的名稱一致歼跟,也就是下面的代碼框架中xxx的值和媳,而不是Shader的文件名:

[cpp] view plain copy

Shader "xxxx"
{

}

舉個例子,腳本代碼如果是這樣:

[cpp] view plain copy

CurShader = Shader.Find ("淺墨Shader編程/Volume8/運(yùn)動模糊特效標(biāo)準(zhǔn)版");

那么獲取到的Shader哈街,文件名是任意的留瞳,但Shader代碼框架肯定是這樣:

[cpp] view plain copy

Shader "淺墨Shader編程/Volume8/運(yùn)動模糊特效標(biāo)準(zhǔn)版"
{
……
}

3.2.2 OnRenderImage函數(shù)與Blit函數(shù)

OnRenderImage()函數(shù)是MonoBehaviour中提供的一個可供我們重寫的函數(shù),它在unity完成所有圖片的渲染后被調(diào)用骚秦。所以我們想實現(xiàn)屏幕特效她倘,主要依靠它來實現(xiàn)。而OnRenderImage函數(shù)的函數(shù)原型是:

[cpp] view plain copy

void OnRenderImage(RenderTexture sourceTexture,RenderTexture destTexture);

另外作箍,我們需要配合一個Graphics.Blit函數(shù)硬梁,實現(xiàn)從源紋理到目標(biāo)渲染紋理的拷貝過程,其原型如下三種:

[cpp] view plain copy

public static void Blit(Texture source,RenderTexture dest);
public static void Blit(Texture source,RenderTexture dest, Material mat, int pass = -1);
public static void Blit(Texture source,Material mat, int pass = -1);

其中胞得。
第一個參數(shù)荧止,Texture類型的source,原始紋理懒震。
第二個參數(shù)罩息,RenderTexture類型的dest,目標(biāo)渲染紋理个扰,若為null瓷炮,表示直接將原始紋理拷貝到屏幕之上。
第三個參數(shù)递宅,Material類型的mat娘香,使用的材質(zhì)(其實也就是Shader)苍狰,根據(jù)不同材質(zhì)的準(zhǔn)備,就是在這里實現(xiàn)后期的效果的烘绽。
第四個參數(shù)淋昭,int類型的pass,有默認(rèn)值 -1安接,表示使用所有的pass翔忽。用于指定使用哪一個pass。

說個題外話盏檐,其實在很久之前的Win32 API游戲編程中歇式,同樣原理和相似用途的Blit函數(shù)用得太多了。

好的胡野,最后看一下實現(xiàn)屏幕特效的核心代碼材失,如下:

[cpp] view plain copy

void OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture)
{
//著色器實例不為空,就進(jìn)行參數(shù)設(shè)置
if (CurShader != null)
{
//設(shè)置Shader中的外部變量
material.SetFloat("_IterationNumber", IterationNumber);
material.SetFloat("_Value", Intensity);
material.SetFloat("_Value2", OffsetX);
material.SetFloat("_Value3", OffsetY);
material.SetFloat("_Value4", blurWidth);
material.SetVector("_ScreenResolution", new Vector4(sourceTexture.width, sourceTexture.height, 0.0f, 0.0f));

    //拷貝源紋理到目標(biāo)渲染紋理硫豆,加上我們的材質(zhì)效果  
    Graphics.Blit(sourceTexture, destTexture, material);  
}  
//著色器實例為空龙巨,直接拷貝屏幕上的效果。此情況下是沒有實現(xiàn)屏幕特效的  
else  
{  
    //直接拷貝源紋理到目標(biāo)渲染紋理  
    Graphics.Blit(sourceTexture, destTexture);  
}  

最后看一下詳細(xì)注釋后的腳本完整實現(xiàn)代碼:

[csharp] view plain copy

using UnityEngine;
using System.Collections;

[ExecuteInEditMode]

public class MotionBlurEffects : MonoBehaviour
{

//-------------------變量聲明部分-------------------  
#region Variables  
public Shader CurShader;//著色器實例  
private Vector4 ScreenResolution;//屏幕分辨率  
private Material CurMaterial;//當(dāng)前的材質(zhì)  

[Range(5, 50)]  
public float IterationNumber = 15;  
[Range(-0.5f, 0.5f)]  
public float Intensity = 0.125f;  
[Range(-2f, 2f)]  
public float OffsetX = 0.5f;  
[Range(-2f, 2f)]  
public float OffsetY = 0.5f;  


public static float ChangeValue;  
public static float ChangeValue2;  
public static float ChangeValue3;  
public static float ChangeValue4;  
#endregion  


//-------------------------材質(zhì)的get&set----------------------------  
#region MaterialGetAndSet  
Material material  
{  
    get  
    {  
        if (CurMaterial == null)  
        {  
            CurMaterial = new Material(CurShader);  
            CurMaterial.hideFlags = HideFlags.HideAndDontSave;  
        }  
        return CurMaterial;  
    }  
}  
#endregion  

//-----------------------------------------【Start()函數(shù)】---------------------------------------------    
// 說明:此函數(shù)僅在Update函數(shù)第一次被調(diào)用前被調(diào)用  
//--------------------------------------------------------------------------------------------------------  
void Start()  
{  
    //依此賦值  
    ChangeValue = Intensity;  
    ChangeValue2 = OffsetX;  
    ChangeValue3 = OffsetY;  
    ChangeValue4 = IterationNumber;  

    //找到當(dāng)前的Shader文件  
    CurShader = Shader.Find("淺墨Shader編程/Volume8/運(yùn)動模糊特效標(biāo)準(zhǔn)版");  

    //判斷是否支持屏幕特效  
    if (!SystemInfo.supportsImageEffects)  
    {  
        enabled = false;  
        return;  
    }  
}  

//-------------------------------------【OnRenderImage()函數(shù)】------------------------------------    
// 說明:此函數(shù)在當(dāng)完成所有渲染圖片后被調(diào)用熊响,用來渲染圖片后期效果  
//--------------------------------------------------------------------------------------------------------  
void OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture)  
{  
    //著色器實例不為空旨别,就進(jìn)行參數(shù)設(shè)置  
    if (CurShader != null)  
    {  
        //設(shè)置Shader中的外部變量  
        material.SetFloat("_IterationNumber", IterationNumber);  
        material.SetFloat("_Value", Intensity);  
        material.SetFloat("_Value2", OffsetX);  
        material.SetFloat("_Value3", OffsetY);  
        material.SetVector("_ScreenResolution", new Vector4(sourceTexture.width, sourceTexture.height, 0.0f, 0.0f));  

        //拷貝源紋理到目標(biāo)渲染紋理,加上我們的材質(zhì)效果  
        Graphics.Blit(sourceTexture, destTexture, material);  
    }  
    //著色器實例為空耘眨,直接拷貝屏幕上的效果昼榛。此情況下是沒有實現(xiàn)屏幕特效的  
    else  
    {  
        //直接拷貝源紋理到目標(biāo)渲染紋理  
        Graphics.Blit(sourceTexture, destTexture);  
    }  
      
}  


//-----------------------------------------【OnValidate()函數(shù)】--------------------------------------    
// 說明:此函數(shù)在編輯器中該腳本的某個值發(fā)生了改變后被調(diào)用  
//--------------------------------------------------------------------------------------------------------  
void OnValidate()  
{  
    //將編輯器中的值賦值回來,確保在編輯器中值的改變立刻讓結(jié)果生效  
    ChangeValue4 = IterationNumber;  
    ChangeValue = Intensity;  
    ChangeValue2 = OffsetX;  
    ChangeValue3 = OffsetY;  

}  

//-----------------------------------------【Update()函數(shù)】------------------------------------------    
// 說明:此函數(shù)在每一幀中都會被調(diào)用  
//--------------------------------------------------------------------------------------------------------   
void Update()  
{  
    if (Application.isPlaying)  
    {  
        //賦值  
        IterationNumber = ChangeValue4;  
        Intensity = ChangeValue;  
        OffsetX = ChangeValue2;  
        OffsetY = ChangeValue3;  

    }  

    //找到對應(yīng)的Shader文件  

if UNITY_EDITOR

    if (Application.isPlaying != true)  
    {  
        CurShader = Shader.Find("淺墨Shader編程/Volume8/運(yùn)動模糊特效標(biāo)準(zhǔn)版");  

    }  

endif

}  


//-----------------------------------------【OnDisable()函數(shù)】---------------------------------------    
// 說明:當(dāng)對象變?yōu)椴豢捎没蚍羌せ顮顟B(tài)時此函數(shù)便被調(diào)用    
//--------------------------------------------------------------------------------------------------------  
void OnDisable()  
{  
    if (CurMaterial)  
    {  
        DestroyImmediate(CurMaterial);  
    }  
}  

}

3.3 關(guān)于如何使用此特效

使用方面的話比較簡單剔难,把腳本文件拖到主攝像機(jī)上面胆屿,效果就出來了。
腳本文件中有如下這些參數(shù)可以調(diào)整偶宫,得到不同的模糊效果:


Iteration Number- 迭代次數(shù)
Intensity - 模糊強(qiáng)度
Offset X - X方向上的偏移
Offset Y - Y方向上的偏移

四非迹、最終的效果展示

這邊貼幾張場景的效果圖和使用了屏幕特效后的效果圖。需要注意的是纯趋,本次的場景效果憎兽,除了類似CS/CF的FPS游戲的控制系統(tǒng)以外,還可以使用鍵盤上的按鍵【F】吵冒,開啟或者關(guān)閉運(yùn)動模糊特效纯命。正如下圖所展示的:


下面放幾張測試截圖。

首先痹栖,Unity5中亿汞,導(dǎo)出的exe使用了新的片頭Logo,質(zhì)感不錯揪阿,好評:


城鎮(zhèn)中的原始效果:


運(yùn)動模糊后的效果:

海港原始效果:


海港運(yùn)動模糊后的效果:

城鎮(zhèn)草叢前的效果:


城鎮(zhèn)草叢前運(yùn)動模糊后的效果:


沙灘原始效果:


沙灘運(yùn)動模糊后的效果:


海面原始效果:


海面運(yùn)動模糊后的效果:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疗我,一起剝皮案震驚了整個濱河市咆畏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吴裤,老刑警劉巖旧找,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異麦牺,居然都是意外死亡钮蛛,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進(jìn)店門剖膳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來愿卒,“玉大人,你說我怎么就攤上這事潮秘。” “怎么了易结?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵枕荞,是天一觀的道長。 經(jīng)常有香客問我搞动,道長躏精,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任鹦肿,我火速辦了婚禮矗烛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘箩溃。我一直安慰自己瞭吃,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布涣旨。 她就那樣靜靜地躺著歪架,像睡著了一般。 火紅的嫁衣襯著肌膚如雪霹陡。 梳的紋絲不亂的頭發(fā)上和蚪,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天,我揣著相機(jī)與錄音烹棉,去河邊找鬼攒霹。 笑死,一個胖子當(dāng)著我的面吹牛浆洗,可吹牛的內(nèi)容都是我干的催束。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼辅髓,長吁一口氣:“原來是場噩夢啊……” “哼泣崩!你這毒婦竟也來了少梁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤矫付,失蹤者是張志新(化名)和其女友劉穎凯沪,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體买优,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡妨马,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了杀赢。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烘跺。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖脂崔,靈堂內(nèi)的尸體忽然破棺而出滤淳,到底是詐尸還是另有隱情,我是刑警寧澤砌左,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布脖咐,位于F島的核電站,受9級特大地震影響汇歹,放射性物質(zhì)發(fā)生泄漏屁擅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一产弹、第九天 我趴在偏房一處隱蔽的房頂上張望派歌。 院中可真熱鬧,春花似錦痰哨、人聲如沸胶果。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽稽物。三九已至,卻和暖如春折欠,著一層夾襖步出監(jiān)牢的瞬間贝或,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工锐秦, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留咪奖,地道東北人。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓酱床,卻偏偏與公主長得像羊赵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內(nèi)容