【Unity Shader】 溶解效果实战教程

@[toc]【Unity Shader URP】溶解效果(Dissolve)实战教程

0. 效果预览


溶解(Dissolve)是游戏中最常用的消失/出现特效之一:角色死亡燃烧消散、传送门边缘腐蚀、道具拾取后溶解消失,都是它。核心视觉特征是从噪声纹理驱动的不规则边缘逐步裁剪像素,边缘带一圈发光色带


1. 原理简述

溶解的本质:用一张噪声纹理的灰度值和一个阈值做比较,低于阈值的像素直接 clip 丢弃;刚好在阈值附近的像素染上边缘发光色。

关键伪代码:

hlsl 复制代码
float noise = tex2D(_NoiseTex, uv).r;   // 0~1 噪声值
clip(noise - _DissolveAmount);            // 低于阈值的像素被丢弃
// 边缘发光:阈值附近的窄带
float edge = smoothstep(_DissolveAmount, _DissolveAmount + _EdgeWidth, noise);
color = lerp(_EdgeColor, baseColor, edge);

_DissolveAmount 从 0 → 1,物体从完整逐渐消失。噪声纹理的分布决定了溶解的形状------Perlin noise 出来是自然燃烧感,Voronoi 出来是晶格碎裂感。


2. 功能点

  • 噪声驱动裁剪 :用噪声纹理 + 阈值 clip 像素,产生不规则溶解边缘
  • 边缘发光色带:溶解边缘处叠加可自定义的发光颜色,支持 HDR
  • 溶解进度控制_DissolveAmount(0~1)控制溶解程度,可用脚本/动画驱动
  • 边缘宽度可调_EdgeWidth 控制发光带的粗细
  • 双面渲染Cull Off,溶解过程中可以看到模型内侧

3. 完整 Shader(可直接用)

hlsl 复制代码
Shader "Custom/Dissolve_URP"
{
    Properties
    {
        // 主贴图(物体原始外观)
        _BaseMap ("Base Map", 2D) = "white" {}
        // 主颜色叠乘
        _BaseColor ("Base Color", Color) = (1,1,1,1)

        // 噪声纹理(灰度,决定溶解形状;推荐 Perlin / Simplex / Voronoi)
        _NoiseTex ("Noise Texture", 2D) = "white" {}
        // 噪声 UV 缩放(值越大,溶解碎片越细小)
        _NoiseScale ("Noise Scale", Float) = 1.0

        // 溶解进度(0 = 完整,1 = 完全消失)
        _DissolveAmount ("Dissolve Amount", Range(0, 1)) = 0.0

        // 边缘发光颜色(建议用 HDR 颜色,配合 Bloom 后处理更好看)
        [HDR] _EdgeColor ("Edge Color", Color) = (3, 0.5, 0, 1)
        // 边缘发光带宽度(占噪声值的比例,越大发光带越宽)
        _EdgeWidth ("Edge Width", Range(0.01, 0.2)) = 0.05
    }

    SubShader
    {
        Tags
        {
            "RenderPipeline" = "UniversalRenderPipeline"
            "Queue" = "AlphaTest"       // 用 AlphaTest 队列,clip 不需要排序
            "RenderType" = "TransparentCutout"
        }

        Pass
        {
            Name "DissolvePass"
            Tags { "LightMode" = "UniversalForward" }

            Cull Off        // 双面渲染:溶解时能看到模型内侧
            ZWrite On       // 写深度:不透明裁剪不需要关深度
            Blend Off       // 不透明混合

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // GPU Instancing(可选,多实例时有用)
            #pragma multi_compile_instancing

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            // =========================================================
            // 贴图声明
            // =========================================================
            TEXTURE2D(_BaseMap);    SAMPLER(sampler_BaseMap);
            TEXTURE2D(_NoiseTex);   SAMPLER(sampler_NoiseTex);

            // =========================================================
            // 材质属性(与 Properties 一一对应)
            // =========================================================
            float4 _BaseMap_ST;
            float4 _BaseColor;
            float  _NoiseScale;
            float  _DissolveAmount;
            float4 _EdgeColor;
            float  _EdgeWidth;

            struct Attributes
            {
                float4 positionOS : POSITION;   // 模型空间顶点
                float2 uv         : TEXCOORD0;  // UV 坐标
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct Varyings
            {
                float4 positionHCS : SV_POSITION;  // 裁剪空间位置
                float2 uv          : TEXCOORD0;    // 传递 UV
                UNITY_VERTEX_INPUT_INSTANCE_ID
                UNITY_VERTEX_OUTPUT_STEREO
            };

            // =========================================================
            // 顶点着色器:标准变换,无特殊操作
            // =========================================================
            Varyings vert(Attributes v)
            {
                Varyings o;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_TRANSFER_INSTANCE_ID(v, o);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);

                o.positionHCS = TransformObjectToHClip(v.positionOS.xyz);
                o.uv = TRANSFORM_TEX(v.uv, _BaseMap);
                return o;
            }

            // =========================================================
            // 片元着色器:核心溶解逻辑
            // =========================================================
            half4 frag(Varyings i) : SV_Target
            {
                UNITY_SETUP_INSTANCE_ID(i);

                // 1) 采样噪声纹理(用缩放后的 UV)
                float2 noiseUV = i.uv * _NoiseScale;
                float noise = SAMPLE_TEXTURE2D(_NoiseTex, sampler_NoiseTex, noiseUV).r;

                // 2) clip:噪声值低于溶解阈值的像素直接丢弃
                //    noise - _DissolveAmount < 0 → 像素被裁剪
                clip(noise - _DissolveAmount);

                // 3) 采样主贴图
                half4 baseCol = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, i.uv);
                baseCol *= (half4)_BaseColor;

                // 3.5) 裁掉原始贴图中透明的像素(sprite 透明区域 alpha ≈ 0)
                clip(baseCol.a - 0.01);

                // 4) 计算边缘发光:噪声值刚好在阈值附近的窄带
                //    diff = 当前噪声值距离裁剪线的距离
                float diff = noise - _DissolveAmount;

                //    edgeFactor:0 = 正好在裁剪线上(最亮),1 = 远离裁剪线(无发光)
                float edgeFactor = saturate(diff / _EdgeWidth);

                //    在边缘带内,用 lerp 混合边缘发光色和原始颜色
                half3 finalColor = lerp(_EdgeColor.rgb, baseCol.rgb, edgeFactor);

                return half4(finalColor, baseCol.a);
            }
            ENDHLSL
        }
    }
}

4. 使用方法

  1. 在 Unity 项目的 Assets/Shaders/ 下新建文件 Dissolve_URP.shader,粘贴上方完整代码。
  2. 新建材质(Create → Material),Shader 选择 Custom/Dissolve_URP
  3. 准备贴图:
    • Base Map:物体的主贴图(漫反射纹理)
    • Noise Texture:一张灰度噪声图(推荐 256×256 或 512×512,Perlin noise 效果最自然)
  4. 将材质赋给场景中的物体(Cube、角色模型等均可)。
  5. 在 Inspector 中调节 Dissolve Amount 滑条(0 → 1),观察溶解过程。
  6. 调节 Edge Color(建议用 HDR 颜色如 (3, 0.5, 0) 橙色火焰感)和 Edge Width 控制发光带表现。

用脚本驱动溶解动画

csharp 复制代码
using UnityEngine;

public class DissolveController : MonoBehaviour
{
    [SerializeField] private Material dissolveMaterial;
    [SerializeField] private float duration = 2.0f;

    private float _progress = 0f;
    private bool _dissolving = false;

    // 外部调用:开始溶解
    public void StartDissolve()
    {
        _progress = 0f;
        _dissolving = true;
    }

    void Update()
    {
        if (!_dissolving) return;

        _progress += Time.deltaTime / duration;
        _progress = Mathf.Clamp01(_progress);
        dissolveMaterial.SetFloat("_DissolveAmount", _progress);

        if (_progress >= 1f)
        {
            _dissolving = false;
            // 溶解完成后可以隐藏物体或销毁
            // gameObject.SetActive(false);
        }
    }
}

5. 参数说明

参数 类型 范围/默认值 说明
_BaseMap 2D white 物体主贴图
_BaseColor Color (1,1,1,1) 主颜色叠乘
_NoiseTex 2D white 噪声纹理(灰度),决定溶解形状
_NoiseScale Float 1.0 噪声 UV 缩放,越大碎片越细
_DissolveAmount Range(0,1) 0.0 溶解进度:0=完整,1=完全消失
_EdgeColor Color [HDR] (3,0.5,0,1) 边缘发光颜色,建议 HDR 值
_EdgeWidth Range(0.01,0.2) 0.05 边缘发光带宽度

6. 变体与扩展

6.1 方向溶解(从下往上)

不用噪声纹理,改用世界空间 Y 坐标做阈值,实现从脚到头的溶解:

hlsl 复制代码
// 在 Varyings 中增加世界坐标
float3 positionWS : TEXCOORD1;

// vert 中赋值
o.positionWS = TransformObjectToWorld(v.positionOS.xyz);

// frag 中替换噪声采样
float dissolveValue = (i.positionWS.y - _YMin) / (_YMax - _YMin);
clip(dissolveValue - _DissolveAmount);

可以和噪声混合使用:float finalValue = dissolveValue * 0.7 + noise * 0.3;,方向感 + 不规则边缘兼得。

6.2 双色边缘(内焰 + 外焰)

模拟火焰:外圈橙色、内圈白热:

hlsl 复制代码
float edgeFactor = saturate(diff / _EdgeWidth);
// 内外两层颜色
half3 innerColor = half3(1, 1, 0.8);   // 白热
half3 outerColor = _EdgeColor.rgb;       // 橙色
half3 edgeBlend = lerp(innerColor, outerColor, edgeFactor);
half3 finalColor = lerp(edgeBlend, baseCol.rgb, step(0.99, edgeFactor));

6.3 溶解出现(反向)

只需要把 clip 条件反过来:

hlsl 复制代码
clip(_DissolveAmount - noise);  // Amount 从 0→1 时物体逐渐出现
float diff = _DissolveAmount - noise;

7. 常见问题

Q: 物体整体突然消失,没有渐进溶解效果?

A: 检查噪声纹理是否是纯白或纯黑。纯色意味着所有像素的噪声值相同,会在同一个阈值同时被 clip。换一张有灰度分布的 Perlin noise 纹理。

Q: 边缘发光颜色看起来很暗,不像火焰?

A: _EdgeColor 需要用 HDR 值(RGB 分量 > 1),比如 (3, 0.5, 0)。同时需要开启 URP 的 Bloom 后处理,否则 HDR 颜色显示不出过曝效果。

Q: 溶解进度调到 1 了但还有残留像素?

A: 噪声纹理中可能存在纯白像素(值 = 1.0),clip(1.0 - 1.0) = clip(0) 刚好不会被裁剪。解决办法:把 clip 改为 clip(noise - _DissolveAmount - 0.001),或者确保噪声纹理最大值 < 1.0。

Q: 模型背面是黑色的?

A: 确认 Cull Off 已生效。如果需要背面也有正确光照,这个 Unlit 版本不含光照计算------背面显示的是纹理本身颜色。如需光照,需要结合 Lit Shader 或双 Pass 方案。

Q: 噪声纹理的 Import Settings 有讲究吗?

A: 推荐设置:Wrap Mode = Repeat (如果模型 UV 超出 0-1 范围)、Filter Mode = Bilinear 、关闭 sRGB(勾选 Linear,因为噪声值是数据不是颜色)。


8. 性能建议

  • 噪声图尺寸:128×128 或 256×256 足够,溶解效果不需要高精度噪声。512 以上浪费。
  • AlphaTest 队列 :本 Shader 用的是 clip 而非透明混合,不需要排序,性能优于 Transparent 队列。
  • Shader 变体控制 :如果项目中同时需要"有边缘发光"和"无边缘发光"两种模式,用 [Toggle] + shader_feature_local 做编译期开关,避免运行时 if
  • 远距离关闭:远处物体溶解效果几乎看不到细节,可以用 LOD 或脚本在远距离直接隐藏物体,省掉 clip 的 GPU 开销。
  • 合批注意:clip 会打断 Early-Z 优化(GPU 无法提前跳过被遮挡的像素),大量溶解物体同时出现时注意 Overdraw。
相关推荐
mxwin3 小时前
Unity URP SRP Batcher 完全指南 URP/HDRP 下的核心批处理机制,大幅降低 CPU 开销
unity·游戏引擎·shader·单一职责原则
小清兔3 小时前
unity中的音频相关_笔记
笔记·unity·音视频
RPGMZ3 小时前
RPGMZ游戏引擎 宠物战斗游戏基础功能实现
javascript·游戏·游戏引擎·宠物·rpgmz·rpgmakermz·宠物战斗系统
The森17 小时前
cocos2d-x棋牌项目-模块2:GameView、Node 与 zOrder
游戏引擎·cocos2d
mxwin19 小时前
Unity Shader 渲染队列 (Render Queue):控制 Geometry、Transparent、Overlay 等队列确保半透明物体渲染正确
unity·游戏引擎
mxwin19 小时前
Unity Shader Alpha Test 与 Alpha Blend:透明度测试与混合的实现及排序问题
unity·游戏引擎
小贺儿开发20 小时前
Unity3D 拼图互动游戏
游戏·unity·人机交互·2d·拼图·互动
mxwin20 小时前
Unity Mask 贴图:用一张纹理的 RGBA 通道分别控制 PBR 材质参数
unity·材质·贴图
FairGuard手游加固21 小时前
FairGuard支持HybridCLR热更DLL加密
游戏·unity·游戏引擎