Unityのカメラ越しにZoomに参加する方法

## はじめに

こちらの「 [TouchDesignerの出力でZoomミーティング参加](https://qiita.com/makoragi/items/5fbeafe4728fa59a5472) 」の記事を拝見し刺激を受けまして、Unityでもやってみました。
大まかに言うと、Zoomが扱うことのできる仮想の映像入力デバイスを用意して、そこにUnityの映像出力を送り込んでやろうというわけです。

## 動作環境
* Webカメラが接続されたWindows PC
* Unity (バージョンは後述の[KlakSpout][]に準拠)
* [Spout][]およびインストーラーに同梱されたSpoutCam (後述)
* [KlakSpout][]

## 仕組み
[Spout][] というプロセス間でのテクスチャ共有の仕組みと、SpoutCamというSpoutのテクスチャを扱える仮想カメラデバイス(形態はDirectShowのソースフィルタ)を使います。
UnityからSpoutの形式映像出力には、@keijiro さんの[KlakSpout][]を利用させてもらいます。
これらを組みわ合わせると、Unityのシーンに配置したカメラの映像を、Zoomから見るとWebカメラと同様に扱うことができます。

## 手順

### 1. SpoutおよびSpoutCamのインストール
まず、[Spout][]のWebサイトからインストーラーをダウンロードしてインストールします。
インストーラーの設定画面で、SpoutCamにもチェックを入れてからインストールしてください。

### 2. Unityで適当な3Dのプロジェクトを作りQuadを配置する
こんな感じでQuadを配置します。
このQuadは、Webカメラの映像をWebCamTextureとして貼り付けて表示するために使います。
Quadのスケールは、Webカメラの画角に合わせて縦横4:3や16:9などの比率になるようにすると良いでしょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39561/1044e57c-b3b6-2ced-cf48-5f3c5af99621.png)

### 3. マテリアルとWebカメラのスクリプトを作ってQuadにアサインする
マテリアルを作ってQuadにアサインします。
Defaultのままでも良いですが、ここではUnlit/Textureシェーダーを使いたかったので、新規作成しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39561/261c545a-48a9-9cc8-c1a4-5cef465c83c0.png)
WebCamTextureをマテリアルのメインテクスチャに設定するためのスクリプトを書きます。[このページ](https://docs.unity3d.com/ScriptReference/WebCamTexture.Play.html) の実装例そのままです。

```WebCamTexturePlayer.cs
using UnityEngine;

public class WebCamTexturePlayer : MonoBehaviour
{
    void Start()
    {
        WebCamTexture webcamTexture = new WebCamTexture();
        Renderer renderer = GetComponent<Renderer>();
        renderer.material.mainTexture = webcamTexture;
        webcamTexture.Play();
    }
}
```
ちなみに、お使いのPCにWebカメラをはじめ複数の映像入力デバイスが接続されている場合には、[ここ](https://docs.unity3d.com/ScriptReference/WebCamTexture-devices.html) とか [ここ](https://docs.unity3d.com/ScriptReference/WebCamTexture-deviceName.html) のAPIおよび実装を参考にして、使用するデバイス名を指定してあげた方が確実です。(今回は長くなるので割愛します)

このスクリプトとマテリアルを、先ほどシーン上に配置したQuadにアサインします。
ここで一旦Playを押してみると、デフォルトのWebカメラの映像がQuadにテクスチャマップされて表示されるのが確認できると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39561/9dc3432e-6dc3-d637-a773-45c0ed2aa7b8.png)

### 4. KlackSpoutをプロジェクトに追加する
[KlakSpout][]の[releases](https://github.com/keijiro/KlakSpout/releases)のから、`KlakSpout.unitypackage`をダウンロードします。
`KlakSpout.unitypackage`をUnityのプロジェクトにインポートします。

### 5. SpoutSenderをカメラに追加する
UnityのシーンのCameraにKlackSpoutの`SpoutSender`スクリプトをアタッチします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39561/b3a10f37-e7e9-511b-cb81-40a970068474.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39561/4d9a1ed0-604b-de73-1aa2-7365ec6cf567.png)

ここまでで、Unityのカメラに映る内容をSpoutで出力できるようになりました。

### 6. Zoomで入力を確認する

SpoutCamに映像を流すために、UnityでPlayボタンを押してシーンをプレイ状態にしておきます。
次にZoomの設定画面からカメラで`Spout Cam`を選ぶと、こんな風になってZoomの会議に参加することができます。(注: 目線は後付けです)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39561/be13cffe-c97a-35c7-7ea5-90bf3d5bbad4.png)

### 7. おまけ。バーチャル背景と遮蔽で遊ぶ
Unity側で体が隠れるように適当な板を置いて、Zoom側でバーチャル背景を選ぶと、Zoomの強力な人物検出機能をうまい事騙せます。
そのため、グリーンバックや緑の服とか着なくても、こんなことができます。(注: 目線は後付けです。あと目を隠すと人物として認識されなくなるので完全に消えてしまいます)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39561/92b4f8ac-5b35-23f7-c502-e194580b7227.png)

### 8. Tipsおよびトラブルシューティング
#### SkypeでSpout Camが選べない
[Skype公式ページにあるSkypeアプリ](https://www.skype.com/ja/get-skype/)を使ってください。
一方で[Microsoft StoreのSkypeアプリ](https://www.microsoft.com/ja-jp/p/skype/9wzdncrfj364?activetab=pivot:overviewtab)ではSpout Camが選べないようです。(UWPでDirectShowじゃなくてMedia Foundationにしか対応してないから?)
#### WebブラウザでSpout Camが選べない
中間に[OBS](https://obsproject.com/)を挟むとうまくいくことがあります。
OBSにSpout入力プラグイン[obs-spout2-source-plugin](https://github.com/Off-World-Live/obs-spout2-source-plugin)と、仮想カメラ出力[obs-virtual-cam](https://github.com/CatxFish/obs-virtual-cam)の2つのプラグインを入れてOBSの入出力をうまいこと設定してください。
これでSpout入力を `OBS-Camera`という名前の映像出力デバイスに見せかけることが出来ます。
あとはこの`OBS-Camera`をブラウザの映像入力デバイス設定で選んでやればたぶんOKです。
#### MacでもUnityからZoomに参加したい
[CamTwist][]というアプリを使って、デスクトップの一部をキャプチャして仮想カメラデバイスの出力としてやるのが簡単です。CamTwistの設定で、UnityのGameビューの範囲を指定するように設定すれば、あとはZoomアプリから`CamTwist`という名称のカメラが選択できるようになっているはずです。
そのほかのやり方としては、[CamTwist][]と、[Syphon](http://syphon.v002.info/)(MacにおけるSpoutのようなテクスチャ共有の仕組み)と、[KlakSyphon][](KlakSpoutと同じく@keijiroさん作のSyphonプラグイン)を組み合わせて使う方法などがあります。
#### Zoomに表示されない
Zoomでバーチャル背景を設定していて、かつUnity側でエフェクトを掛けたり解像度が粗かったりする場合には、Zoomの人物認識機能により人物とみなされず、その場にいなかったことにされる場合があります。

それでは、Unityで楽しいZoomライフを！

[Spout]: https://spout.zeal.co/
[KlakSpout]: https://github.com/keijiro/KlakSpout
[CamTwist]: http://camtwiststudio.com/
[KlakSyphon]: https://github.com/keijiro/KlakSyphon