先上效果

溶解效果

再上代码

Shader "Custom/BarrierDissolve"
{
    Properties
    {
        [Header(Base)]
        [PerRendererData] _MainTex ("Main Texture", 2D) = "white" {}
        [HDR] _Color ("Tint Color", Color) = (1,1,1,1)
        
        [Header(Dissolve Settings)]
        _NoiseTex ("Noise Texture", 2D) = "gray" {}
        _Progress ("Progress (0=Center, 1=Full)", Range(0, 1.5)) = 0.0
        _MaxDistance ("Max Distance (Screen Corner)", Float) = 0.8
        _Center ("Center Point (UV)", Vector) = (0.5, 0.5, 0, 0)
        
        [Header(Edge Settings)]
        _NoiseStrength ("Noise Distortion Strength", Range(0, 1)) = 0.2
        _EdgeWidth ("Edge Width", Range(0.01, 0.5)) = 0.1
        [HDR] _EdgeColor ("Edge Color", Color) = (0, 1, 1, 1)
        
        [HideInInspector] _StencilComp ("Stencil Comparison", Float) = 8
        [HideInInspector] _Stencil ("Stencil ID", Float) = 0
        [HideInInspector] _StencilOp ("Stencil Operation", Float) = 0
        [HideInInspector] _StencilWriteMask ("Stencil Write Mask", Float) = 255
        [HideInInspector] _StencilReadMask ("Stencil Read Mask", Float) = 255
        [HideInInspector] _ColorMask ("Color Mask", Float) = 15
    }
    SubShader
    {
        Tags 
        { 
            "Queue"="Transparent" 
            "IgnoreProjector"="True" 
            "RenderType"="Transparent" 
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }
        
        Stencil
        {
            Ref [_Stencil]
            Comp [_StencilComp]
            Pass [_StencilOp]
            ReadMask [_StencilReadMask]
            WriteMask [_StencilWriteMask]
        }

        Cull Off
        Lighting Off
        ZWrite Off
        ZTest [unity_GUIZTestMode]
        Blend SrcAlpha OneMinusSrcAlpha
        ColorMask [_ColorMask]

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float4 color : COLOR;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float2 uv_noise : TEXCOORD1;
                fixed4 color : COLOR;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _NoiseTex;
            float4 _NoiseTex_ST;
            
            fixed4 _Color;
            float _Progress;
            float _MaxDistance;
            float4 _Center;
            
            float _NoiseStrength;
            float _EdgeWidth;
            fixed4 _EdgeColor;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.uv_noise = TRANSFORM_TEX(v.uv, _NoiseTex);
                o.color = v.color * _Color;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // 计算像素到中心的距离 (UV空间)
                float dist = distance(i.uv, _Center.xy);

                // 采样噪声 (0~1)
                float noise = tex2D(_NoiseTex, i.uv_noise).r;

                // 计算当前的扩散半径阈值
                float currentRadius = _Progress * (_MaxDistance + _NoiseStrength);

                // 可见性判定值
                float val = currentRadius - (noise * _NoiseStrength) - dist;

                // 裁剪
                if (val < 0)
                {
                    discard;
                }

                // 采样主纹理,并乘以顶点颜色
                fixed4 col = tex2D(_MainTex, i.uv) * i.color;
                
                // 计算边缘发光
                float edgeFactor = 1.0 - smoothstep(0.0, _EdgeWidth, val);
                
                // 叠加边缘颜色
                col.rgb += _EdgeColor.rgb * edgeFactor;
                
                // 让边缘的 Alpha 也受影响,保证边缘发光是不透明的
                col.a = max(col.a, edgeFactor * _EdgeColor.a);

                return col;
            }
            ENDCG
        }
    }
}

附带一张噪声图

附一个代码进度用法

using UnityEngine;
using PrimeTween;

public class BarrierDissolve : MonoBehaviour
{
    private Material _material;
    private int _progressId;
    private const string PropertyName = "_Progress";
    private const float Duration = 1.0f;
    private const float TargetValue = 1.5f;
    private Tween _tween;

    private void Awake()
    {
        _progressId = Shader.PropertyToID(PropertyName);
        
        // 尝试获取 SpriteRenderer 或 MeshRenderer
        var rend = GetComponent<Renderer>();
        if (rend != null)
        {
            // Renderer.material 会自动实例化材质,不影响其他物体
            _material = rend.material;
        }

        if (_material == null)
        {
            Debug.LogError($"[BarrierDissolve]没有Render在{gameObject.name}物体身上, 或者材质丢失");
        }
    }

    private void OnEnable()
    {
        if (_material == null) return;
        
        // 停止之前的动画(如果有)
        if (_tween.isAlive) _tween.Stop();

        // 初始值
        _material.SetFloat(_progressId, 0f);

        // 使用 PrimeTween 从 0 到 TargetValue 变化
        _tween = Tween.Custom(0f, TargetValue, Duration, onValueChange: val => 
        {
            _material.SetFloat(_progressId, val);
        });
    }

    private void OnDisable()
    {
        // 禁用时停止动画
        if (_tween.isAlive) _tween.Stop();
    }

    private void OnDestroy()
    {
        // 停止动画
        if (_tween.isAlive) _tween.Stop();

        // 销毁实例化的材质,防止内存泄漏
        if (_material != null)
        {
            Destroy(_material);
            _material = null;
        }
    }
}

更多推荐