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中 ddx ddy是什么
unity·游戏引擎·shader
郝学胜-神的一滴5 小时前
[简化版 GAMES 101] 计算机图形学 08:三角形光栅化上
c++·unity·游戏引擎·godot·图形渲染·opengl·unreal
nnsix5 小时前
Unity ILRuntime 笔记
unity·游戏引擎
nnsix7 小时前
Unity API 兼容的 .NET Standard 2.1 和 .NET Framework 区别
unity·游戏引擎·.net
mxwin7 小时前
Unity Shader 制作半透明物体 使用多Pass提前写入深度的方式 避免穿模
unity·游戏引擎
nnsix9 小时前
Unity HybridCLR 笔记
笔记·unity·游戏引擎
nnsix10 小时前
Unity Addressables 笔记
unity·游戏引擎
RReality10 小时前
【Unity Shader URP】视差贴图 实战教程
ui·平面·unity·游戏引擎·图形渲染·贴图
小清兔1 天前
Addressable的设置打包流程
笔记·游戏·unity·c#
3D霸霸1 天前
Sourcetree 拉取新工程
数据仓库·unity