【Unity3D】 デプスバッファを利用する

# DepthTextureについて

## Cameraから描画する

* カラーバッファ<br>![DepthTexture00s.jpg](https://qiita-image-store.s3.amazonaws.com/0/39049/9f0011b6-9649-2c05-5ab5-a4ab3acb4676.jpeg)

* デプスバッファ<br>![DepthTexture01s.jpg](https://qiita-image-store.s3.amazonaws.com/0/39049/0aca92f5-d3fd-2d7d-326c-5e640d0da0b9.jpeg)

● 手順
1. Cameraのデプスバッファ描画先にRenderTextureをアタッチする。
2. OnPostRender() で加工したり、画面に出力したりします。

●サンプルコード
```csharp
public class RenderDepthBuffer : MonoBehaviour
{
	private RenderTexture m_colorTex;
	private RenderTexture m_depthTex;
	public Material m_postRenderMat;

	void Start ()
	{
		Camera cam = GetComponent<Camera>();
		cam.depthTextureMode = DepthTextureMode.Depth;

		// カラーバッファ用 RenderTexture
		m_colorTex = new RenderTexture(Screen.width, Screen.height, 0, RenderTextureFormat.ARGB32);
		m_colorTex.Create();

		// デプスバッファ用 RenderTexture
		m_depthTex = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.Depth);
		m_depthTex.Create();

		// cameraにカラーバッファとデプスバッファをセットする
		cam.SetTargetBuffers(m_colorTex.colorBuffer, m_depthTex.depthBuffer);
	}
	
	void OnPostRender()
	{
		// RenderTarget無し：画面に出力される
		Graphics.SetRenderTarget(null);

		// デプスバッファを描画する(m_postRenderMatはテクスチャ画像をそのまま描画するマテリアル)
		Graphics.Blit(m_depthTex, m_postRenderMat);
	}
}
```

## 別のカメラで利用する

カメラAで描画されたデプスバッファを元に、カメラBで別の描画を行う。
とりあえずサンプルコードだけ。

```csharp
{
	// 1つ目のcameraで描画されたカラーバッファとデプスバッファ
	public RenderTexture m_firstColorTex;
	public RenderTexture m_firstDepthTex;

	// 2つ目のcameraの描画先
	private RenderTexture m_renderBuffer;

	public Material m_postRenderMat;
	public Material m_cutOffMat;

	void Start()
	{
		Camera cam = GetComponent<Camera>();

		// 二つ目のcameraの描画バッファ
		m_renderBuffer = new RenderTexture(Screen.width, Screen.height, 0, RenderTextureFormat.ARGB32);
		m_renderBuffer.Create();

		// ・カラーバッファは上記で作成したもの
		// ・デプスバッファは別のcameraで描画されたもの
		// をセットする。
		cam.SetTargetBuffers(m_renderBuffer.colorBuffer, m_firstDepthTex);
	}

	void OnPostRender()
	{
		// 描画先を完全にクリアする
		Graphics.SetRenderTarget(null);
		GL.Clear(true, true, new Color(0, 0, 0, 0));

		// 1つ目のcameraで描画されたカラーバッファを画面に描画
		Graphics.Blit(m_firstColorTex, m_postRenderMat, 0);

		// 2つ目のcameraで描画されたカラーバッファを合成
		// そのまま描画すると全て塗りつぶされてしまうので、適当にカットオフマテリアルで描画
		Graphics.Blit(m_renderBuffer, m_cutOffMat, 0);
	}
}
```

## デプスバッファから輪郭を抽出する

* 最終結果<br>![DepthEdge01s.jpg](https://qiita-image-store.s3.amazonaws.com/0/39049/e4f20878-d366-04bb-ce26-047580409113.jpeg)

* 抽出された輪郭<br>![DepthEdge00s.jpg](https://qiita-image-store.s3.amazonaws.com/0/39049/9c3ea498-b10d-6b6e-42aa-4a461d91ff23.jpeg)

●手順
1. cameraにカラーバッファとデプスバッファのRenderTextureを設定
2. OnPostRender() でカラーバッファを画面に描画。
3. デプスバッファから輪郭を抽出して別のRenderTextureへ描画
4. 輪郭テクスチャを画面に合成

●カメラにアタッチするコンポーネント

```csharp
	private RenderTexture m_colorTex;	// カラーバッファ
	private RenderTexture m_depthTex;	// デプスバッファ
	private RenderTexture m_edgeTex;	// 輪郭描画先のバッファ
	
	public Material m_postRenderMat;	// カラーバッファ描画用
	public Material m_renderEdgeMat;	// 輪郭抽出シェーダー
	public Material m_cutOffMat;		// 輪郭合成用

	void OnPostRender()
	{
		// カラーバッファを転送(m_postRenderMatはテクスチャ画像をそのまま描画するマテリアル)
		Graphics.SetRenderTarget(null);
		GL.Clear(true, true, new Color(0, 0, 0, 0));
		Graphics.Blit(m_colorTex, m_postRenderMat);

		// 輪郭抽出用RenderTargetへ描画
		Graphics.SetRenderTarget(m_edgeTex);
		GL.Clear(true, true, new Color(0, 0, 0, 0));
		Graphics.Blit(m_depthTex, m_edgeTex, m_renderEdgeMat);

		// 輪郭テクスチャを画面に合成
		Graphics.SetRenderTarget(null);
		Graphics.Blit(m_edgeTex, m_cutOffMat);
	}
```

●輪郭抽出シェーダー

```c
Shader "Custom/RenderDepthEdge" {
	Properties {
		_MainTex ("Depth Texture", 2D) = "white" {}
		_Threshold ("Edge Threshold", Range(0.0001, 1)) = 0.01
		_EdgeColor ("Edge Color", Color) = (1,1,1,1)
		_Thick("Thick", Range(0.1, 5)) = 1
	}
	
	SubShader {
		Tags { "RenderType"="Opaque" }
		ZTest Off
		ZWrite Off
		Lighting Off
		AlphaTest Off
		
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"

			sampler2D _MainTex;
			float4	_MainTex_ST;
			float _Threshold;
			float4 _EdgeColor;
			float _Thick;
			float4	_MainTex_TexelSize;	// 1テクセルを正規化した値

			struct appdata_t {
				float4 vertex : POSITION;
				float2 texcoord : TEXCOORD0;
			};
			struct v2f {
				float4 vertex : SV_POSITION;
				half2 texcoord : TEXCOORD0;
			};

			v2f vert(appdata_t v)
			{
				v2f o;
				o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
				o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
				return o;
			}

			fixed4 frag(v2f input) : Color
			{
				float tx = _MainTex_TexelSize.x * _Thick;
				float ty = _MainTex_TexelSize.y * _Thick;

				//※「DirectX9 シェーダープログラミングブック」を参考にしています。
				// 輪郭判定値の計算
				// (-1,-1) ┼─┼ (0, -1)
				//         │  │
				// (-1, 0) ┼─┼ (0,  0)
				// の4ピクセルの対角線同士でデプス値の差を取って2乗したものを加算する
				float col00 = Linear01Depth(tex2D(_MainTex, input.texcoord + half2(-tx, -ty)).r);
				float col10 = Linear01Depth(tex2D(_MainTex, input.texcoord + half2(  0, -ty)).r);
				float col01 = Linear01Depth(tex2D(_MainTex, input.texcoord + half2(-tx,   0)).r);
				float col11 = Linear01Depth(tex2D(_MainTex, input.texcoord + half2(  0,   0)).r);
				float val = (col00 - col11) * (col00 - col11) + (col10 - col01) * (col10 - col01);

				// 閾値以下ならクリップする
				if(val < _Threshold)
				{
					clip(-1);
				}

				fixed4 col = _EdgeColor;
				return col;
			}
			ENDCG
		}
	} 
}
```