おもちゃラボ

Unityで遊びを作ってます

【Unityシェーダ入門】デプスバッファの内容を表示する

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

f:id:nn_hokuson:20180208191631j:plain:w600

このデプスバッファは被写界深度のエフェクトや影の計算など、さまざまな場面で使われています。Unityを使っていると影やエフェクトなどは自動的に計算してくれるので、あまり馴染みがないかもしれません・・・

ただ、エフェクトを作るときに必要になることが時々あります。そこで、ここではデプスバッファ(Zバッファ)の内容を表示する方法を書いていきます。

デプスバッファの値を表示する流れ

上にも書いたとおり、デプスバッファとはカメラからオブジェクトまでの距離を格納したバッファです。この距離をZ値といいUnityの場合0〜1で取得できます。カメラからの距離が近ければ0、遠くなるに従って値が大きくなっていきます。つまり、近くにあるものほど黒く表示されるのですね。

f:id:nn_hokuson:20180208193944j:plain

デプスバッファの値はシェーダの_CameraDepthTexture変数で取得できます。そこで、この値を表示するまでの流れは次のようになります。

  1. Z値を取得して表示するシェーダを作る
  2. ポストエフェクト用のC#スクリプトを作る
  3. C#スクリプトにdepthシェーダを指定する

それでは1ステップずつ見ていきましょう。

Z値を取得して表示するシェーダを作る

まずはデプスバッファから値(Z値)を取得し表示するシェーダを作成しましょう。プロジェクトウィンドウで右クリックし、Create→Shader→Unlit Shaderを選択して、depth.shaderという名前で保存してください。また、作成したdepth.shaderを右クリックしてCreate→MaterialでUnlit_depth.matというマテリアルを作成しておきます。

f:id:nn_hokuson:20180208192022p:plain:w200

続いて今作成したdepth.shaderを開いて次のプログラムを入力してください。

Shader "depth"
{
        SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

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

            sampler2D _CameraDepthTexture;

            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 = v.uv;
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                fixed depth = UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture,i.uv));
                return fixed4(depth,depth,depth,1);
            }
            ENDCG
        }
    }
}

通常の頂点・フラグメントシェーダを見慣れている方にとっては非常にシンプルな内容になっていると思います。

特徴としては、sampler2D _CameraDepthTexture;としてデプスバッファのテクスチャを宣言し、フラグメントシェーダでtex2D関数を使ってデプスバッファからZ値を取り出しています。最後にデプス値をRGBAの色に変換して出力しているだけです。

ポストエフェクト用のC#スクリプトを作る

デプスバッファの内容はポストエフェクトとして表示するのでした。そこで、ポストエフェクト用のスクリプトを作りましょう。

プロジェクトウィンドウで右クリックし、Create→Script→C# Scriptを選択して、DispDepth.csというファイルを作成してください。

作成したC#スクリプトに次のプログラムを入力します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

public class DispDepth : MonoBehaviour {
    public Material mat;
    void Start () 
    {
        GetComponent<Camera>().depthTextureMode |= DepthTextureMode.Depth;
    }

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

OnRenderImageの中でGraphics.Blitメソッドを使ってポストエフェクトをかける方法は定石です。ポストエフェクトの基本的な方法は次の記事を参考にしてください。

nn-hokuson.hatenablog.com

簡単に説明すると、OnRenderImage の第一引数であるsourceにはレンダリング後の画像が入っていいます。このsource画像に対してmatで指定したシェーダの処理をして最終出力結果をdestに出力するイメージです。

f:id:nn_hokuson:20180208194757j:plain:w650

またStartメソッドの中で、カメラがデプステクスチャを生成するように次の一行を追加しています。これを書かないと_CameraDepthTextureでデプス値を取得することが出来ません。

_camera.depthTextureMode |= DepthTextureMode.Depth;

C#スクリプトにdepthシェーダを指定する

DispDepth.csのスクリプトが入力できたら、これをヒエラルキービューのMain Cameraにドラッグ&ドロップしてアタッチしましょう。

f:id:nn_hokuson:20180208192639j:plain

最後にスクリプト中のMat変数にdepth.matを指定します。Main CameraにアタッチしたDepthスクリプトのMat欄にdepth.matをドラッグ&ドロップしてください。

f:id:nn_hokuson:20180208192923j:plain

実行すると次のようにゲーム画面にデプスバッファの値が表示されます。

f:id:nn_hokuson:20180208192958p:plain:w400

デプスバッファを使った応用例は西川善司さんの「ゲーム制作者になるための3Dグラフィックス技術」にいくつか例が載っています。合わせて参考にしてみてください。

[asin:4844333542:detail]
[asin:4844329510:detail]