今天我们要在 UGUI 上实现图片RGB通道分离抖动效果,先看最终效果图

p002701_imageglitch

实现这个效果,使用了 Shader 和 C# 代码,Shader 用于实现效果,C# 用于控制抖动开关,也就是什么时候抖动,什么时候停止。Shader 的原理大概就是通过不同的参数,分别采样图片的RGB三个通道的颜色,然后再合成最终的颜色。Shader代码如下

Shader "iMoeGirl/DouYin" {
    Properties {
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
        _ScanLineJitter("ScanLineJitter", Vector) = (0, 0, 0, 0)
        _VerticalJump("VerticalJump", Vector) = (0, 0, 0, 0)
        _HorizontalShake("HorizontalShake", Float) = 0.005
        _ColorDrift("ColorDrift", Vector) = (0.06, 0, 0, 0)
        _IsOpen("IsOpen", Int) = 1
    }

    SubShader {
              
        pass {
            Blend SrcAlpha OneMinusSrcAlpha
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            float2 _ScanLineJitter;
            float2 _VerticalJump;   
            float _HorizontalShake;
            float2 _ColorDrift;     

            struct appdata_t {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f {
                float4 vertex : SV_POSITION;
                half2 uv : TEXCOORD0;
            };

            float nrand(float x, float y)
            {
                return frac(sin(dot(float2(x, y), float2(12.9898, 78.233))) * 43758.5453);
            }

            v2f vert(appdata_t IN){
                v2f OUT;
                OUT.vertex = UnityObjectToClipPos(IN.vertex);
                OUT.uv = IN.uv;
                return OUT;
            }

            sampler2D _MainTex;
            float _Scale;
            float _IsOpen;

            fixed4 frag(v2f i) : SV_TARGET {
                float u = i.uv.x;
                float v = i.uv.y;

                // Scan line jitter
                float jitter = nrand(v, _Time.x) * 2 - 1;
                jitter *= step(_ScanLineJitter.y, abs(jitter)) * _ScanLineJitter.x;

                // vertical jump
                float jump = lerp(v, frac(v + _VerticalJump.y), _VerticalJump.x);

                // Horizontal shake
                float shake = (nrand(_Time.x, 2) - 0.5) * _HorizontalShake;

                // Color drift
                float drift = (sin(jump + _ColorDrift.y )) * _ColorDrift.x;

                float value = (nrand(_Time.x, 2) - 0.5) * 0.002 * _IsOpen;

                half4 src1 = tex2D(_MainTex, frac(float2(u + (jitter + shake) * _IsOpen, jump)));
                half4 src2 = tex2D(_MainTex, frac(float2(u + (jitter + shake + drift + value) * _IsOpen, jump)));
                half4 src3 = tex2D(_MainTex, frac(float2(u + (jitter + shake - drift - value * 3) * _IsOpen, jump)));
                return half4(src1.r, src2.g, src3.b, src1.a);

            }

            ENDCG
        }
    }
}

C# 代码通过控制Shader中的_IsOpen的值,设置为0则是关掉抖动效果,设置为1则是开启抖动效果。通过随机值来控制抖动间隔。

using UnityEngine;
using UnityEngine.UI;

public class ImageGlitch : MonoBehaviour
{
    private Image mImage;

    private float effectTicker = 0.0f;
    private float stayTicker = 0.0f;

    private void Start()
    {
        mImage = gameObject.GetComponent<Image>();
        CloseEffect();
        stayTicker = Random.Range(0.2f, 0.6f);
    }

    // Update is called once per frame
    void Update()
    {
        if(stayTicker > 0)
        {
            stayTicker -= Time.deltaTime;
            if(stayTicker <= 0)
            {
                effectTicker = Random.Range(0.2f, 0.6f);
                OpenEffect();
            }
        }

        if(effectTicker > 0)
        {
            effectTicker -= Time.deltaTime;
            if(effectTicker <= 0)
            {
                stayTicker = Random.Range(1.0f, 2.0f);
                CloseEffect();
            }
        }
    }

    private void OpenEffect()
    {
        mImage.material.SetInt("_IsOpen", 1);
    }

    private void CloseEffect()
    {
        mImage.material.SetInt("_IsOpen", 0);
    }
}

新建立 一个 Image 组件,然后将自己的图片赋予 Image。新建一个 Material,使用我们上面写的 Shader,然后将 Material 赋予 Image 组件,挂载 C# 代码,运行即可看到效果。