很多游戲Logo中可以看到這種流光效果,一般的實現方案就是對帶有光條的圖片uv根據時間進行移動,然后和原圖就行疊加實現,不過實現過程中稍稍有點需要注意的地方。之前考慮過風宇沖的實現方式,但是考慮到shader中太多的計算,還是放棄了。
基礎版本
Shader "UICustom/ImageFlashEffect2"
{
Properties
{
_MainTex ("Main Texture", 2D) = "white" {}
_LightTex ("Light Texture", 2D) = "white" {}
_LightColor("Light Color",Color) = (1,1,1,1)
_LightPower("Light Power",Range(0,5)) = 1
//每次持續時間,受Angle和Scale影響
_LightDuration("Light Duration",Range(0,10)) = 1
//時間間隔,受Angle和Scale影響
_LightInterval("Light Interval",Range(0,20)) = 3
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
}
Cull Off
Lighting Off
ZWrite Off
Fog { Mode Off }
Offset -1, -1
Blend SrcAlpha OneMinusSrcAlpha
AlphaTest Greater 0.1
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float2 lightuv : TEXCOORD1;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _LightTex ;
float4 _LightTex_ST;
half _LightInterval ;
half _LightDuration ;
half4 _LightColor ;
half _LightPower ;
half _LightOffSetX ;
half _LightOffSetY ;
v2f vert (appdata v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
fixed currentTimePassed = fmod(_Time.y,_LightInterval);
//uv offset, Sprite wrap mode need "Clamp"
fixed offsetX = currentTimePassed / _LightDuration;
fixed offsetY = currentTimePassed / _LightDuration;
fixed2 offset ;
offset.x = offsetX - 0.5f;
offset.y = offsetY - 0.5f;
o.lightuv = v.uv + offset ;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.lightuv = TRANSFORM_TEX(o.lightuv, _LightTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 mainCol = tex2D(_MainTex, i.uv);
fixed4 lightCol = tex2D(_LightTex, i.lightuv);
lightCol *= _LightColor ;
//need blend
//lightCol.rgb *= mainCol.rgb ;
fixed4 fininalCol ;
fininalCol.rgb = mainCol.rgb + lightCol.rgb * _LightPower;
fininalCol.a = mainCol.a * lightCol.a ;
return fininalCol ;
}
ENDCG
}
}
}
需要注意的點:
- 時間間隔問題
fixed currentTimePassed = fmod(_Time.y,_LightInterval);
//uv offset, Sprite wrap mode need "Clamp"
fixed offsetX = currentTimePassed / _LightDuration;
fixed offsetY = currentTimePassed / _LightDuration;
offsetX 、offsetY其實是0~_LightInterval的數值,需要設置圖片為光線圖片的Wrap模式為Clamp,才能實現時間間隔控制
- 色彩融合
Pixel着色器中frag()函數是通過原始顏色和光線顏色疊加的方式實現的,也有將光線顏色和原圖混合后再疊加的做法,這個我覺得看實際應用。注意alpha的控制
優化
1. 增加角度和大小控制
基本版本的實現中光線只能根據流光圖片中亮線的寬度和方向決定實際的滾動方向和大小。有時候如果需要經常調節方向和大小,可以考慮加入相關因素。流光的Uv是通過原圖當前Uv加上時間軸參數獲得,可以考慮通過修改流光uv的計算方式來實現。如下面代碼。不過這種方式增加了一定計算量,不需要的話則直接跳過。還有一點就是流光貼圖必須是垂直或者水平。
float2 base = v.uv ;
base.x -= _LightOffSetX ;
base.y -= _LightOffSetX ;
base = base / _LightScale ;
float2 base2 = v.uv;
base2.x = base.x * cosInRad - base.y * sinInRad ;
base2.y = base.y * cosInRad + base.x * sinInRad ;
o.lightuv = base2 + offset ;
材質復用
只是想不同的圖片都是使用相同的材質,應該保證每個圖片的效果都可以獨立進行修改保存,可以考慮每張圖片都創建一份材質,然后修改其參數。
void UpdateParam()
{
if (Material == null)
{
Debug.LogWarning("Metarial is miss");
return;
}
if (mGraphic == null)
{
mGraphic = GetComponent<MaskableGraphic>();
}
if (mGraphic is Text)
{
Debug.LogError("FlashEffec need component type of Image、RawImage");
return;
}
if (mDynaMaterial == null)
{
mDynaMaterial = new Material(Material);
mDynaMaterial.name = mDynaMaterial.name + "(Copy)";
mDynaMaterial.hideFlags = HideFlags.DontSave | HideFlags.NotEditable;
}
if (mDynaMaterial == null)
{
return;
}
mDynaMaterial.mainTexture = null;
if (OverrideTexture != null)
{
mDynaMaterial.mainTexture = OverrideTexture;
if (mGraphic is RawImage)
{
RawImage img = mGraphic as RawImage;
img.texture = null;
}
else if (mGraphic is Image)
{
Image img = mGraphic as Image;
img.sprite = null;
}
}
else
{
mDynaMaterial.mainTexture = mGraphic.mainTexture;
}
if (Duration > Interval)
{
Debug.LogWarning("ImageFlashEffect.UpdateParam:Duration need less Interval");
Interval = Duration + 0.5f;
}
mDynaMaterial.SetColor("_LightColor", Color);
mDynaMaterial.SetFloat("_LightPower", Power);
mDynaMaterial.SetFloat("_LightScale", Scale);
mDynaMaterial.SetFloat("_LightAngle", Angle);
mDynaMaterial.SetFloat("_LightDuration", Duration);
mDynaMaterial.SetFloat("_LightInterval", Interval);
mDynaMaterial.SetFloat("_LightOffSetX", OffSet);
mGraphic.material = mDynaMaterial;
mGraphic.SetMaterialDirty();
}
下載:https://github.com/carlosCn/Unity_Image_Flash_Effect
參考
http://blog.csdn.net/qq992817263/article/details/51200424
http://qkxue.net/info/169189/Unity-Simple-Shaderlab-uGui-shader-1

