轉載請標明出處http://www.cnblogs.com/zblade/
在實際的游戲工程中,經常美術和策划會提出溶解的表現要求。比如子彈在飛行的時候,彈道不斷的消融;角色受到大型炮彈的攻擊,在擊飛的時候不斷的消融等等諸如此類的表現。一般的消融都是結合粒子系統來實現,通過給粒子Render組件添加material來實現表現。
通過總結我在項目中用到的消融shader,以及在網上查找到的部分消融shader,我做一個基本的shader歸類,便於今后的思路查找,其中有任何錯誤請指出,大家一起學習進步。
實現溶解效果,基本方法是用一個基本紋理貼圖+無序圖來實現溶解的效果,基本紋理貼圖用來表示正常的效果,無序圖則表示消融的參考值。通常對消融圖是讓美術做一張層級圖,其中rgba四個通道任意選一個通道作為溶解的無序通道。
下面我先列出參考的一些shader的實現:
1、基本的實現單次溶解的vert/frag shader
Shader "Esfog/Dissolve" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _NoiseTex ("NoiseTex (R)",2D) = "white"{} _DissolveSpeed ("DissolveSpeed (Second)",Float) = 1 _EdgeWidth("EdgeWidth",Range(0,0.5)) = 0.1 _EdgeColor("EdgeColor",Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } Pass { CGPROGRAM #pragma vertex vert_img #pragma fragment frag #include "UnityCG.cginc" uniform sampler2D _MainTex; uniform sampler2D _NoiseTex; uniform float _DissolveSpeed; uniform float _EdgeWidth; uniform float4 _EdgeColor; float4 frag(v2f_img i):COLOR { float DissolveFactor = saturate(_Time.y / _DissolveSpeed); float noiseValue = tex2D(_NoiseTex,i.uv).r; if(noiseValue <= DissolveFactor) { discard; } float4 texColor = tex2D(_MainTex,i.uv); float EdgeFactor = saturate((noiseValue - DissolveFactor)/(_EdgeWidth*DissolveFactor)); float4 BlendColor = texColor * _EdgeColor; return lerp(texColor,BlendColor,1 - EdgeFactor); } ENDCG } } FallBack Off }
這篇shader來自於Esfog溶解shader,具體的代碼解釋可以參看原文作者的解釋,比較詳細,網上很多的相似shader大概都是來自於此。
我只說一下大概的思路,基本就是采樣無序圖,然后通過其中的某個通道(此處為r)的值,與當前的溶解系數對比,如果當前的通道值小於溶解系數,則說明當前片元需要被剔除。如果不被剔除,則判斷當前值距離消融的比例來設置消融的邊緣顏色混合。
2、根據外部觸發的消融vert/frag shader
上面的shader在開始觸發的時候就會不斷的消融,在某些時候,我們希望通過外部的時間來控制消融的觸發與否,則可以在shader中提供一個外部參數,通過在代碼中查找到該material,通過設置material的值來用作觸發消融。我們可以在上面的shader的基礎上做進一步的改進,改進后的代碼如下:
Shader "Esfog/Dissolve" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _NoiseTex ("NoiseTex (R)",2D) = "white"{} _DissolveSpeed ("DissolveSpeed (Second)",Float) = 1 _EdgeWidth("EdgeWidth",Range(0,0.5)) = 0.1 _EdgeColor("EdgeColor",Color) = (1,1,1,1)
_DissolveStartTime("DissolveStartTime",float)=0 } SubShader { Tags { "RenderType"="Opaque" } Pass { CGPROGRAM #pragma vertex vert_img #pragma fragment frag #include "UnityCG.cginc" uniform sampler2D _MainTex; uniform sampler2D _NoiseTex; uniform float _DissolveSpeed; uniform float _EdgeWidth; uniform float4 _EdgeColor; uniform float _DissolveStartTime;
float4 frag(v2f_img i):COLOR {
bool isNormal = true;
float c = 1;
float4 texColor = tex2D(_MainTex,i.uv);
if(_DissolveStartTime > 0 )
{
float DissolveFactor = saturate((_Time.y-_DissolveStartTime) * _DissolveSpeed);
float noiseValue = tex2D(_NoiseTex,i.uv).r;
if(noiseValue <= DissolveFactor)
{
discard;
}
float EdgeFactor = saturate((noiseValue - DissolveFactor)/(_EdgeWidth*DissolveFactor)); float4 BlendColor = texColor * _EdgeColor;
texColor = lerp(texColor,BlendColor,1 - EdgeFactor);
}
return texColor;
} ENDCG } } FallBack Off }
通過外部的設置material中的_DissolveStartTime,我們可以控制消融的開始與否,這兒在對消融系數求解的時候,我改為相乘,這樣消融的速度就是正確的表明速度。
3、用透明通道來實現的消融
以上兩種shader都是基於一張無序圖來判定是否消融,如果我們需要做一種透明度逐漸消散的效果,直接采用discard會顯得很生硬,不能實現不透明,半透明,透明這樣的逐漸消散的效果。如果我們需要用透明的方式來實現消融。這兒我給出一種用透明度實現的消融shader的實現,主要也是通過兩張貼圖的采用和某些通道值得對比來控制混合的alpha通道。
代碼如下:
Shader"Z/DissolveWithBlend" { Propertise{ _Color("Color&Alpha",Color)=(1,1,1,1) _MainTex("MainTex",2D)="white"{} _Mask("Mask",2D)="white"{} _AlphaFactor("AlphaFactor",Float)=0.0 } Subshader{ Tags{"Queue"="Transparent" "IgnoreProjector"="True""RenderType"="Transparent"} Pass{ Tags{"LigthMode"="ForwardBase"} Blend SrcAlpha OneMinusAlpha Cull Front ZWrite off CGPROGRAM #include "UnigytCG.cginc" #pragma vertex vert #pragma fragment frag sampler2D _MainTex; float4 _MainTex_ST; sampler2D _Mask; float4 _Mask_ST; struct a2v{ float4 vertex:POSITION; float2 texcoord:TEXCOORD0; } struct v2f{ float4 pos:SV_POSITION; float2 uv0:TEXCOORD0; float2 uv1:TEXCOORD1; } v2f vert(a2v i){ v2f o; o.pos = mul(UNITY_MATRIX_MVP,i.vertex); o.uv0 = TRANSFORM_TEX(i.texcoord,_MainTex); o.uv1 = TRANSFORM_TEX(i.texcoord,_Mask); } fixed4 frag(v2f i):Color{ float4 _mainVar = tex2D(_MainTex,i.uv0); float4 _maskVar = tex2D(_Mask,i.uv1); float3 emissive = _Color.rgb * _mainVar.rgb; return fixed4(emissive,_Color.a * _mainVar.a * step(maskVar.r,_AlphaFactor)); } ENDCG } }
FallBack"Diffuse" }
基本的操作就是通過設置顏色的alpha通道的透明度,來實現一種用透明度控制的消融效果。當然,這樣的shader對於時間的控制,是需要這個特效外部的particle組件來控制的,這里沒有設置對時間的操作。