AzureKinectでグリーンバック無しでクロマキー的な何か

3D Sensor Advent Calendar 2019 の13日目の記事です。

##はじめに
LIVを使ってBeatSaberのMR動画を撮影してるときに、「これ自分でも実装できるんじゃないか？？」と思ったのをきっかけに挑戦してみました。
2時間くらい頑張ったらサクッと出来たので記事を書いてみます。

## 出来たこと
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">AzureKinectでグリーンバック無しのクロマキー合成的な何か <a href="https://t.co/SuvWXWhAbf">pic.twitter.com/SuvWXWhAbf</a></p>&mdash; そいちろ@LookingGlass&amp;AzureKinect (@asidys230) <a href="https://twitter.com/asidys230/status/1205414541896478720?ref_src=twsrc%5Etfw">December 13, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 環境
・Unity 2019.2.11.f
・Azure Kinect (Amazonで買った)

## 手順(ざっくり)
###1．ポイントクラウド表示
こちらの素晴らしいブログを参照してください。
　[C#で始めるAzure Kinect開発⑥：Unity+C#でPoint Cloud表示](https://tks-yoshinaga.hatenablog.com/entry/azurekinect-6)

こんな感じに表示できるはず
![K4A_NoGreenBack - AzureKinectSample - PC, Mac & Linux Standalone - Unity 2019.2.11f1 Personal_ _OpenGL ES 3.2_ 2019-12-13 12-59-09_Moment.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215186/7ddf4fc1-e4fe-9bf2-2829-5b5fb377b35c.jpeg)


###2.クロマキー的な何か
今回のクロマキー的な何かはオブジェクトが重なった部分を描画しないようにして実現しました。
人物抽出ではなくて背景除去になります。

背景のみが映っている状態でポイントクラウドを複製、後述のシェーダーを適用したマテリアルに変更。
複製したポイントクラウドのPointSizeを少し大きくして、背景のポイントクラウドを覆うとこんな感じに。
![K4A_NoGreenBack - AzureKinectSample - PC, Mac & Linux Standalone - Unity 2019.2.11f1 Personal_ _OpenGL ES 3.2_ 2019-12-13 12-59-09_Moment(3).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215186/d65c5956-6a1e-2e99-228e-8c02c6f769ee.jpeg)

シェーダー素人なので、こちらの記事を参考にしました。
　[【Unityシェーダ入門】オブジェクトが重なった部分をくり抜く](http://nn-hokuson.hatenablog.com/entry/2017/06/01/220504)	

ポイントクラウド用に色々書き替えたのが以下です。

```ColorutoutPoint.shader
Shader "Custom/ColorutoutPoint" {
	Properties{
	 _Size("ShaderSize", Float) = 1.000000
	 _PointSize("PointSize", Float) = 5.0
	 _Color("Color", Color) = (1,1,1,1)

	}
		SubShader{
		 Tags {"Queue" = "Geometry-1"}

		 Pass {
						Zwrite On
						ColorMask 0

		CGPROGRAM
		#pragma vertex vert
		#pragma fragment frag
		#pragma target 2.0
		#include "UnityCG.cginc"
		#pragma multi_compile_fog
		#define USING_FOG (defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2))

		float _Size;
		float _PointSize;

		// uniforms
		// vertex shader input data
		struct appdata {
		  float3 pos : POSITION;
		  half4 color : COLOR;
		  UNITY_VERTEX_INPUT_INSTANCE_ID
		};

		// vertex-to-fragment interpolators
		struct v2f {
		  float psize : PSIZE;
		  fixed4 color : COLOR0;
		  #if USING_FOG
			fixed fog : TEXCOORD0;
		  #endif
		  float4 pos : SV_POSITION;
		  UNITY_VERTEX_OUTPUT_STEREO
		};

		// vertex shader
		v2f vert(appdata IN) {
			IN.pos.xyz = IN.pos.xyz * _Size;

		  v2f o;
		  UNITY_SETUP_INSTANCE_ID(IN);
		  UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
		  half4 color = IN.color;
		  float3 eyePos = mul(UNITY_MATRIX_MV, float4(IN.pos,1)).xyz;
		  half3 viewDir = 0.0;
		  o.color = saturate(color);
		  // compute texture coordinates
		  // fog
		  #if USING_FOG
			float fogCoord = length(eyePos.xyz); // radial fog distance
			UNITY_CALC_FOG_FACTOR_RAW(fogCoord);
			o.fog = saturate(unityFogFactor);
		  #endif
			// transform position
			o.pos = UnityObjectToClipPos(IN.pos);
			o.psize = _PointSize;
			return o;
		  }

		// fragment shader
		fixed4 frag(v2f IN) : SV_Target {
		  fixed4 col;
		  col = IN.color;
		  // fog
		  #if USING_FOG
			col.rgb = lerp(unity_FogColor.rgb, col.rgb, IN.fog);
		  #endif
		  return col;
		}
		ENDCG
		 }
	}
}
```
  
```PointCloud.cs
        //背景除去用メッシュ生成
        if (Input.GetKeyDown(KeyCode.Space))
        {
            if (GameObject.Find("CutoutMesh"))
            {
                GameObject obj = GameObject.Find("CutoutMesh");
                Destroy(obj);
            }
            verticesGreenBack = vertices;
            greenBack = new GameObject();
            greenBack.name = "CutoutMesh";
            greenBack.AddComponent<MeshFilter>();
            meshRendererG = greenBack.AddComponent<MeshRenderer>();

            meshG = new Mesh();

            //65535点以上描画する際に下記を記述
            meshG.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;

            //頂点座標と色をmeshに渡す
            meshG.vertices = verticesGreenBack;

            //リストに格納した番号の頂点座標を点として描画
            meshG.SetIndices(indices, MeshTopology.Points, 0);

            //メッシュをこのオブジェクトのMeshFilterに適用
            greenBack.GetComponent<MeshFilter>().mesh = meshG;

            meshRendererG.material = materialG;
        }

        // 削除
        if (Input.GetKey(KeyCode.Backspace))
        {
            GameObject obj = GameObject.Find("CutoutMesh");
            Destroy(obj);
        }
```

###3.おもむろにカメラの前へ。
いい感じに人だけ映る。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">AzureKinectでグリーンバック無しのクロマキー合成的な何か <a href="https://t.co/SuvWXWhAbf">pic.twitter.com/SuvWXWhAbf</a></p>&mdash; そいちろ@LookingGlass&amp;AzureKinect (@asidys230) <a href="https://twitter.com/asidys230/status/1205414541896478720?ref_src=twsrc%5Etfw">December 13, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


##さいごに
明日はtakminさんの「kalibrを使ってカメラ/IMUキャリブレーション」です。