【Unity】【シェーダ】深度値を使ったソフトパーティクルのシェーダを書く

深度値を使ったソフトパーティクルのシェーダを書いてみました。

Unity2019.2.10

ソフトパーティクル?

ソフトパーティクルとは、パーティクルが他の物体に近づいたときに、
他の物体に近い部分を半透明にフェードさせる仕組みです。

これにより、地面や壁に近いパーティクルの不自然さを解消できます。

f:id:halya_11:20191125131305p:plain
パーティクルのスタンダードシェーダー - Unity マニュアル より

本記事ではこのソフトパーティクルを実装する方法をまとめます。

シェーダ

それでは早速シェーダを書いてみます。

Shader "SoftParticle"
{
    SubShader
    {
        Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" }
        Blend SrcAlpha OneMinusSrcAlpha
        ColorMask RGB
        Cull Back
        Lighting Off
        ZWrite Off

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float4 projPos : TEXCOORD0;
                half depth : TEXCOORD1;
            };

            sampler2D _CameraDepthTexture;
            #define INV_INTENSITY 1.0

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.projPos = ComputeScreenPos(o.vertex);
                COMPUTE_EYEDEPTH(o.depth);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                half bgDepth = LinearEyeDepth(tex2Dproj(_CameraDepthTexture, i.projPos).r);

                float alpha = saturate((bgDepth - i.depth) * INV_INTENSITY);
                return fixed4(1, 1, 1, alpha);
            }
            ENDCG
        }
    }
}

やっていることはシンプルで、_CameraDepthTextureに書かれた深度値と
今描画しようとしている部分の深度値を比較して、近かったら半透明にしています。

_CameraDepthTextureから深度値を取る方法は以下の記事にまとめていますので、必要に応じて参照してください。

light11.hatenadiary.com

また、描画地点の深度値を取得する方法は以下の記事にまとめています。
こちらも必要に応じて参照してください。

light11.hatenadiary.com

結果

まず、プラットフォームによっては深度テクスチャが生成されていない場合があるので、
CameraのdepthTextureModeを下記のように変更しておきます。

using UnityEngine;

public class DepthTexture : MonoBehaviour {


    void Start () {
        GetComponent<Camera>().depthTextureMode |= DepthTextureMode.Depth;
    }
}

深度テクスチャを生成した上で前節のシェーダをパーティクルに適用すると以下のような結果となります。

f:id:halya_11:20191125005037g:plain

正常にソフトパーティクルが描画されていることが確認できました。

関連

light11.hatenadiary.com

light11.hatenadiary.com