Unity URP 软粒子(Soft Particles)完全指南

通过深度纹理采样消除粒子与场景边界处的"纸片感",让粒子效果自然融入游戏世界

什么是软粒子?

软粒子(Soft Particles)是一种让粒子效果在接近不透明物体时自动淡出的技术。在 Unity 的通用渲染管线(URP)中,通过比较粒子自身深度与场景深度,实现边界处的平滑过渡。

为什么需要软粒子?

没有软粒子时,粒子效果会呈现明显的"纸片感"------粒子与场景物体交界处会有一条生硬的边界线。这在烟雾、火焰、爆炸等粒子特效中尤为明显。

问题效果示意

软粒子的优势

  • 视觉真实感:粒子效果自然融入场景,消除"悬浮"感
  • 性能友好:仅需采样一张深度纹理,几乎无额外开销
  • 易于实现:只需几行 Shader 代码即可启用
  • 通用性强:适用于烟雾、火焰、爆炸、水花、魔法特效等各类粒子系统

实现原理

软粒子的核心思想是计算粒子深度场景深度之间的差值,当粒子接近场景物体时(深度差趋近于0),自动降低粒子的不透明度。

过渡系数 = saturate( (场景深度 - 粒子深度) / 过渡距离 )

深度差值示意

公式解析

参数 说明
场景深度 从 _CameraDepthTexture 采样,表示到不透明物体的距离
粒子深度 粒子在深度缓冲中的深度值(线性化后)
过渡距离 软粒子生效的范围(通常 0.5~2.0 单位)
saturate 将结果限制在 [0, 1] 范围内

URP 配置步骤

在 URP 中启用软粒子需要两步:开启相机深度纹理 + 配置 Shader。

Step 1: 开启相机深度纹理

方法 A:修改 URP 渲染器数据(推荐)

在 Project 窗口中找到你的 URP Renderer Data 资产(通常在 Settings 文件夹),勾选 Depth Texture 选项。

💡 提示

开启 Depth Texture 会增加一个深度 Pass,建议在移动端根据需求权衡性能。

Step 2: 相机配置(备选方案)

也可以在相机的 Rendering 选项卡中,将 Depth Texture 设置为 AlwaysOn Demand

Shader 实现

完整的软粒子 Shader 示例,包含逐行注释说明:

完整 Shader 代码

cs 复制代码
// 软粒子 Shader - URP 版本

Shader "Custom/SoftParticle"

{

    Properties

    {

        _MainTex ("Texture", 2D) = "white" {}

        _Softness ("Softness", Range(0.1, 5.0)) = 1.0

        _Color ("Tint Color", Color) = (1, 1, 1, 1)

    }

    }


    SubShader

    {

        Tags { "Queue"="Transparent" "RenderType"="Transparent" }

        Blend SrcAlpha OneMinusSrcAlpha

        ZWrite Off

        Cull Off


        // ----------------------------------------

        // Fragment Shader - 核心软粒子逻辑

        // ----------------------------------------

        Pass

        {

            HLSLPROGRAM

            #pragma vertex vert

            #pragma fragment frag

            #pragma multi_compile _ _

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


            // ----------------------------------------

            // 声明深度纹理

            // _CameraDepthTexture 是 URP 自动提供的深度纹理

            // ----------------------------------------

            TEXTURE2D(_CameraDepthTexture);

            SAMPLER(sampler_CameraDepthTexture);


            CBUFFER_START(UnityPerMaterial)

                float _Softness;

                float4 _Color;

            CBUFFER_END


            struct appdata

            {

                float4 vertex : POSITION;

                float2 uv : TEXCOORD0;

            };


            struct v2f

            {

                float2 uv : TEXCOORD0;

                float4 vertex : SV_POSITION;

                // 存储变换后的深度值(用于后续比较)

                float depth : TEXCOORD1;

            };


            v2f vert(appdata v)

            {

                v2f o;

                o.vertex = TransformObjectToHClip(v.vertex.xyz);

                o.uv = v.uv;

                // 计算线性深度,范围 [Near, Far] -> [0, 1]

                o.depth = -o.vertex.w;

                return o;

            }


            half4 frag(v2f i) : SV_Target

            {

                // ----------------------------------------

                // Step 1: 采样粒子纹理

                // ----------------------------------------

                half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv);

                col *= _Color;


                // ----------------------------------------

                // Step 2: 采样场景深度纹理

                // ----------------------------------------

                float rawDepth = SAMPLE_DEPTH_TEXTURE(

                    _CameraDepthTexture,

                    sampler_CameraDepthTexture,

                    ComputeScreenPos(i.vertex / i.vertex.w).xy

                );


                // ----------------------------------------

                // Step 3: 线性化深度值

                // 深度纹理存储的是非线性深度,需转换

                // ----------------------------------------

                float sceneDepth = LinearEyeDepth(rawDepth, _ZBufferParams.x);


                // ----------------------------------------

                // Step 4: 计算深度差值

                // sceneDepth - i.depth 即为粒子到场景物体的距离

                // ----------------------------------------

                float depthDelta = sceneDepth - i.depth;


                // ----------------------------------------

                // Step 5: 计算软粒子系数

                // saturate 将结果限制在 [0,1] 范围

                // ----------------------------------------
                float softFactor = saturate(depthDelta / (_Softness + 0.0001));


                // ----------------------------------------

                // Step 6: 应用淡出效果

                // ----------------------------------------
                col.a *= softFactor;


                return col;

            }

            ENDHLSL

        }

    }

}

🔧 代码要点

  • 第 41-42 行:必须声明 _CameraDepthTexture 才能采样场景深度
  • 第 86 行:使用 LinearEyeDepth 将非线性深度转换为眼睛空间线性深度
  • 第 93 行:核心公式------深度差值除以过渡距离,得到 [0,1] 的淡出系数
  • 第 98 行:将系数乘以 Alpha,使粒子边缘自然淡出

使用技巧与注意事项

参数调节建议

参数 推荐值 说明
_Softness 0.5 ~ 2.0 值越大,软粒子过渡范围越宽
Render Queue Transparent 确保粒子在不透明物体之后渲染
Sorting Fudge 0.1 ~ 0.5 有助于粒子正确排序

常见问题排查

  • 软粒子不生效? 检查 URP Renderer Data 是否勾选了 Depth Texture
  • 粒子完全消失? 可能是 _Softness 值过大,或粒子被裁剪了
  • 接缝明显? 确保粒子的 Shader 正确处理了深度采样
  • 移动端性能? 深度纹理采样有成本,高端效果可考虑 LOD

总结

软粒子是提升粒子特效真实感的关键技术。通过采样场景深度纹理 并与粒子自身深度做差值,在粒子接近不透明几何体时自动淡出边缘,从而消除"纸片感"。

核心实现只需:开启 Depth Texture声明 _CameraDepthTexture计算深度差并应用 Alpha 三步即可完成。

相关推荐
mxwin2 小时前
Unity Shader 深度偏移Depth Bias / Offset 完全指南
unity·游戏引擎·shader
星河耀银海3 小时前
Unity基础:UI组件详解:Button按钮的点击事件绑定
ui·unity·lucene
RReality4 小时前
【Unity Shader URP】平面反射(Planar Reflection)实战教程
ui·平面·unity·游戏引擎·图形渲染·材质
风酥糖5 小时前
Godot游戏练习01-第30节-教程结束我继续
游戏·游戏引擎·godot
Heikepengmu5 小时前
用Unity打造愤怒的小鸟游戏
游戏·unity·游戏引擎
雪儿waii14 小时前
Unity 中的 Resources 详解
unity·游戏引擎
RReality1 天前
【Unity UGUI】Toggle / ToggleGroup 与 Dropdown
ui·unity·游戏引擎·图形渲染·材质
雪儿waii1 天前
Unity 中的 InvokeRepeating 详解
unity·游戏引擎
mxwin1 天前
Unity Shader 程序化生成:Shader 中的数学宇宙
unity·游戏引擎