【Unity】【シェーダ】ビュー空間の法線を_CameraDepthNormalsTextureから取得する

Unityのシェーダでビュー空間の法線を取得する方法をまとめました。

Unity2019.2.10

_CameraDepthNormalsTextureを生成する

Unityのシェーダではビュー空間の法線は_CameraDepthNormalsTextureから取得できます。
このテクスチャを生成するためには、まず以下のようにCameraのdepthTextureModeを変更しておきます。

using UnityEngine;

public class DepthNormalTexture : MonoBehaviour {

    [SerializeField]
    private Shader _shader;
    private Material _material;

    void Start () {
        // _CameraDepthNormalsTextureを生成するようにする設定
        GetComponent<Camera>().depthTextureMode |= DepthTextureMode.DepthNormals;

        _material = new Material(_shader);
    }

    private void OnRenderImage(RenderTexture source, RenderTexture dest)
    {
        Graphics.Blit(source, dest, _material);
    }
}

このテクスチャにはビュー空間の法線と深度値が独自のフォーマットで格納されています。

なお上記のソースコードではこのついでに次節のシェーダをポストエフェクトとして適用できるようにしています。
ポストエフェクトの掛け方に関しては下の記事にまとめていますので必要に応じて参照してください。

light11.hatenadiary.com

シェーダで法線を取得する

次にこのテクスチャから、シェーダを使って法線を取得します。
まずシェーダ全文です。

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

        Tags{ "RenderType" = "Opaque" }

        Pass
        {
            HLSLPROGRAM
            #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 _CameraDepthNormalsTexture;

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                // _CameraDepthNormalsTextureからサンプリング
                float4 cdn = tex2D(_CameraDepthNormalsTexture, i.uv);
                // サンプリングした値をDecodeViewNormalStereo()で変換
                float3 n = DecodeViewNormalStereo(cdn) * float3(1.0, 1.0, -1.0);

                return float4(n, 1.0);
            }
            ENDHLSL
        }
    }
}

コメントの通り、_CameraDepthNormalsTextureから法線を取得するにはまずこのテクスチャから値をサンプリングし、
サンプリングした値をDecodeViewNormalStereo()関数で変換します。
またこのままだとZ軸の手前方向が正の値となっているためZ値を反転させています。

レンダリング結果

このシェーダを使ってレンダリングすると以下のような結果が得られます。

f:id:halya_11:20191221123100p:plain

正常にビュー空間の法線が得られていそうです。

関連

light11.hatenadiary.com

light11.hatenadiary.com

参考

github.com

docs.unity3d.com