溶解效果在游戲中是很常見的,比如在一些神話或者魔法世界中,一些NPC角色在劇情需要時候會身體會漸漸的消失掉.甚至有一些更炫的,比如用火焰噴射器把目標燃盡。這些都可以用到溶解效果。這篇文章主要是講解一下比較基礎的溶解效果如何實現,實現的方法並不唯一,本篇只是其中一種思路。
原理
既然想讓角色的身體一塊塊漸漸消失,不妨就讓角色身體上相應的部位不進行渲染(或者改成透明,我們這里選擇前者)。那根據什么來判斷身體的哪一部分需要被溶解呢,這時候就需要一張額外的貼圖或者利用角色紋理貼圖的Alpha通道(本篇選擇前者)。這個貼圖和紋理貼圖一樣,對應着玩家身體的每一個位置,這樣我們就可以根據貼圖上某個指定通道的顏色值來控制角色各個身體部位是否溶解了。
另外我們還會涉及到的一個命令叫discard,是由CG提供的,若出現在fragmentShader中表示立即放棄當前處理的片元。也就是說當我們判定當前片遠需要溶解的時候我們就使用discard命令。
上面這張圖就是用來控制溶解程度的紋理,我們這里比較簡單,只使用了R通道,如果你想做的很復雜也可以利用上其它的通道。在本篇中我們將根據時間的推進不斷溶解掉貼圖上R通道顏色值較小的區域。美術可以利用這張貼圖控制角色的任意溶解順序和效果。由於我的素材都是隨便找的,自己又不會畫畫,所以這個圖和角色紋理可能並不搭配,請不要在意。
再看下面這個圖,我先來提前說一下本篇Shader在Properties中提供的各個可調節的參數
1.Base(RGB)是角色紋理貼圖,不用解釋了.
2.NoiseTex(R)是很重重要的,我們用來控制角色溶解的樣式貼圖,我們只利用了R通道。
3.DissolveSpeed(Second),整個溶解過程需要的時間,單位是秒
4.EdgeWidth,這個就是額外加的一個邊緣效果,比如你觀察紙在化作灰燼時他的周圍會先變成黑褐色。我們這里的EdgeWidth就是定義這個周圍區域的大小,注意這個width並不是指的長度,而是定義一個透明度的間隔區間,也就是與基准值相差多少可以算作邊緣處理。
5.EdgeColor,和4一樣,邊緣效果的顏色。
實現
1 Shader "Esfog/Dissolve"
2 { 3 Properties 4 { 5 _MainTex ("Base (RGB)", 2D) = "white" {} 6 _NoiseTex ("NoiseTex (R)",2D) = "white"{} 7 _DissolveSpeed ("DissolveSpeed (Second)",Float) = 1
8 _EdgeWidth("EdgeWidth",Range(0,0.5)) = 0.1
9 _EdgeColor("EdgeColor",Color) = (1,1,1,1) 10 } 11 SubShader 12 { 13 Tags { "RenderType"="Opaque" } 14
15 Pass 16 { 17 CGPROGRAM 18 #pragma vertex vert_img
19 #pragma fragment frag
20 #include "UnityCG.cginc"
21
22 uniform sampler2D _MainTex; 23 uniform sampler2D _NoiseTex; 24 uniform float _DissolveSpeed; 25 uniform float _EdgeWidth; 26 uniform float4 _EdgeColor; 27
28 float4 frag(v2f_img i):COLOR 29 { 30 float DissolveFactor = saturate(_Time.y / _DissolveSpeed); 31 float noiseValue = tex2D(_NoiseTex,i.uv).r; 32 if(noiseValue <= DissolveFactor) 33 { 34 discard; 35 } 36
37 float4 texColor = tex2D(_MainTex,i.uv); 38 float EdgeFactor = saturate((noiseValue - DissolveFactor)/(_EdgeWidth*DissolveFactor)); 39 float4 BlendColor = texColor * _EdgeColor; 40
41 return lerp(texColor,BlendColor,1 - EdgeFactor); 42 } 43
44 ENDCG 45 } 46 } 47
48 FallBack Off 49 }
5~9行,前面解釋過了,這里就不再說明了。
18行,這里我使用了unity提供的vert_img頂點着色器,因為我們的需求很簡單,只需要進行頂點坐標變換以及把uv傳給后面就行了,既然有現成的就不自己寫了,它會自動把結果返回到一個v2f_img結構體中.
30行,這里的_Time.y就是從物體一開始渲染到現在所過去的時間unity給我們提供了幾個不同的時間參數都保存在_Time中,其中_Time.y是標准的時間,單位(s)。用它去除以_DissolveSpeed.就能得到當前時間已經占用了多少總溶解時間.由於我們要保證比值范圍限定在(0,1)的范圍內,所以最后調用一個saturate()函數,這個函數式CG提供的就是來完成這個目的的。
31行,從溶解貼圖里面取出r通道的值.
32~35行,我們利用剛才算出比值來對溶解貼圖里面的R通道顏色值進行判斷,如果R通道顏色值小於等於這個比值那么我們就通過discard拋棄當前片遠,當前片遠的處理會立刻結束。
38行,又是計算一個比值,分子是noiseValue - DissolveFactor,這個值表示溶解貼圖上的R通道值和目前的溶解基准值相差多少,而(_EdgeWidth*DissolveFactor)可以這樣理解,_EdgeWidth表示可以多大的差值可以算作是邊緣,而乘以一個DissolveFactor,就表示邊緣最大寬度會隨着時間變化時間越長,寬度越寬。最后這兩個值相除就代表當前片元的邊緣程度,值越大表示離他被溶解掉的時間越長,反之表示他很快就要被溶解掉了.
39行,我們計算一下當前紋理顏色與邊緣顏色相乘的值,后面需要用。
41行,這里我們用lerp函數來利用上一步計算出來的(1-EdgeFactor)對原始紋理顏色和上一步中的混合顏色進行一個插值,在解釋第38行時候我們已經說了,EdgeFactor越小表示離溶解時間越近,也就是說它邊緣化的顏色成分越重,那么由於lerp(x,y,a) = x*(1-a) + y*a;為了讓邊緣化的顏色重一些我們就使用1-EdgeFactor作為因子了,反之亦然。
好了看一下效果,使用不同的溶解貼圖會產生完全不同的效果,下面展示一下我從網上隨便找到的兩個貼圖,由於和原模型紋理並不是一套的,所以看上去會有點怪,不要在意。
使用這個貼圖1:
效果如下
使用貼圖2:
效果如下:
好了基本效果就是如此,總體來說這個Shader的效果看起來好與不好,主要還是取決於美術提供的溶解貼圖是否合適,本節素材並不是一套的,看起來會有點怪。
另外一點就是大家在學習Shader的過程中你總會發現別人打源代碼中會出現很多pow,lerp,或者對各個參數的加減乘除,希望大家不要太糾結這些地方的意義,有時候確實是有一定的數學原理,但很多時候都是開發者自己寫的一個經驗公式,為了調效果而寫的。隨着你的經驗慢慢增多,漸漸的你也會開始使用一些經驗公式。效果對了就好,只要不影響性能。畢竟給玩家看的又不是源代碼。
尊重他人智慧成果,歡迎轉載,請注明作者esfog,原文地址http://www.cnblogs.com/Esfog/p/4469025.html