【Unity】【シェーダ】カメラからの深度情報を用いてアウトラインを描画する

f:id:halya_11:20180508215236p:plain

デプステクスチャを用いてアウトラインを描画する方法です。

考え方

デプステクスチャの取得の仕方は下の記事と同様です。

light11.hatenadiary.com

デプスからアウトラインを取る方法は下の記事を参考にさせていただいています。

qiita.com

要は近隣のピクセルとの深度値が大幅に違えばそこをアウトラインとみなすという考え方です。

実装

まずはシェーダです。
ポストエフェクトで使うので、そのためのシェーダとして書きます。

Shader "DepthOutline"
{
    SubShader
    {
        Cull Off
        ZTest Always
        ZWrite Off

        Tags { "RenderType"="Opaque" }

        Pass
        {
            CGPROGRAM
           #pragma vertex vert
           #pragma fragment frag
            
           #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };
            
            sampler2D _CameraDepthTexture;
            float4 _CameraDepthTexture_ST;
            float4 _CameraDepthTexture_TexelSize;
            float _OutlineThreshold;
            float4 _OutlineColor;
            float _OutlineThick;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _CameraDepthTexture);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                float diffX = _CameraDepthTexture_TexelSize.x * _OutlineThick;
                float diffY = _CameraDepthTexture_TexelSize.y * _OutlineThick;
                float col00 = Linear01Depth(tex2D(_CameraDepthTexture, i.uv + half2(-diffX, -diffY)).r);
                float col10 = Linear01Depth(tex2D(_CameraDepthTexture, i.uv + half2(0, -diffY)).r);
                float col01 = Linear01Depth(tex2D(_CameraDepthTexture, i.uv + half2(-diffX, 0)).r);
                float col11 = Linear01Depth(tex2D(_CameraDepthTexture, i.uv + half2(0, 0)).r);
                float outlineValue = (col00 - col11) * (col00 - col11) + (col10 - col01) * (col10 - col01);

                clip(outlineValue - _OutlineThreshold);

                return _OutlineColor;
            }
            ENDCG
        }
    }
}

次にスクリプトです。

using UnityEngine;
using UnityEngine.Rendering;

[RequireComponent(typeof(Camera))]
public class DepthTexture : MonoBehaviour
{

    [SerializeField]
    private Shader _shader;
    [SerializeField]
    private float _outlineThreshold = 0.01f;
    [SerializeField]
    private Color _outlineColor = Color.white;
    [SerializeField]
    private float _outlineThick = 1.0f;

    private Material _material;

    private void Awake()
    {
        Initialize();
    }

    private void Update()
    {
#if UNITY_EDITOR
        SetMaterialProperties();
#endif
    }

    private void Initialize()
    {
        var camera = GetComponent<Camera>();
        camera.depthTextureMode |= DepthTextureMode.Depth;

        if (camera.allowMSAA || camera.allowHDR)
        {
            return;
        }

        _material = new Material(_shader);
        SetMaterialProperties();

        var commandBuffer = new CommandBuffer();
        int tempTextureIdentifier = Shader.PropertyToID("_PostEffectTempTexture");
        commandBuffer.GetTemporaryRT(tempTextureIdentifier, -1, -1);
        commandBuffer.Blit(BuiltinRenderTextureType.CurrentActive, tempTextureIdentifier);
        commandBuffer.Blit(tempTextureIdentifier, BuiltinRenderTextureType.CurrentActive, _material);
        commandBuffer.ReleaseTemporaryRT(tempTextureIdentifier);
        camera.AddCommandBuffer(CameraEvent.AfterEverything, commandBuffer);
    }

    private void SetMaterialProperties()
    {
        if (_material != null)
        {
            _material.SetFloat("_OutlineThreshold", _outlineThreshold);
            _material.SetColor("_OutlineColor", _outlineColor);
            _material.SetFloat("_OutlineThick", _outlineThick);
        }
    }
}

スクリプトはデプステクスチャ取得の記事にあるものを少し書き換えたものになります。

light11.hatenadiary.com

結果

スクリプトをカメラにアタッチして、シェーダの参照を持たせます。
次のようなシーンを用意します。

f:id:halya_11:20180508215159p:plain

再生をすると、

f:id:halya_11:20180508215236p:plain

アウトラインが描画されました。
スクリプトのパラメータを編集すれば各Cubeが重なっている部分にもアウトラインを引けます。

関連

light11.hatenadiary.com

light11.hatenadiary.com