【shader UI 特效】用 shader 实现 UI 边缘流光效果

没有找到合适的插件,就找工具写了一个随便改改

换个贴图可以更好看

Shader "Custom/EdgeFlow"
{
    Properties
    {
        _MainTex ("Base Texture", 2D) = "white" {}
        _ShrinkRatio ("Shrink Ratio", Range(0.01, 0.5)) = 0.2 // 缩小比例
        _MoveSpeed ("Move Speed", Range(-1, 1)) = 1.0 // 移动速度
        _EdgeOffset ("Edge Offset", Range(0, 0.1)) = 0.02 // 边缘偏移量,控制离边缘的距离
        _TintColor ("Tint Color", Color) = (1,1,1,1) // 颜色 tint
        [Toggle]_TrailEnable("Enable Trail",Int) = 0
        _TrailLength ("Trail Length", Range(1, 50)) = 5 // 拖尾长度
        _TrailShrinkSpeed ("Trail Shrink Speed", Range(0.1, 5)) = 1.0 // 拖尾缩小速度
        _TrailDistance("Trail Distance Offset",Range(0.01,0.2)) = 0.1
        //[Toggle]_RandomOffset("Trail Random Offset",Int) = 0
        _RandomRange("Trail Random Range",Range(0,0.2)) = 0.1
        [Toggle]_DualEnable("Enable Dual Point",Int) = 0
        _SecondTrailTint("Second Tint Color",Color)=(1,1,1,1)
        _SecondTrailOffset("Second Position Offset",float) = 0
    }

    SubShader
    {
        Tags { "Queue"="Transparent" "RenderType"="Transparent" "IgnoreProjector"="True" }
        LOD 100

        Blend SrcAlpha OneMinusSrcAlpha
        Cull Off
        ZWrite Off

        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;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _ShrinkRatio;
            float _MoveSpeed;
            float _EdgeOffset;
            float4 _TintColor;
            float _TrailLength;
            float _TrailShrinkSpeed;
            float _TrailDistance;
            bool _TrailEnable;
            bool _DualEnable;
            float4 _SecondTrailTint;
            float _SecondTrailOffset;

            // 存储历史位置的数组(简化实现,实际项目中可能需要更复杂的缓冲区)
            // 这里我们用时间参数来模拟历史位置
            float2 calculatePosition(float timeOffset) {
                float time = (_Time.y - timeOffset) * _MoveSpeed;
                float progress = frac(time) * 4.0;
                
                float halfSize = _ShrinkRatio * 0.5;
                float edgeoffset = (1.0 - 2.0 * _EdgeOffset - 2.0 * halfSize);
                
                float2 pos;
                if (progress < 1.0)
                {
                    float t = progress;
                    pos = float2(_EdgeOffset + halfSize + t * edgeoffset, 
                                1.0 - _EdgeOffset - halfSize);
                }
                else if (progress < 2.0)
                {
                    float t = progress - 1.0;
                    pos = float2(1.0 - _EdgeOffset - halfSize, 
                                1.0 - _EdgeOffset - halfSize - t * edgeoffset);
                }
                else if (progress < 3.0)
                {
                    float t = progress - 2.0;
                    pos = float2(1.0 - _EdgeOffset - halfSize - t * edgeoffset, 
                                _EdgeOffset + halfSize);
                }
                else
                {
                    float t = progress - 3.0;
                    pos = float2(_EdgeOffset + halfSize, 
                                _EdgeOffset + halfSize + t * edgeoffset);
                }
                return pos;
            }

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // 计算当前主贴图的位置
                float currentTime = _Time.y * _MoveSpeed;
                float currentProgress = frac(currentTime) * 4.0;
                
                float halfSize = _ShrinkRatio * 0.5;
                float edgeoffset = (1.0 - 2.0 * _EdgeOffset - 2.0 * halfSize);
                
                float2 current_pos = calculatePosition(0);
                
                // 计算当前主贴图的UV坐标
                float2 current_scaledUV = (i.uv - current_pos) / (_ShrinkRatio) + 0.5;
                float2 current_inBounds = step(0.0, current_scaledUV) * step(current_scaledUV, 1.0);
                float current_alpha = current_inBounds.x * current_inBounds.y;
                fixed4 current_col = tex2D(_MainTex, current_scaledUV) * _TintColor;
                current_col.a *= current_alpha;
                
                // 计算拖尾效果
                fixed4 trail_col = fixed4(0, 0, 0, 0);
                float trail_contribution = 0;

                // 生成多个拖尾贴图
                if(_TrailEnable)
                for (int j = 1; j <= _TrailLength; j++) {
                    float timeOffset = (float)j * _TrailDistance / _TrailShrinkSpeed; // 时间间隔
                    //float timeOffset = (float)j * 0.1 / _TrailShrinkSpeed; // 时间间隔
                    float2 trail_pos = calculatePosition(timeOffset);
                    
                    // 计算拖尾的缩放比例(随时间减小)
                    float trail_shrink_ratio = _ShrinkRatio * (1.0 - (float)j / _TrailLength);
                    if (trail_shrink_ratio <= 0) trail_shrink_ratio = 0.01; // 防止除零
                    
                    float2 trail_scaledUV = (i.uv - trail_pos) / (trail_shrink_ratio) + 0.5;
                    float2 trail_inBounds = step(0.0, trail_scaledUV) * step(trail_scaledUV, 1.0);
                    float trail_alpha = trail_inBounds.x * trail_inBounds.y;
                    
                    if (trail_alpha > 0.5) {
                        fixed4 temp_col = tex2D(_MainTex, trail_scaledUV) * _TintColor;
                        temp_col.a *= trail_alpha * (1.0 - (float)j / _TrailLength); // 透明度渐变
                        trail_col = max(trail_col, temp_col); // 叠加拖尾颜色
                    }
                }
                fixed4 second_trail_col = fixed4(0,0,0,0);
                if (_DualEnable) {
                    // 计算第二个贴图当前位置
                    float2 second_current_pos = calculatePosition(_SecondTrailOffset);
                    float2 second_current_scaledUV = (i.uv - second_current_pos) / (_ShrinkRatio) + 0.5;
                    float2 second_current_inBounds = step(0.0, second_current_scaledUV) * step(second_current_scaledUV, 1.0);
                    float second_current_alpha = second_current_inBounds.x * second_current_inBounds.y;
                    fixed4 second_current_col = tex2D(_MainTex, second_current_scaledUV) * _SecondTrailTint;
                    second_current_col.a *= second_current_alpha;
                    
                    // 生成第二个贴图的拖尾
                    for (int k = 1; k <= _TrailLength; k++) {
                        float timeOffset = (float)k * _TrailDistance / _TrailShrinkSpeed;
                        float2 second_trail_pos = calculatePosition(timeOffset + _SecondTrailOffset);
                        
                        float second_trail_shrink_ratio = _ShrinkRatio * (1.0 - (float)k / _TrailLength);
                        if (second_trail_shrink_ratio <= 0) second_trail_shrink_ratio = 0.01;
                        
                        float2 second_trail_scaledUV = (i.uv - second_trail_pos) / (second_trail_shrink_ratio) + 0.5;
                        float2 second_trail_inBounds = step(0.0, second_trail_scaledUV) * step(second_trail_scaledUV, 1.0);
                        float second_trail_alpha = second_trail_inBounds.x * second_trail_inBounds.y;
                        
                        if (second_trail_alpha > 0.5) {
                            fixed4 temp_col = tex2D(_MainTex, second_trail_scaledUV) * _SecondTrailTint;
                            temp_col.a *= second_trail_alpha * (1.0 - (float)k / _TrailLength);
                            second_trail_col = max(second_trail_col, temp_col);
                        }
                    }
                    
                    // 混合第二个贴图和其拖尾
                    second_trail_col = second_current_col + (1.0 - second_current_col.a) * second_trail_col;
                }

                // 混合所有效果
                fixed4 final_col = current_col + (1.0 - current_col.a) * trail_col;
                final_col = final_col + (1.0 - final_col.a) * second_trail_col;
                final_col.a = max(max(current_col.a, trail_col.a), second_trail_col.a);

                // 混合主贴图和拖尾效果
                // fixed4 final_col = current_col + (1.0 - current_col.a) * trail_col;
                // final_col.a = max(current_col.a, trail_col.a); // 确保透明度正确
                
                return final_col;
            }
            ENDCG
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值