最近遇到了一个需求,想要一种在刀剑上带有光晕的酷炫效果,甚至是还想要有闪烁呼吸效果,于是就写了一个简单的叫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参数,可以实现想要的效果。