Part 2:透明度,深度测试及渲染队列

继续下一步前,我们先解决之前出现的问题,从透明度开始。为了从输入纹理获取合适的Alpha值,只需添加混合模式即可。

我们可以选择常用命令,例如:加法(One One命令),Alpha混合(SrcAlpha OneMinusSrcAlpha)和Alpha混合预乘(One OneMinusSrcAlpha)。对黑色背景上纹理(例如默认粒子纹理)的最好选择是加法和预乘。

我们选择加法,因为它最直接,在黑暗场景中效果最好,并且和HDR 和 阈值泛光的结合效果很好,因为像素会通过加法互相叠加。

回到Unity编辑器,我们增大了粒子大小,以突出目前存在的一个显示问题。虽然粒子通过加法混合清楚地渲染了出来,渲染效果就像液滴或熔岩灯,但它们没有半透明效果,而且公告牌四边形的轮廓很清楚。
「U3D」自定义粒子顶点流

为了解决该问题,我们需要禁用深度测试。

Tags { "RenderType"="Opaque" }
LOD 100
 
Blend One One // 加法混合
ZWrite Off //关闭深度测试

如下图所示,处理方法很有效。
「U3D」自定义粒子顶点流

下图是没有修改粒子大小时,应该呈现的效果。
「U3D」自定义粒子顶点流

虽然可能效果不太明显,但我们的着色器仍然会在场景中对其它透明对象进行排序,例如精灵。解决这个问题很简单,只需将材质上的渲染队列更改为透明层即可。
「U3D」自定义粒子顶点流

我们可以添加Queue = Transparent标记从着色器自动执行此操作。

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

Part 3:顶点颜色和着色

下面我们来解决顶点流不匹配着色器输入的警告。
「U3D」自定义粒子顶点流

最简单的方法是用过在编辑器选择Color流,单击“+”旁边的“-”按钮,移除Color流,这样问题就解决了。
「U3D」自定义粒子顶点流

但是本文想说明,我们应该了解真正解决该错误,而不是简单的删除Color流。

熟悉Unity粒子系统的基础知识的开发者,应该知道我们可以定义所有粒子初始化时的起始颜色,生命周期颜色和随速度变化的颜色。在当前着色器中,这些设置没有任何效果,因为数据是通过COLOR顶点输入传递的。

这些警告实际在告诉我们,着色器中没有地方接收该数据,即使粒子系统已设置为发送数据。因此当我们移除它时,警告就消失了。如果在着色器中接收Color流,但并不发送该数据,我们也会得到相同的警告。

现在我们更新着色器,以便从粒子系统接收Color输入流。

struct appdata
{
    float4 vertex : POSITION;
    fixed4 color : COLOR;
    float3 uv : TEXCOORD0;
};
 
struct v2f
{
    float3 uv : TEXCOORD0;
    UNITY_FOG_COORDS(1)
    float4 vertex : SV_POSITION;
    fixed4 color : COLOR;
};

然后我们将v2f struct和输入初始化到顶点部分。

v2f vert (appdata v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
 
    // 从保存在颜色顶点输入的粒子系统接收数据,并将该数据用于初始化颜色。
    o.color = v.color; 
    o.uv.xy = TRANSFORM_TEX(v.uv, _MainTex);
   
    // 初始化uv.z(它保存了粒子寿命百分比)
    o.uv.z = v.uv.z;
 
    UNITY_TRANSFER_FOG(o,o.vertex);
    return o;
}

返回到Unity编辑器的粒子系统设置,警告已经消失了。
「U3D」自定义粒子顶点流

现在将Main模块的Start Color设为蓝色。
「U3D」自定义粒子顶点流

可能你已经明白了,但是这样做不会改变什么。因为我们接收了数据,但还没在片段部分处理数据。

现在修改设置,使粒子系统组件的颜色对纹理颜色进行着色,然后再插补为红色。

fixed4 frag (v2f i) : SV_Target
{
    // 采样纹理
    fixed4 col = tex2D(_MainTex, i.uv);
 
    //让纹理颜色和粒子系统的顶点颜色输入相乘
    col *= i.color;
 
    float particleAgePercent = i.uv.z;
    float4 colourRed = float4(1, 0, 0, 1);
 
    // 根据粒子寿命百分比从纹理颜色插值为红色
    col = lerp(col, colourRed * col.a, particleAgePercent);
 
    // 应用模糊效果
    UNITY_APPLY_FOG(i.fogCoord, col);
    return col;
}

现在Unity编辑器中,我们可以看见下图画面,和预期一样,粒子首先会被着色为蓝色。
「U3D」自定义粒子顶点流

我们已经成功编写好了粒子着色器,它可以处理自定义顶点流,下面是完整的着色器代码。

Shader "Unlit/Simple 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;
                float3 uv : TEXCOORD0;
            };
 
            struct v2f
            {
                float3 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
                fixed4 color : COLOR;
            };
 
            sampler2D _MainTex;
            float4 _MainTex_ST;
 
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
 
                // 从保存在颜色顶点输入的粒子系统接收数据,并将该数据用于初始化颜色
                o.color = v.color;
                o.uv.xy = TRANSFORM_TEX(v.uv, _MainTex);
             
                // 初始化当前uv.z(包含粒子的寿命百分比)
                o.uv.z = v.uv.z;
  
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }
 
            fixed4 frag (v2f i) : SV_Target
            {
                // 采样纹理
                fixed4 col = tex2D(_MainTex, i.uv);
 
                // 让纹理颜色和粒子系统的顶点颜色输入相乘
                col *= i.color;
 
                float particleAgePercent = i.uv.z;
                float4 colourRed = float4(1, 0, 0, 1);
 
               //根据粒子寿命百分比,从纹理颜色插值为红色
               col = lerp(col, colourRed * col.a, particleAgePercent);
 
                // 应用模糊效果
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

Next page