溶解效果 (Dissolve Effect)

使用噪声纹理实现物体消融

📚 原理

溶解效果的核心是使用噪声纹理的灰度值与阈值比较,低于阈值的像素被裁剪。通过动画改变阈值,实现逐渐消失的效果。

原始物体 + 噪声纹理 = 溶解结果

💻 代码实现

基础溶解 Shader

// Properties
_DissolveMap("Dissolve Map", 2D) = "white" {}
_DissolveThreshold("Dissolve Threshold", Range(0, 1)) = 0
_EdgeWidth("Edge Width", Range(0, 0.1)) = 0.05
_EdgeColor("Edge Color", Color) = (1, 0.5, 0, 1)

// Fragment Shader
half4 frag(Varyings input) : SV_Target
{
    // 采样溶解贴图
    float dissolveValue = SAMPLE_TEXTURE2D(_DissolveMap, sampler_DissolveMap, input.uv).r;
    
    // 裁剪低于阈值的像素
    clip(dissolveValue - _DissolveThreshold);
    
    // 采样基础颜色
    half4 baseColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv);
    
    // 边缘发光
    float edge = 1 - smoothstep(0, _EdgeWidth, dissolveValue - _DissolveThreshold);
    half3 finalColor = lerp(baseColor.rgb, _EdgeColor.rgb, edge);
    
    return half4(finalColor, baseColor.a);
}

完整 Shader 文件结构

Shader "Custom/Dissolve"
{
    Properties
    {
        _BaseMap("Base Map", 2D) = "white" {}
        _BaseColor("Base Color", Color) = (1, 1, 1, 1)
        _DissolveMap("Dissolve Map", 2D) = "white" {}
        _DissolveThreshold("Threshold", Range(0, 1)) = 0
        _EdgeWidth("Edge Width", Range(0, 0.2)) = 0.05
        _EdgeColor("Edge Color", Color) = (1, 0.5, 0, 1)
    }
    
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" }
        
        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            
            struct Attributes
            {
                float4 positionOS : POSITION;
                float2 uv : TEXCOORD0;
            };
            
            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD0;
            };
            
            TEXTURE2D(_BaseMap);
            SAMPLER(sampler_BaseMap);
            TEXTURE2D(_DissolveMap);
            SAMPLER(sampler_DissolveMap);
            
            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
                half4 _BaseColor;
                float _DissolveThreshold;
                float _EdgeWidth;
                half4 _EdgeColor;
            CBUFFER_END
            
            Varyings vert(Attributes input)
            {
                Varyings output;
                output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
                output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
                return output;
            }
            
            half4 frag(Varyings input) : SV_Target
            {
                float dissolve = SAMPLE_TEXTURE2D(_DissolveMap, sampler_DissolveMap, input.uv).r;
                clip(dissolve - _DissolveThreshold);
                
                half4 baseColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv) * _BaseColor;
                
                float edge = 1 - smoothstep(0, _EdgeWidth, dissolve - _DissolveThreshold);
                half3 finalColor = lerp(baseColor.rgb, _EdgeColor.rgb, edge);
                
                return half4(finalColor, 1);
            }
            ENDHLSL
        }
    }
}

🎮 应用场景

💀 角色死亡

敌人被击败后的消失效果

✨ 传送门

物体进入传送门时的溶解

🔥 燃烧效果

配合橙色边缘模拟燃烧

⚡ 数字化

科幻风格的像素化消失

💡 技巧

多层边缘

// 双色边缘
float edge1 = 1 - smoothstep(0, _EdgeWidth, dissolve - _DissolveThreshold);
float edge2 = 1 - smoothstep(0, _EdgeWidth * 2, dissolve - _DissolveThreshold);

half3 color = baseColor.rgb;
color = lerp(color, _EdgeColor2.rgb, edge2); // 外层
color = lerp(color, _EdgeColor1.rgb, edge1); // 内层

方向性溶解

// 从下往上溶解
float dirDissolve = input.positionOS.y * 0.5 + 0.5; // 归一化高度
float finalDissolve = dissolve * dirDissolve;
clip(finalDissolve - _DissolveThreshold);