本系列文章由@淺墨_毛星云 出品,轉(zhuǎn)載請注明出處。 文章鏈接: http://blog.csdn.net/poem_qianmo/article/details/42060963 作者:毛星云(淺墨) 微博:http://weibo.com/u/1723155442 郵箱: happylifemxy@163.com
本文算是固定功能Shader的最后一篇,下一次更新應(yīng)該就會開始講解表面Shader,而講解完表面Shader,后續(xù)文章最終會講解到頂點著色器和片段著色器(也就是可編程Shader)。 文章第一部分復(fù)習(xí)和進(jìn)一步了解了Unity中Shader的三種形態(tài),然后講解了固定功能Shader中混合操作的方方面面,然后以6個Shader的書寫作為實戰(zhàn)內(nèi)容,最后創(chuàng)建了一個溫馨美好的圣誕夜場景進(jìn)行了Shader的測試。 依舊是國際慣例,先上本文配套程序的截圖吧。 圣誕節(jié)就快到了,而下次更新就已經(jīng)過了圣誕節(jié),于是這次更新淺墨就提前把這個場景放出來吧,預(yù)祝大家圣誕節(jié)快樂~ 雪花飄落: 
可愛的圣誕雪人: 
精心裝扮的圣誕樹:  月是故鄉(xiāng)明: 
霧氣彌漫: 
OK,圖先就上這么多。文章末尾有更多的運行截圖,并提供了源工程的下載??蛇\行的exe下載在這里: 【可運行的exe游戲場景請點擊這里下載試玩】 好的,我們正式開始。
一、再談Unity中Shader的三種形態(tài)
因為Unity中基礎(chǔ)的固定功能Shader的知識點基本上講完,下期開始就要準(zhǔn)備講表面著色器(Surface Shader)了,所以在文章開頭,讓我們復(fù)習(xí)和更深入了解一下Unity中Shader的三種形態(tài)。
在Unity中,Shader便可以分成如下三種基本類型: 1.固定功能著色器(FixedFunction Shader) 2.表面著色器(SurfaceShader) 3.頂點著色器&片段著色器(Vertex Shader & Fragment Shader) 顧名思義,其中的固定功能著色器便是我們所說的固定功能渲染管線(fixed-functionrenderingpipelines)的具體表現(xiàn),而表面著色器、頂點著色器以及片段著色器便屬于可編程渲染管線。下面分別對其進(jìn)行簡單的介紹。
1.1 Unity中的Shader形態(tài)之一:固定功能Shader
這里的固定功能著色器可以說是Unity為Shader的書寫自帶的一層殼,Unity已經(jīng)在內(nèi)部為我們做了大量的工作,我們只要稍微記住一些關(guān)鍵字、一些規(guī)范就可以實現(xiàn)出很多不錯的效果。固定功能著色器是我們初學(xué)Unity Shader的最近幾篇文章中的主要學(xué)習(xí)對象。而后面的表面著色器、頂點著色器以及片段著色器就是在固定功能著色器的基礎(chǔ)上嵌套了CG語言的代碼而成的更加復(fù)雜的著色器。我們來看看他們的一些基本概念。 固定管線是為了兼容老式顯卡。都為頂點光照,就是我們前四篇文章加上這篇文章中講到的內(nèi)容。 其特征是里面的核心是下面Material材質(zhì)屬性塊、沒有CGPROGRAM和ENDCG塊,以及各種頂點著色和片段著色的宏命令。 一個光照材質(zhì)完備版的固定功能Shader示例如下: Shader '淺墨Shader編程/Volume5/固定功能的Shader示例' { //-------------------------------【屬性】----------------------------------------- Properties { _Color ('主顏色', Color) = (1,1,1,0) _SpecColor ('高光顏色', Color) = (1,1,1,1) _Emission ('自發(fā)光顏色', Color) = (0,0,0,0) _Shininess ('光澤度', Range (0.01, 1)) = 0.7 _MainTex ('基本紋理', 2D) = 'white' {} } //--------------------------------【子著色器】-------------------------------- SubShader { //----------------通道--------------- Pass { //-----------材質(zhì)------------ Material { //可調(diào)節(jié)的漫反射光和環(huán)境光反射顏色 Diffuse [_Color] Ambient [_Color] //光澤度 Shininess [_Shininess] //高光顏色 Specular [_SpecColor] //自發(fā)光顏色 Emission [_Emission] } //開啟光照 Lighting On //開啟獨立鏡面反射 SeparateSpecular On //設(shè)置紋理并進(jìn)行紋理混合 SetTexture [_MainTex] { Combine texture * primary DOUBLE, texture * primary } } } }
我們將此Shader編譯后賦給材質(zhì),得到如下效果: 
實際場景中的測試效果: 
1.2 Unity中的Shader形態(tài)之二:表面著色器SurfaceShader
這部分算是Unity微創(chuàng)新自創(chuàng)的一套著色器標(biāo)準(zhǔn)。 表面著色器(Surface Shader)這個概念更多的只是在Unity中聽說,可以說是Unity自己發(fā)揚光大的一項使Shader的書寫門檻降低和更易用的技術(shù)。我們會在接下來的學(xué)習(xí)中逐漸意識到Unity是如何為我們把Shader的復(fù)雜性包裝起來,使其書寫的過程更便捷和易用 的。一些特性如下: · SurfaceShader可以認(rèn)為是一個光照Shader的語法塊、一個光照VS/FS的生成器。減少了開發(fā)者寫重復(fù)代碼的需要。 · 特征是在SubShader里出現(xiàn)CGPROGRAM和ENDCG塊。(而不是出現(xiàn)在Pass里。因為SurfaceShader自己會編譯成多個Pass。) · 編譯指令是: #pragma surface surfaceFunction lightModel[optionalparams] o surfaceFunction:surfaceShader函數(shù),形如void surf (Input IN, inoutSurfaceOutput o) o lightModel:使用的光照模式。包括Lambert(漫反射)和BlinnPhong(鏡面反射)。 也可以自己定義光照函數(shù)。比如編譯指令為#pragma surface surf MyCalc 在Shader里定義half4 LightingMyCalc (SurfaceOutputs, 參數(shù)略)函數(shù)進(jìn)行處理(函數(shù)名在簽名加上了“Lighting”)。 · 我們自己定義輸入數(shù)據(jù)結(jié)構(gòu)(比如上面的Input)、編寫自己的Surface函數(shù)處理輸入、最終輸出修改過后的SurfaceOutput。而SurfaceOutput的定義為:
struct SurfaceOutput{ half3 Albedo; // 紋理顏色值(r, g, b) half3 Normal; // 法向量(x, y, z) half3 Emission; // 自發(fā)光顏色值(r, g, b) half Specular; // 鏡面反射度 half Gloss; // 光澤度 half Alpha; // Alpha不透明度};
上面是一些特性總結(jié),讓我們看一個具體Shader示例: Shader '淺墨Shader編程/Volume5/表面Shader示例 ' { //-------------------------------【屬性】----------------------------------------- Properties { _MainTex ('【紋理】Texture', 2D) = 'white' {} _BumpMap ('【凹凸紋理】Bumpmap', 2D) = 'bump' {} _RimColor ('【邊緣顏色】Rim Color', Color) = (0.17,0.36,0.81,0.0) _RimPower ('【邊緣顏色強度】Rim Power', Range(0.6,9.0)) = 1.0 } //----------------------------【開始一個子著色器】--------------------------- SubShader { //渲染類型為Opaque,不透明 Tags { 'RenderType' = 'Opaque' } //-------------------開始CG著色器編程語言段----------------- CGPROGRAM //使用蘭伯特光照模式 #pragma surface surf Lambert //輸入結(jié)構(gòu) struct Input { float2 uv_MainTex;//紋理貼圖 float2 uv_BumpMap;//法線貼圖 float3 viewDir;//觀察方向 }; //變量聲明 sampler2D _MainTex;//主紋理 sampler2D _BumpMap;//凹凸紋理 float4 _RimColor;//邊緣顏色 float _RimPower;//邊緣顏色強度 //表面著色函數(shù)的編寫 void surf (Input IN, inout SurfaceOutput o) { //表面反射顏色為紋理顏色 o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; //表面法線為凹凸紋理的顏色 o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); //邊緣顏色 half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal)); //邊緣顏色強度 o.Emission = _RimColor.rgb * pow (rim, _RimPower); } //-------------------結(jié)束CG著色器編程語言段------------------ ENDCG } //“備胎”為普通漫反射 Fallback 'Diffuse' } 我們將此Shader編譯后賦給材質(zhì),得到如下效果: 
調(diào)各種顏色玩一玩:  

而實際場景中的測試效果(對應(yīng)于一開始的金色): 
1.3 Unity中的Shader形態(tài)之三:可編程Shader
可編程Shader其實就是頂點著色器和片段著色器,這一部分和DirectX系的HLSL和CG著色器語言聯(lián)系緊密。其實就是Unity給HLSL和CG報了一個ShaderLab的殼。 研究過Direct3D和OpenGL著色器編程的童鞋們一定對頂點著色器和片段著色器不陌生。我們來簡單介紹一下他們的用途。 頂點著色器:產(chǎn)生紋理坐標(biāo),顏色,點大小,霧坐標(biāo),然后把它們傳遞給裁剪階段。 片段著色器:進(jìn)行紋理查找,決定什么時候執(zhí)行紋理查找,是否進(jìn)行紋理查找,及把什么作為紋理坐標(biāo)。 可編程Shader的特點為:
編譯指令 | 示例/含義 | #pragma vertex name #pragma fragment name | 替換name,來指定Vertex Shader函數(shù)、Fragment Shader函數(shù)。 | #pragma target name | 替換name(為2.0、3.0等)。設(shè)置編譯目標(biāo)shader model的版本。 | #pragma only_renderers name name ... #pragma exclude_renderers name name... | #pragma only_renderers gles gles3, #pragma exclude_renderers d3d9 d3d11 opengl, 只為指定渲染平臺(render platform)編譯 |
- 關(guān)于引用庫。通過形如#include 'UnityCG.cginc'引入指定的庫。常用的就是UnityCG.cginc了。其他庫詳見官網(wǎng)Built-in shader include files。
- ShaderLab內(nèi)置值。Unity給Shader程序提供了便捷的、常用的值,比如下面例子中的UNITY_MATRIX_MVP就代表了這個時刻的MVP矩陣。詳見官網(wǎng)ShaderLab built-in values。
- Shader輸入輸出參數(shù)語義(Semantics)。在管線流程中每個階段之間(比如Vertex Shader階段和FragmentShader階段之間)的輸入輸出參數(shù),通過語義字符串,來指定參數(shù)的含義。常用的語義包括:COLOR、SV_Position、TEXCOORD[n]。完整的參數(shù)語義可見HLSL Semantic(由于是HLSL的連接,所以可能不完全在Unity里可以使用)。
- 特別地,因為Vertex Shader的的輸入往往是管線的最開始,Unity為此內(nèi)置了常用的數(shù)據(jù)結(jié)構(gòu):
數(shù)據(jù)結(jié)構(gòu) | 含義 | appdata_base | 頂點著色器輸入位置、法線以及一個紋理坐標(biāo)。 | appdata_tan | 頂點著色器輸入位置、法線、切線以及一個紋理坐標(biāo)。 | appdata_full | 頂點著色器輸入位置、法線、切線、頂點顏色以及兩個紋理坐標(biāo)。 | appdata_img | 頂點著色器輸入位置以及一個紋理坐標(biāo)。 |
讓我們用一個可編程著色器Shader示例結(jié)束此部分的講解: Shader '淺墨Shader編程/Volume5/可編程Shader示例' { //-------------------------------【屬性】-------------------------------------- Properties { _Color ('Color', Color) = (1.0,1.0,1.0,1.0) _SpecColor ('Specular Color', Color) = (1.0,1.0,1.0,1.0) _Shininess ('Shininess', Float) = 10 } //--------------------------------【子著色器】-------------------------------- SubShader { //-----------子著色器標(biāo)簽---------- Tags { 'LightMode' = 'ForwardBase' } //----------------通道--------------- Pass { //-------------------開始CG著色器編程語言段----------------- CGPROGRAM #pragma vertex vert #pragma fragment frag //---------------聲明變量-------------- uniform float4 _Color; uniform float4 _SpecColor; uniform float _Shininess; //--------------定義變量-------------- uniform float4 _LightColor0; //--------------頂點輸入結(jié)構(gòu)體------------- struct vertexInput { float4 vertex : POSITION; float3 normal : NORMAL; }; //--------------頂點輸出結(jié)構(gòu)體------------- struct vertexOutput { float4 pos : SV_POSITION; float4 col : COLOR; }; //--------------頂點函數(shù)-------------- vertexOutput vert(vertexInput v) { vertexOutput o; //一些方向 float3 normalDirection = normalize( mul( float4(v.normal, 0.0), _World2Object ).xyz ); float3 viewDirection = normalize( float3( float4( _WorldSpaceCameraPos.xyz, 1.0) - mul(_Object2World, v.vertex).xyz ) ); float3 lightDirection; float atten = 1.0; //光照 lightDirection = normalize(_WorldSpaceLightPos0.xyz); float3 diffuseReflection = atten * _LightColor0.xyz * max( 0.0, dot( normalDirection, lightDirection ) ); float3 specularReflection = atten * _LightColor0.xyz * _SpecColor.rgb * max( 0.0, dot( normalDirection, lightDirection ) ) * pow( max( 0.0, dot( reflect( -lightDirection, normalDirection ), viewDirection ) ), _Shininess ); float3 lightFinal = diffuseReflection + specularReflection + UNITY_LIGHTMODEL_AMBIENT; //計算結(jié)果 o.col = float4(lightFinal * _Color.rgb, 1.0);//顏色 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);//位置 return o; } //--------------片段函數(shù)--------------- float4 frag(vertexOutput i) : COLOR { return i.col; } //-------------------結(jié)束CG著色器編程語言段------------------ ENDCG } } //備胎 Fallback 'Diffuse' }
我們將此Shader編譯后賦給材質(zhì),得到如下效果: 
可以發(fā)現(xiàn)就算這么簡單的可編程Shader其細(xì)節(jié)效果也是非常出色,比固定功能Shader和表面Shader都看起來高端。我們依然是調(diào)各種顏色玩一玩:


實際場景中的測試效果: 
OK,下面我們來看本次文章的主角——blending操作。
二、混合操作(Blending)
我們直奔主題吧?;旌喜僮髯畛R姷挠猛颈闶怯脕碇谱魍该魑矬w、或者是進(jìn)行紋理的混合。它是Shader渲染的最后一步: 
如上圖所示,正被渲染的像素經(jīng)過頂點光照、頂點著色器、剔除和深度測試,霧效、Alpha測試等一系列操作之后,最后一步便是混合操作。這個時候計算結(jié)果即將被輸出到幀緩沖中。而混合操作,就是管理如何將這些像素輸出到幀緩存中的這樣一個過程——是直接替換原來的,是一加一的混合,還是有Alpha參與的不等比地混合等等。 混合操作有兩個對象:源和目標(biāo),因此也有兩個對應(yīng)的因子,即源因子和目標(biāo)因子(對應(yīng)于下面講解的Blend SrcFactor DstFactor操作)。 而如果我們把RGB顏色通道和Alpha通道分開來操作的話,混合就有了4個操作對象(對應(yīng)于下面講解的Blend SrcFactor DstFactor,SrcFactorA DstFactorA操作)。
2.1 混合操作相關(guān)的句法
Blend OffTurn off blending 關(guān)閉混合
Blend SrcFactorDstFactor基本的配置并啟動混操作。對產(chǎn)生的顏色乘以SrcFactor.對 已存在于屏幕的顏色乘以DstFactor,并且兩者將被疊加在一起。 Blend SrcFactorDstFactor, SrcFactorA DstFactorA同上,但是使用不同的要素來混合alpha通道,也就是有了4個操作對象 BlendOp Add /Min | Max | Sub | RevSub此操作不是Blend操作一樣添加混合顏色在一起,而是對它們做不同的操作。 而如下便是常用混合操作符(blend operations)的含義列舉:
Add | 將源像素和目標(biāo)像素相加. | Sub | 用源像素減去目標(biāo)像素 | RevSub | 用目標(biāo)像素減去源像素 | Min | 取目標(biāo)像素和源像素顏色的較小者作為結(jié)果 | Max | 取目標(biāo)像素和源像素顏色的較大者作為結(jié)果 |
2.2 混合因子(Blend factors)列舉
以下所有的屬性都可作為SrcFactor或DstFactor。其中,Source指的是被計算過的顏色,Destination是已經(jīng)在屏幕上的顏色。
| One | 值為1,使用此因子來讓幀緩沖區(qū)源顏色或是目標(biāo)顏色完全的通過。 | | Zero | 值為0,使用此因子來刪除幀緩沖區(qū)源顏色或目標(biāo)顏色的值。 | | SrcColor | 使用此因子為將當(dāng)前值乘以幀緩沖區(qū)源顏色的值 | | SrcAlpha | 使用此因子為將當(dāng)前值乘以幀緩沖區(qū)源顏色Alpha的值。 | | DstColor | 使用此因子為將當(dāng)前值乘以幀緩沖區(qū)源顏色的值。 | | DstAlpha | 使用此因子為將當(dāng)前值乘以幀緩沖區(qū)源顏色Alpha分量的值。 | | OneMinusSrcColor | 使用此因子為將當(dāng)前值乘以(1 -幀緩沖區(qū)源顏色值) | | OneMinusSrcAlpha | 使用此因子為將當(dāng)前值乘以(1 -幀緩沖區(qū)源顏色Alpha分量的值) | | OneMinusDstColor | 使用此因子為將當(dāng)前值乘以(1 –目標(biāo)顏色值) | | OneMinusDstAlpha | 使用此因子為將當(dāng)前值乘以(1 –目標(biāo)Alpha分量的值) |
2.3 常見的混合操作句法示例
上面都是一些句法和列表的列舉,往往會令人一頭霧水,下面這是一些示例,用其中的任何一句加在Pass中就可以實現(xiàn)對應(yīng)的混合操作了: Blend SrcAlpha OneMinusSrcAlpha // Alpha混合Blend One One // 相加Blend One OneMinusDstColor // 比較柔和的相加(SoftAdditive)Blend DstColor Zero // 乘法Blend DstColor SrcColor // 2倍乘法
三、QianMo's Toolkit升級到v1.3 這次QianMo's Toolkit又迎來了新的特性——飛翔。 將腳本賦給Controller,并調(diào)整相應(yīng)的速度,(并可以先禁掉之前的鼠標(biāo)視角控制相關(guān)腳本)然后點運行,并可以在天空中自由地飛翔了。 
其中用W、A、S、D控制前后左右,R、F控制上升下降。 其代碼如下: //-----------------------------------------------【腳本說明】-------------------------------------------------------// 腳本功能: 控制Contorller在場景中飛翔// 使用語言: C#// 開發(fā)所用IDE版本:Unity4.5 06f 、Visual Studio 2010 // 2014年12月 Created by 淺墨 // 更多內(nèi)容或交流,請訪問淺墨的博客:http://blog.csdn.net/poem_qianmo//---------------------------------------------------------------------------------------------------------------------//-----------------------------------------------【使用方法】-------------------------------------------------------// 第一步:在Unity中拖拽此腳本到場景的Controller之上,或在Inspector中[Add Component]->[淺墨's Toolkit]->[SetMaxFPS]// 第二步:在面板中設(shè)置相關(guān)鼠標(biāo)速度//---------------------------------------------------------------------------------------------------------------------using UnityEngine;using System.Collections;//添加組件菜單[AddComponentMenu('淺墨's Toolkit/FlyController')]public class FlyController : MonoBehaviour{ //參數(shù)定義 public float lookSpeed = 5.0f; public float moveSpeed = 1.0f; public float rotationX = 0.0f; public float rotationY = 0.0f; void Update() { //獲取鼠標(biāo)偏移量 rotationX += Input.GetAxis('Mouse X') * lookSpeed; rotationY += Input.GetAxis('Mouse Y') * lookSpeed; rotationY = Mathf.Clamp(rotationY, -90, 90); //鼠標(biāo)控制視角 transform.localRotation = Quaternion.AngleAxis(rotationX, Vector3.up); transform.localRotation *= Quaternion.AngleAxis(rotationY, Vector3.left); transform.position += transform.forward * moveSpeed * Input.GetAxis('Vertical'); transform.position += transform.right * moveSpeed * Input.GetAxis('Horizontal'); //I鍵,向上平移 if (Input.GetKey(KeyCode.R)) transform.position += transform.up * moveSpeed; //K鍵,向下平移 if (Input.GetKey(KeyCode.F)) transform.position -= transform.up * moveSpeed; }}
就這樣,我們的QianMo’s Toolkit中的工具越來越多: 
四、Shader書寫實戰(zhàn)
1. 紋理載入Shader
因為后面幾個shader的需要,先根據(jù)我們之前所學(xué),幾行代碼就可以實現(xiàn)一個紋理載入Shader:
Shader '淺墨Shader編程/Volume5/18.基本紋理載入' { //-------------------------------【屬性】-------------------------------------- Properties { _MainTex ('基本紋理', 2D) = 'black' { } } //--------------------------------【子著色器】-------------------------------- SubShader { //-----------子著色器標(biāo)簽---------- Tags { 'Queue' = 'Geometry' } //子著色器的標(biāo)簽設(shè)為幾何體 //----------------通道--------------- Pass { //設(shè)置紋理 SetTexture [_MainTex] { combine texture } } }}
我們將此Shader編譯后賦給材質(zhì),得到如下效果: 
實際場景中的運行效果如下: 
2.基本blend使用
在上面簡單的texture載入的Shader的基礎(chǔ)上加上一行關(guān)于blend的代碼,就成了我們今天的第二個Shader: Shader '淺墨Shader編程/Volume5/19.基本blend使用' { //-------------------------------【屬性】----------------------------------------- Properties { _MainTex ('將要混合的基本紋理', 2D) = 'black' { } } //--------------------------------【子著色器】---------------------------------- SubShader { //-----------子著色器標(biāo)簽---------- Tags { 'Queue' = 'Geometry' } //子著色器的標(biāo)簽設(shè)為幾何體 //----------------通道--------------- Pass { //進(jìn)行混合 Blend DstColor Zero // 乘法 //設(shè)置紋理 SetTexture [_MainTex] { combine texture } } }}
我們將此Shader編譯后賦給材質(zhì),得到如下效果: 
我們采用的是乘法混合操作( Blend DstColor Zero),可以發(fā)現(xiàn)顏色相對于第一個Shader有了稍微的變暗。 雖然肉眼很難看出區(qū)別,但實際上的確是有變化的: 
3.基本blend使用+顏色可調(diào)
再給我們的Shader加上一點可自定義的顏色,并讓紋理的alpha通道插值混合頂點顏色 。代碼如下: Shader '淺墨Shader編程/Volume5/20.基本blend使用+顏色可調(diào)' { //-------------------------------【屬性】----------------------------------------- Properties { _MainTex ('將混合的紋理', 2D) = 'black' {} _Color ('主顏色', Color) = (1,1,1,0) } //--------------------------------【子著色器】-------------------------------- SubShader { //-----------子著色器標(biāo)簽---------- Tags { 'Queue' = 'Transparent' } //子著色器的標(biāo)簽設(shè)為透明 //----------------通道--------------- Pass { Blend One OneMinusDstColor // 柔性相加 SetTexture [_MainTex] { // 使顏色屬性進(jìn)入混合器 constantColor [_Color] // 使用紋理的alpha通道插值混合頂點顏色 combine constant lerp(texture) previous } } }}
我們將此Shader編譯后賦給材質(zhì),得到如下效果:  調(diào)成各種顏色:  
實際場景中的測試效果: 
4.基本blend使用+頂點光照
在之前Shader的基礎(chǔ)上,給我們的Shader再加上材質(zhì)屬性和頂點光照:
Shader '淺墨Shader編程/Volume5/21.基本blend使用+頂點光照' { //-------------------------------【屬性】----------------------------------------- Properties { _MainTex ('Texture to blend', 2D) = 'black' {} _Color ('主顏色', Color) = (1,1,1,0) } //--------------------------------【子著色器】-------------------------------- SubShader { //-----------子著色器標(biāo)簽---------- Tags { 'Queue' = 'Transparent' } //----------------通道--------------- Pass { //【1】設(shè)置材質(zhì) Material { Diffuse [_Color] Ambient [_Color] } //【2】開啟光照 Lighting On Blend One OneMinusDstColor // Soft Additive SetTexture [_MainTex] { // 使顏色屬性進(jìn)入混合器 constantColor [_Color] // 使用紋理的alpha通道插值混合頂點顏色 combine constant lerp(texture) previous } } }}
于是結(jié)果如下: 
調(diào)各種顏色看看: 

在場景中的測試效果圖為: 
5.實現(xiàn)玻璃效果第二版
之前我們用剔除實現(xiàn)過一版玻璃效果,這次我們來用blend實現(xiàn)完全不一樣的玻璃效果,需要載入一個cubemap(其實根據(jù)cubemap的選擇不同,會實現(xiàn)不同的效果,比如本次的最終效果就有點像金屬材質(zhì))。
Shader的代碼如下: Shader '淺墨Shader編程/Volume5/22.玻璃效果v2' { //-------------------------------【屬性】-------------------------------------- Properties { _Color ('Main Color', Color) = (1,1,1,1) _MainTex ('Base (RGB) Transparency (A)', 2D) = 'white' {} _Reflections ('Base (RGB) Gloss (A)', Cube) = 'skybox' { TexGen CubeReflect } } //--------------------------------【子著色器】-------------------------------- SubShader { //-----------子著色器標(biāo)簽---------- Tags { 'Queue' = 'Transparent' } //----------------通道--------------- Pass { //進(jìn)行紋理混合 Blend One One //設(shè)置材質(zhì) Material { Diffuse [_Color] } //開光照 Lighting On //和紋理相乘 SetTexture [_Reflections] { combine texture Matrix [_Reflection] } } }}
我們將此Shader編譯后賦給材質(zhì),得到如下效果,可以發(fā)現(xiàn)最終效果是一個帶室內(nèi)場景反射的金屬材質(zhì): 
實際場景中的測試效果: 
6. 實現(xiàn)玻璃效果第三版
我們給上面的第二版加上第二個pass,最終代碼如下:
Shader '淺墨Shader編程/Volume5/23.玻璃效果v3' { //-------------------------------【屬性】----------------------------------------- Properties { _Color ('Main Color', Color) = (1,1,1,1) _MainTex ('Base (RGB) Transparency (A)', 2D) = 'white' {} _Reflections ('Base (RGB) Gloss (A)', Cube) = 'skybox' { TexGen CubeReflect } } //--------------------------------【子著色器】---------------------------------- SubShader { //-----------子著色器標(biāo)簽---------- Tags { 'Queue' = 'Transparent' } //----------------通道1-------------- Pass { Blend SrcAlpha OneMinusSrcAlpha Material { Diffuse [_Color] } Lighting On SetTexture [_MainTex] { combine texture * primary double, texture * primary } } //----------------通道2-------------- Pass { //進(jìn)行紋理混合 Blend One One //設(shè)置材質(zhì) Material { Diffuse [_Color] } //開光照 Lighting On //和紋理相乘 SetTexture [_Reflections] { combine texture Matrix [_Reflection] } } }}
載入同樣的紋理和cubemap,得到的效果更加醇厚優(yōu)異: 
實際場景中的測試效果: 
OK,這次的Shader實戰(zhàn)就是上面的這些了。
五、圣誕夜場景創(chuàng)建
就像文章開頭中說的,圣誕節(jié)就快到了,而下次更新就已經(jīng)過了圣誕節(jié),于是這次更新淺墨就提前把這個精心準(zhǔn)備的圣誕夜場景放出來吧,預(yù)祝大家圣誕節(jié)快樂~ 以大師級美工鬼斧神工的場景作品為基礎(chǔ),淺墨調(diào)整了場景布局,加入了音樂,并加入了更多高級特效,于是便得到了如此這次溫馨美好的場景。 而冬天穿衣不便,加上路滑,淺墨故意把controller調(diào)出了走路打滑的感覺。 運行游戲,圣誕音樂漸漸響起,雪白的雪花靜靜飄落,我們來到美麗的圣誕夜: 
雪花飛揚: 
月上樹梢: 
淺墨精心裝扮的圣誕樹: 
月光給屋頂披上一層清輝:  火爐、圣誕禮物: 
月是故鄉(xiāng)明: 
可愛的圣誕雪人: 
淺墨是不會告訴你們是怎么進(jìn)到門緊關(guān)的房子里面來的,自己摸索吧~
大雪紛飛的路: 
最后放一張這次Shader的全家福: 
OK,美圖就放這么多。游戲場景可運行的exe可以在文章開頭中提供的鏈接下載。而以下是源工程的下載鏈接。
本篇文章的示例程序源工程請點擊此處下載: 【淺墨Unity3D Shader編程】之五 圣誕夜篇配套Unity工程下載
好的,本篇文章到這里就全部結(jié)束了。 淺墨在這里提前祝大家圣誕節(jié)快樂~ 下周一,新的游戲編程之旅,我們不見不散~ 
|