《Unity自定义粒子顶点流》教程中,我们学习了如何制作基础的粒子着色器,让它读取和使用从正确配置的粒子系统发送的顶点流,最后我们得到了富有弹性的弹簧粒子效果,该效果不是很有创意,也并不惊艳。

本教程中,我们将在此基础上更进一步,实现下图的效果。
「U3D」伴随Simplex噪声的GPU粒子动画

如果你已经完成了上一个的教程,并拥有《创建3D均匀粒子网格》教程的脚本,那么本教程中特效的制作过程会非常简单。

本教程中我们会对每个粒子的中心传递额外的3D顶点流,然后使用该顶点流决定来自噪声函数的顶点偏移。本文将使用的噪声函数为Simplex噪声,类似柏林噪声。

Part 1:粒子系统设置

首先设置一个基础粒子系统,我们可以用它来测试效果。我们将使用默认粒子纹理,最后的结果类似下图。
「U3D」伴随Simplex噪声的GPU粒子动画

首先,创建一个粒子系统,重置Transform组件,然后按下图设置Main模块。勾选了Prewarm,设置Start Speed为0,并将Start Size设为在0.25和1之间随机取值,然后设置Max Particles为10,000。
「U3D」伴随Simplex噪声的GPU粒子动画

设置Emission模块的Rate over Time为2,000。

将Shape设为Box,Scale设为[50, 0, 50]。

现在我们应该会得到一个反复浮动的平坦粒子层。
「U3D」伴随Simplex噪声的GPU粒子动画

我们需要使生成效果更平滑,现在启用Color over Lifetime模块,按照下图设置梯度,从而浅入淡出粒子。
「U3D」伴随Simplex噪声的GPU粒子动画

在Renderer模块中,启用Custom Vertex Streams并添加Center流。
「U3D」伴随Simplex噪声的GPU粒子动画

这样会造成TEXCOORD1的溢出,我们会在处理着色器时解决该问题,这样就完成了测试粒子系统。
「U3D」伴随Simplex噪声的GPU粒子动画

Part 2:基本着色器

在这一部分,我们将创建基本着色器,用来处理额外的顶点流数据。

该着色器将基于我们在《Unity自定义粒子顶点流》中创建的基础无光粒子着色器

我们需要修改一部分内容,首先是名称。

Shader "Unlit/Simplex Noise Particle Unlit"

将tc0之前为uv,定义为float4,并添加TEXCOORD1为tc1,。这样允许我们通过使用该着色器,传入并保存更多来自粒子系统的自定义顶点流数据。

struct appdata
{
    float4 vertex : POSITION; 
    fixed4 color : COLOR;
    float4 tc0 : TEXCOORD0;
    float4 tc1 : TEXCOORD1;
};
 
struct v2f
{
    float4 tc0 : TEXCOORD0;
    float4 tc1 : TEXCOORD1;
    UNITY_FOG_COORDS(1)
    float4 vertex : SV_POSITION;
    fixed4 color : COLOR;
};

接下来,我们初始化导出的tc0.zw到tc0.zw输入中的内容。

我们指定了z和w,因为xy已经被指定为纹理的UV坐标。我们可以将整个tc1输入转储到tc1输出,因为在顶点程序中,这部分数据不和之前的任何操作共享。

v2f vert (appdata v)
{
    v2f o;
 
    float3 vertexOffset = 0;
 
    v.vertex.xyz += vertexOffset;
    o.vertex = UnityObjectToClipPos(v.vertex);
 
    // 从保存在颜色顶点输入的粒子系统接收数据,并将该数据用于初始化颜色
    o.color = v.color;
    o.tc0.xy = TRANSFORM_TEX(v.tc0, _MainTex);
 
    // 初始化tex coord变量
    o.tc0.zw = v.tc0.zw;
    o.tc1 = v.tc1;
 
    UNITY_TRANSFER_FOG(o,o.vertex);
    return o;
}

我们可以使用该着色器处理上一部分使用的粒子系统,而且不必看到烦人的不匹配流警告。

我们返回Unity编辑器中,创建一个新材质,为其指定着色器,然后设置粒子系统Renderer模块的材质。指定Unity的默认粒子纹理。
「U3D」伴随Simplex噪声的GPU粒子动画

如果一切顺利,我们应该不会看到任何警告,而且粒子系统会按照之前的方法渲染。

下面是完整的基本着色器代码。

Shader "Unlit/Simplex Noise Particle Unlit"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
 
    SubShader
    {
        Tags { "Queue" = "Transparent" "RenderType" = "Opaque" }
        LOD 100
 
        Blend One One // 加法混合
        ZWrite Off // 关闭深度测试
 
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
 
            // 实现模糊效果
            #pragma multi_compile_fog
 
            #include "UnityCG.cginc"
 
            struct appdata
            {
                float4 vertex : POSITION;
                fixed4 color : COLOR;
                float4 tc0 : TEXCOORD0;
                float4 tc1 : TEXCOORD1;
            };
 
            struct v2f
            {
                float4 tc0 : TEXCOORD0;
                float4 tc1 : TEXCOORD1;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
                fixed4 color : COLOR;
            };
 
            sampler2D _MainTex;
            float4 _MainTex_ST;
                
            v2f vert (appdata v)
            {
                v2f o;
                
                float3 vertexOffset = 0;
 
                v.vertex.xyz += vertexOffset;
                o.vertex = UnityObjectToClipPos(v.vertex);
 
                // 从保存在颜色顶点输入的粒子系统接收数据,并将该数据用于初始化颜色。
                o.color = v.color;
                o.tc0.xy = TRANSFORM_TEX(v.tc0, _MainTex);
 
                // 初始化tex coord变量
                o.tc0.zw = v.tc0.zw;
                o.tc1 = v.tc1;
 
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }
 
            fixed4 frag (v2f i) : SV_Target
            {
                // 采样纹理
                fixed4 col = tex2D(_MainTex, i.tc0); 
 
                // 让纹理颜色和粒子系统的顶点颜色输入相乘
                col *= i.color;
                col *= col.a;
 
                // 应用模糊效果
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}