知識0からのUnityShader勉強

知識0からのUnityShader勉強

UnityのShaderをメインとして、0から学んでいくブログです。

【UnityShader】デプスバッファを可視化する #26

前回の成果

soramamenatan.hatenablog.com

今回やること

デプスバッファを可視化しようと思います。

light11.hatenadiary.com

デプスバッファ(深度バッファ・Zバッファ)とは、カメラからオブジェクトまでの距離を格納したバッファとなります。

下記の記事で解説したのですが、可視化までは行っていなかったので今回やっていきます。

soramamenatan.hatenablog.com

事前準備

Scene上にオブジェクトを複数個配置し、Z座標をズラしてください。
また今回はmaterialをオブジェクトにアタッチしないでください
そして、PostEffect.csをCameraにアタッチして、_shaderには今回製作したものをアタッチしてください。

f:id:soramamenatan:20191014170601p:plain

ソースコード

Shaderのコード

Shader "Unlit/depthEffect" {
    SubShader {
        Tags { "RenderType" = "Opaque"}

        LOD 200

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

            sampler2D _CameraDepthTexture;
            float4 _CameraDepthTexture_ST;

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

            struct v2f {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

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

            fixed4 frag(v2f i) : COLOR{
                return UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture, i.uv));
            }
            ENDCG
        }
    }
}

fragで何を返しているのかがいまいちわかりません。

Scriptのコード

using UnityEngine;

[ExecuteInEditMode, RequireComponent(typeof(Renderer))]

public class PostEffect : MonoBehaviour {
    [SerializeField]
    private Shader _shader;
    private Material _material;

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

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

こちらもいまいちよくわかりません。
ひとつずつ紐解いていきます。

_CameraDepthTexture

これはデプステクスチャを取得できる変数です。

デプステクスチャ

これは各ピクセルが高い精度の0~1のデプス値をもっているテクスチャです。

docs.unity3d.com

TRANSFORM_TEX

soramamenatan.hatenablog.com

この記事でも説明した通り、テクスチャのスケールとオフセットを適応するものです。
また、float4やhalf4のテスクチャ名_STが必要になります。
今回の場合は

float4 _CameraDepthTexture_ST;

これです。

UNITY_SAMPLE_DEPTH

非線形深度データを線形スケールに変換するものです。
Unityの公式にもリファレンスがないのでおまじないだと思って大丈夫です。
詳しくはこちらのサイト様に乗っています。

hiyoko1986.web.fc2.com

DepthTextureMode

これがないとデプステクスチャが生成されず、_CameraDepthTextureに値が代入されないので気をつけてください。

変数名 用途
None デプステクスチャを生成しない(デフォルト)
Depth デプステクスチャを生成する
DepthNormals 深度と法線を組み合わせたテクスチャを生成する
MotionVectors モーションベクターレンダリングするかを指定する

モーションベクターとは、オブジェクトが前フレームからどれだけ移動したかをベクトルで表したものです。
今回のデプスバッファとは関係ないので詳しくはこちらのサイト様を参考にしてください。

light11.hatenadiary.com

ORしている理由

ORとはビット演算のことです。
DepthTextureModeは複数の状態を持てるので、ORをしています。

// |= がOR
GetComponent<Camera>().depthTextureMode |= DepthTextureMode.Depth;

ビット演算について触れると長くなってしまうのでこちらのサイト様を参考にしてください。

qiita.com

OnRenderImage,Graphics.Blit

この関数はカメラによるレンダリングが完了した時に呼ばれるものです。

void OnRenderImage(RenderTexture source, RenderTexture dest)

sourceには、入力となる画像が入っています。
destには出力先の画像が渡されます。

sourceのtextureからMeshを生成して、destへ描画すれば良いのですがそれを自動でやってくれる関数がBlitになります。

Graphics.Blit(source, dest, _material);

Blitを用いることによって、sourceにmaterialをアタッチしたものをdestに渡すことができます。

ちなみに第2引数をnullにすることで画面をレンダリング対象にすることもできます。

light11.hatenadiary.com

結果

かなりわかりにくいですが、各オブジェクトのZ座標によって色が変わっています。

f:id:soramamenatan:20191023104718p:plain

ソースコードにコメントを付与

Shaderのコード

Shader "Unlit/depthEffect" {
    SubShader {
        // 一般的なShaderを使用
        Tags { "RenderType" = "Opaque"}
        // しきい値
        LOD 200

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

            // デプステクスチャを取得
            sampler2D _CameraDepthTexture;
            // TRANSFORM_TEXを使用するのに必要
            float4 _CameraDepthTexture_ST;

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

            struct v2f {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            v2f vert(appdata v) {
                v2f o;
                // クリップ座標に変換
                o.vertex = UnityObjectToClipPos(v.vertex);
                // テクスチャのスケールとオフセットを適応
                o.uv = TRANSFORM_TEX(v.uv, _CameraDepthTexture);
                return o;
            }

            fixed4 frag(v2f i) : COLOR{
                // 非線形深度データを線形スケールに変換
                return UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture, i.uv));
            }
            ENDCG
        }
    }
}

Scriptのコード

using UnityEngine;

public class PostEffect : MonoBehaviour {
    [SerializeField]
    private Shader _shader;
    private Material _material;

    void Start() {
        // デプステクスチャの生成
        GetComponent<Camera>().depthTextureMode |= DepthTextureMode.Depth;
        _material = new Material(_shader);
    }

    // カメラによるレンダリング終了時に呼ばれる
    void OnRenderImage(RenderTexture source, RenderTexture dest) {
        // sourceのtextureを元に、_marerialを使用してdestに書き出し
        Graphics.Blit(source, dest, _material);
    }
}

今回はここまでです。 ご視聴ありがとうございました。