【Unity】【シェーダ】描画する地点の深度を取得する

Unityのシェーダで描画する地点の深度を取得する方法です。

Unity2019.2.10

深度を取得する

深度を取得するには、以下のように頂点シェーダでCOMPUTE_EYEDEPTHマクロを使用します。

Shader "Depth"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

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

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                // COMPUTE_EYEDEPTHマクロに変数を渡す
                COMPUTE_EYEDEPTH(o.depth.x);
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                return i.depth.x;
            }
            ENDCG
        }
    }
}

COMPUTE_EYEDEPTHの中身

COMPUTE_EYEDEPTHはUnityCG.cgincに定義されています。
関連するソースコードだけを抜き出すと以下のようになります。

#define COMPUTE_EYEDEPTH(o) o = -UnityObjectToViewPos( v.vertex ).z

inline float3 UnityObjectToViewPos( in float3 pos )
{
    return mul(UNITY_MATRIX_V, mul(unity_ObjectToWorld, float4(pos, 1.0))).xyz;
}
inline float3 UnityObjectToViewPos(float4 pos) // overload for float4; avoids "implicit truncation" warning for existing shaders
{
    return UnityObjectToViewPos(pos.xyz);
}

シンプルにビュー空間におけるZ座標を返しているだけです。
座標変換とその用語については以下の記事を参照してください。

light11.hatenadiary.com

0~1の値に変換する

次に深度値を、カメラの位置を0、カメラのFarPlaneを1とする値に変換してみます。

UnityではFarPlaneの位置はProjectionParams.zで取得できます。
また便利なことに、1/FarPlaneの値が
ProjectionParams.wに格納されています。

docs.unity3d.com

そのため、深度値に_ProjectionParams.wを掛けるだけで0~1の値に変換できます。

COMPUTE_EYEDEPTH(o.depth);
o.depth *= _ProjectionParams.w;

関連

light11.hatenadiary.com

light11.hatenadiary.com

参考

docs.unity3d.com

docs.unity3d.com