最近遇到了一個需求,想要一種在刀劍上帶有光暈的酷炫效果,甚至是還想要有閃爍呼吸效果,於是就寫了一個簡單的叫LightSwrod的Shader去實現,先上圖看看效果吧。
簡單展示
這是劍本身的樣子
這是用了LightSword后的效果
(原諒我的審美吧~~~調了半天也就這難看的樣子T_T)
這是Inspector里面Material的參數
參數的解釋將在后面進行細致講解
創建流程
來講述一下創建光劍效果的流程吧
1.准備兩張貼圖:
一張是劍體本身的貼圖,是真正要顯示出來的劍體
一張是要展現光暈效果,給Shader用的Alpha貼圖
Alpha貼圖可以拿劍體本身的貼圖復制一份來修改,由於Shader只取Alpha貼圖的Alpha值,所以修改方式是在劍體周圍加上漸變透明的顏色,最終光暈顏色越淺的地方Alpha值越低。
2.創建Sprite:
先將貼圖轉換成sprite格式
將圖片子節點下的sprite拖到Hierarchy下變成一個GameObject
再將貼圖調到Advanced格式
注意:需要將Mesh Type調成FullRect模式,使Sprite生成的Mesh是一個完整的矩形,
否則Unity會自動 生成一個被裁減的Mesh,在這部分之外是不會被渲染的,導致光暈有被裁剪的感覺,如下圖
3.創建Material和Shader:
Material可以直接在Project里面右鍵->Create->Material來創建,Shader同理 。
如下,我們創建了Material和Shader
接着將Shader拖到Material上,但你你看到的Inspector的樣子一定跟上面給不一樣,因為Shader還沒有改寫好,下面就開始講述如何編寫這個Shader。
4.Shader編寫:
先把完整的Shader貼出來吧:
// 發光劍的光暈實現
// By XiaoZeFeng
Shader "custom/LightSword"
{
Properties
{
_MainTex("Main Texture (RGB)", 2D) = "white" {} // 主貼圖 //
_MainColorTimes("MainColorTimes", Range(0, 30)) = 1 // 主圖顏色增強倍數 //
_EmissionTex("_EmissionTex", 2D) = "white" {} // 光暈Alpha圖,取Alpha值填補Emission顏色 //
_EmissionAlphaTimes("EmissionAlphaTimes", Range(0, 50)) = 1 // 光暈Alpha增強倍數 //
_EmissionAlphaExponent("EmissionAlphaExponent", Range(0, 10)) = 1 // 光暈Alpha指數,用於消除黑邊 //
_Emission1("Emmisive Color1", Color) = (0,0,0,0) // 劍體本身的發光顏色 //
_EmissionColorTimes1("EmissionColorTimes1", float) = 1 // 劍體本身的發光顏色倍數 //
_Emission2("Emmisive Color2", Color) = (0,0,0,0) // 劍體光暈的發光顏色 //
_EmissionColorTimes2("EmissionColorTimes2", float) = 1 // 劍體光暈的發光顏色倍數 //
_AllAlpha("AllAlpha", Range(0, 1)) = 1 // 整體Alpha值 //
}
SubShader
{
Tags
{
"Queue" = "Transparent+100"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
}
LOD 100
Cull Off
ZWrite Off
AlphaTest Off
Blend SrcAlpha OneMinusSrcAlpha
Fog{ Mode Off }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
uniform fixed _MainColorTimes;
sampler2D _EmissionTex;
uniform float _EmissionAlphaTimes;
uniform float _EmissionAlphaExponent;
uniform fixed4 _Emission1;
uniform fixed _EmissionColorTimes1;
uniform fixed4 _Emission2;
uniform fixed _EmissionColorTimes2;
uniform fixed _AllAlpha;
struct appdata_t
{
float4 vertex : POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
half2 texcoord : TEXCOORD;
};
v2f vert(appdata_t IN)
{
v2f OUT;
OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
OUT.texcoord = IN.texcoord;
OUT.color = IN.color;
return OUT;
}
fixed4 frag(v2f IN) : COLOR
{
fixed4 mainColor = tex2D(_MainTex, IN.texcoord);
fixed4 emisionColor = tex2D(_EmissionTex, IN.texcoord);
mainColor.rgb = mainColor.rgb * _MainColorTimes
+ _Emission1 * mainColor.a * _EmissionColorTimes1
+ _Emission2 * (1 - mainColor.a) * emisionColor.a * _EmissionColorTimes2;
mainColor.a = max(mainColor.a, pow(emisionColor.a, _EmissionAlphaExponent)
* _EmissionAlphaTimes) * _AllAlpha;
return mainColor;
}
ENDCG
}
}
}
Shader講解
properties
在properties里面定義了10個參數,其中兩個是貼圖,兩個是顏色,其他都是float數值,其他參數所表達的含義都在注釋中有標注。
vertex
vertex方法里正常的將物體投影到屏幕上,慣例的做法,沒有什么特殊性,就不過多的解釋了。
fragment
fragment方法是實現劍光暈效果的主體,將由兩個公式來分別計算光暈劍每個像素點的顏色和Alpha值。
首先是取出劍本體和Alpha貼圖上關於當前像素點的顏色和Alpha值
fixed4 mainColor = tex2D(_MainTex, IN.texcoord);
fixed4 emisionColor = tex2D(_EmissionTex, IN.texcoord);
接着是像素點的顏色
mainColor.rgb = mainColor.rgb * _MainColorTimes
+ _Emission1 * mainColor.a * _EmissionColorTimes1
+ _Emission2 * (1 - mainColor.a) * emisionColor.a * _EmissionColorTimes2;
像素點的顏色由三方面組成:
1.劍主體貼圖的顏色。
2.劍體自發光的顏色。
3.劍體周圍光暈的顏色。
1.劍主體貼圖的顏色。
mainColor.rgb * _MainColorTimes
這里將劍體本身的顏色乘以_MainColorTimes參數來把劍體本身顏色增強,以免在光暈中顯得單薄。
2.劍體自發光的顏色。
_Emission1 * mainColor.a * _EmissionColorTimes1
_Emission1是劍體發光顏色參數,乘以mainColor.a主貼圖的Alpha值使其劍本體上有顏色變化,在劍體以外,也就是Alpha值為0的地方無效;最后乘以_EmissionColorTimes1顏色增強參數同樣用於將發光色增強。
3.劍體周圍光暈的顏色。
_Emission2 * (1 - mainColor.a) * emissionColor.a * _EmissionColorTimes2
_Emission2是劍體周圍發光顏色參數,乘以(1 - mainColor.a) 使其劍本體以外有顏色變化,在劍體本身,也就是Alpha值大於0的地方影響減弱或不影響;接着乘以emissionColor.a 也就是Alpha貼圖的Alpha值使光暈效果根據Alpha貼圖的Alpha值漸變顯示出向周圍漸漸變淡的效果,最后乘以_EmissionColorTimes2顏色增強參數同樣用於將發光色增強。
通過這三種顏色的疊加,實現了劍體顏色,劍體自發光和周圍光暈的效果。
最后是像素點顏色透明度Alpha值的計算:
mainColor.a = max(mainColor.a, pow(emisionColor.a, _EmissionAlphaExponent) * _EmissionAlphaTimes) * _AllAlpha;
輸出顏色的透明度等於劍主體的透明度和周圍光暈的透明度間的最大值最后乘以_AllAlpha參數,取最大值的原因在於使劍體和光暈得以顯示,因為在第一張貼圖中,除劍體以外部分的Alpha值都是0,所以需要將周圍光暈本身的Alpha值也給計算進去。
CGpow(emisionClolor.a, _EmissionAlphaExponent) * _EmissionAlphaTimes
是計算劍體周圍光暈Alpha值的公式,也許你有疑問,這里直接取第二章貼圖的Alpha值不就好了,為啥要搞這么復雜的計算?
首先pow()是一個指數運算方法,他將Alpha貼圖的Alpha值進行了指數運算,使得其漸變率變得更加明顯,這么做的原因是在Shader的實現過程中發現在Alpha貼圖外側,也就是Alpha值變低時會有黑邊的效果,樣子如下
通過這個指數運算,可以明顯的減緩這種問題,接着乘以_EmissionAlphaTimes參數同樣是為了增強Alpha值的效果,以達到讓光暈更加明顯的效果但同時也會增強黑邊的問題,所以這個值需要跟_EmissionAlphaExponent參數進行配合使用。
在Alpha值計算的最后,還將總的Alpha值乘以_AllAlpha參數,這是為了實現光暈的呼吸閃爍效果,通過在Unity里面創建Animation來變化這個_AllAlpha參數,可以實現想要的效果。