UnityでVR対応のどこでもドア実現を目指す その1 Depth Mask編

# 概要
Unityによるどこでもドア実装の試行錯誤について記事にしていこうと思います。

※7/2 解説ページのリンクを貼りました

# 解説ページ
今回使用するテクニックの補足については、以下のページを参照ください

[UnityでVR対応のどこでもドア実現を目指す その1 Depth Mask編 の 補足](http://qiita.com/tsgcpp/items/c728b661805159a23812)

# 最終目標
どこでもドアのような別空間を映し、かつ空間の移動するモックをUnityで実現する。
(実現出来ないかもしれません。。。)

# 経緯
きっかけはTextureRenderを使ったPortalの実装記事を読ませていただいてるときに「ワープ先描画して、Depth Buffer偽装して、ワープホール部分の色をそのままにすればいけんじゃね？」という発想になったからです。

実際に実装してみるとメリット、デメリット両方あって微妙かな？とも思ったのですが、Qiitaで記事を書いてみたかったというのもあったので、復習を兼ねて記事にしようと思います。

# 動作環境
- Windows10
- Unity 5.6.1f1
- HTC Vive(VRの場合)

# 方針
- 複数のカメラを用意し、描画の順番を利用して実装
  - Render Texture は不使用 (※現時点では)
- DepthMaskを利用
  
# 目標結果
- 青色の空間が実際に自分のいる場所
- 青色のCubeの手前にどこでもドアを設置
- 赤色の空間の赤色のSphereの手前がどこでもドアの先

![whole.JPG](https://qiita-image-store.s3.amazonaws.com/0/188798/fb65b20f-ef7b-b91f-1b91-4507f833677a.jpeg)

![result.JPG](https://qiita-image-store.s3.amazonaws.com/0/188798/468ab396-d2a5-7a96-dc4a-5e4f13778f26.jpeg) 

# 実装

## 1. 移動前(青)のオブジェクトを用意
青いPlaneの上に青いCubeを配置し、その手前にどこでもドアを想定したPlane(以下**Dokodemo Door** or どこでもドア)を配置

![blue.JPG](https://qiita-image-store.s3.amazonaws.com/0/188798/6c55922c-b730-b11b-701f-46945552ab02.jpeg)

## 2. 移動先(赤)のオブジェクトを用意
赤いPlaneの上に赤いSphereを配置

![red.JPG](https://qiita-image-store.s3.amazonaws.com/0/188798/89aaf95d-88ae-1dec-2863-40cab7ec07a7.jpeg)

## 3. Depth Mask用Shaderを用意
※Depth Maskについては [こちら](http://qiita.com/tsgcpp/items/c728b661805159a23812#depth-mask) を参照ください

以下のshaderを使用することでメッシュ領域の色を変更することなく、Depth Bufferのみ変更することができます。

```
Shader "DepthMask" {
    SubShader {
        Pass
        {
            // Depth Testの実施 および Depth Bufferの更新
            Zwrite On

            // 色情報は更新しない
            ColorMask 0
        }
    } 
}
```

※OpenGLのglDepthMaskと混同しそうですが別物です

## 4. Depth Mask用ShaderをアタッチしたMaterialを作成
1. 新規Material を作成し、名前を「**Depth Mask Material**l」(任意)にする
2. Depth Mask Shader を上記 Depth Mask Materialにアタッチ
3. Depth Mask Materialの**Render Queueを他のMaterialより小さくする**(ここではGeometry-1)
  * 他のメッシュよりも先にDepth Maskをかけることがポイント！

![depthmask.JPG](https://qiita-image-store.s3.amazonaws.com/0/188798/2dc60e8f-4c46-a7f5-7aac-790b57899367.jpeg)

※ちなみにRender Queueを他と同じにすると、描画順が不規則になりました

## 5. Depth Mask Material を Dokodemo Doorにアタッチ
上記で作成したDepth Mask Materialを「Dokodemo Door」のMesh Rendererにアタッチ

## 6. 2つのカメラを用意
1. Main Cameraを青空間用として(以下「**青カメラ**」)、青空間の任意の位置に配置
2. 赤空間用に新規Camera(以下「**赤カメラ**」)を作成し、赤空間の**どこでもドアから青カメラまでの相対位置**に配置
  * 「2.」の画像はカメラから見て、赤いSphereの手前にどこでもドアの移動先がある想定で配置
  * ※赤空間にどこでもドアのオブジェクトは不要

## 7. 2つのカメラのDepthを調整
※カメラの描画順については [こちら](http://qiita.com/tsgcpp/items/c728b661805159a23812#%E3%82%AB%E3%83%A1%E3%83%A9%E3%81%8C%E8%A4%87%E6%95%B0%E5%AD%98%E5%9C%A8%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88%E3%81%AE%E6%8F%8F%E7%94%BB%E9%A0%86) を参照ください

1. 赤カメラは先に描画するため**Depth: -1** (数値自体は任意)
2. 青カメラはあとから描画するため**Depth: 0** (赤カメラより大きな値で任意)

## 8. 結果表示
ここまで実装すればGame画面で「目標結果」と同様の画面になっていると思います。
ですが、まだまだどこでもドアまでは遠いです。。。

# 現時点の致命的な欠点
以下のように赤空間のどこでもドアの移動先の手前に白いSphereを配置してみると？

![omg.JPG](https://qiita-image-store.s3.amazonaws.com/0/188798/2a809b7a-cf0e-4199-9234-d926d36575b2.jpeg)

白いSphereがどこでもドアに写っちゃいます。。。

![omg2.JPG](https://qiita-image-store.s3.amazonaws.com/0/188798/4de8a74c-d0c2-94dd-eb26-b4194f27e211.jpeg)

どこでもの移動先の手前にあるオブジェクトのため、映るとおかしいものです。

なぜこうなったかというと、
カメラの描画範囲は横から見た場合、以下のような視錐台の範囲となります。
ですが今回の場合は赤い領域は描画せず、青い領域のみ描画したいのでちょっとテクニックが入ります。

![omg3_2.JPG](https://qiita-image-store.s3.amazonaws.com/0/188798/a2abaeec-750e-40ad-b64d-686b55039b8f.jpeg)


どうすればいいのか？
**長くなってるので次回に続く**


# 今回の実装のメリット・デメリット
この実装方法はメリット、デメリットが幾つか見られます。
せっかくなので一部記載しようと思います。

## メリット
### どこでもドアの形状に自由度がある
以下はunitychanのメッシュにDepth Mask Materialをアタッチして、どこでもドアの代わりに配置したものです。
立体感のある？ワープゲート的なものが出来たりします。
これはVRでも確認できますが、少し不思議な感じでしたｗ

![どこでも Unity Chan.JPG](https://qiita-image-store.s3.amazonaws.com/0/188798/eaf3c586-6ff0-b2d4-1117-9e88b2a0ff25.jpeg)


## デメリット
### 描画負荷が高い
Screen全体に対して描画を実施している関係上、最終的な描画結果で隠れる部分の処理は無駄になってしまいます。
描画されない部分にキャラクターなどのMesh大量のオブジェクトがあったら最悪です。。。

これに関してはCamera.rectによるクリッピングで多少なりとも解決しようとしたのですが、rectが異なる復数カメラが有る場合Depthがおかしくなるバグがあるようで現在難航中。。。

バグ詳細：https://issuetracker.unity3d.com/issues/camera-depth-is-ignored-when-setting-both-cameras-viewport-other-than-0-0-1-1

# 今回のまとめ
今回はDepth Maskを使用してどこでもドアの先を映すところまでを実装してみました。
まだまだどこでもドアとして足りない部分がたくさんあるので、少しずつでも解決していきたいと思います。
(果たして納得のいくとこまでいくのかどうか。。。)

初記事だったので、至らない点がたくさんあるかと思います。
もしご指摘やコメントなど有りましたら、よろしくお願いします。

今回は全体的に文言が硬かった気がするので、次回はストーリー調にしてみようかと思います。