[Unity] 寿命に応じてディゾルブするParticleSystemのシェーダー

##はじめに
出来たもの
![ageDissolveParticleSample.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440297/afa5d347-c33e-f1c9-4001-417eb1c43f63.gif)

##概要
Partcleの寿命の割合によってDissolveエフェクトをかける。
CustomVertexStreamsを使っていい感じにする。

##実装

### ParticleSystem設定
CustomVertexStreamsを有効にしてUV2、AgePercentの順で追加する。
![cvs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440297/47e7cece-4041-c384-99b2-54c29feaef85.png)

### パラメーター
![params.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440297/d6e18c5c-b8ae-170a-281e-60122c10a9dd.png)

###shader

``` hlsl
// Copyright (c) 2021 @meta556
// Released under the MIT license
// https://opensource.org/licenses/mit-license.php
Shader "metaaa/particleAgeDissolve"
{
    Properties
    {
        _MainTex("MainTex", 2D) = "white" {}
        _MainColor("Color", Color) = (1,1,1,1)
        _Mask("DissolveMask", 2D) = "white" {}
        _Width("Width", Range(0,1)) = 0.001
        [HDR] _Color("Line Color", Color) = (1,1,1,1)
        _DissolveStartPer("DissolveStartAgePercent", Range(0,1)) = 0.0
        _DissolveEndPer("DissolveEndAgePercent", Range(0,1)) = 1.0
    }
    SubShader
    {
        Tags { "RenderType" = "Transparent" "Queue"="Transparent" }
            Blend SrcAlpha OneMinusSrcAlpha
            Cull Off

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            struct appdata
            {
                float4 vertex : POSITION;
                float4 uv : TEXCOORD0;
                fixed4 color : COLOR;
                float4 texcoord1 : TEXCOORD1;
            };
            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                fixed4 color : COLOR;
                float agePercent : TEXCOORD1;
            };
            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _Mask;
            fixed4 _Color;
            fixed4 _MainColor;
            fixed _Width;
            float _DissolveStartPer;
            float _DissolveEndPer;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.agePercent = v.texcoord1.x;
                o.color = v.color;
                return o;
            }

            float InverseLerp(float a, float b, float x)
            {
                return (x - a) / (b - a);
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv) * _MainColor * i.color;
                fixed noise = tex2D(_Mask, i.uv).r;
                float dissolve = clamp(i.agePercent, _DissolveStartPer, _DissolveEndPer);
                dissolve = InverseLerp(_DissolveStartPer, _DissolveEndPer, dissolve);
                dissolve = lerp(- _Width, 1 + _Width, dissolve);
                // main
                col.a = step(noise, 1 - dissolve );
                // line
                fixed flg = step(noise, 1 - dissolve + _Width) * step(1 - dissolve  , noise);
                col = (flg >= 1) ? _Color : col;
                return col;
            }
            ENDCG
        }
    }
}

```

### Particleの寿命をシェーダーで受け取る

CVSにAgePercent(TEXCOORD1.x)を設定したのでappdataで受け取ってFragmentShaderに渡します。
AgePercentは0~1の値で、生まれた直後は0で死に際に1になります。

``` hlsl
struct appdata
{
    float4 vertex : POSITION;
    float4 uv : TEXCOORD0;
    fixed4 color : COLOR;
    float4 texcoord1 : TEXCOORD1;
};
struct v2f
{
    float2 uv : TEXCOORD0;
    float4 vertex : SV_POSITION;
    fixed4 color : COLOR;
    float agePercent : TEXCOORD1;
};

v2f vert (appdata v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    o.agePercent = v.texcoord1.x;
    o.color = v.color;
    return o;
}

```

### FragmentShaderでDissolveさせる

DissolveMaskのテクスチャのピクセルのr値と寿命を加工した値を比較してアルファを1か0に設定しています。
Dissolveが始まる寿命と終わる寿命を設定するためにAgePercentをClampしています。

InverseLerp関数はlerp関数の逆で、範囲と値から割合を求めます。
Clampしたものをlerpで使うため、InverseLerpで0~1の範囲にしています。

``` hlsl
float InverseLerp(float a, float b, float x)
{
    return (x - a) / (b - a);
}

fixed4 frag (v2f i) : SV_Target
{
    fixed4 col = tex2D(_MainTex, i.uv) * _MainColor * i.color;
    fixed noise = tex2D(_Mask, i.uv).r;
    float dissolve = clamp(i.agePercent, _DissolveStartPer, _DissolveEndPer);
    dissolve = InverseLerp(_DissolveStartPer, _DissolveEndPer, dissolve);
    dissolve = lerp(- _Width, 1 + _Width, dissolve);
    // main
    col.a = step(noise, 1 - dissolve );
    // line
    fixed flg = step(noise, 1 - dissolve + _Width) * step(1 - dissolve  , noise);
    col = (flg >= 1) ? _Color : col;
    return col;
}

```

##Link
https://booth.pm/ja/items/3109052