スマホ向けVR映像を実装してみる

この記事は <a herf="https://qiita.com/advent-calendar/2017/ipu">岩手県立大学 Advent Calendar 2017</a> の9日目の記事です。
記事中に所属研究室の話題がありますが，語った内容が研究室の総意であったり，来年度以降もそうであるといったわけではありません．
あくまで一個人の見解であることをご理解の上お読みください．

#はじめに
私が所属する研究室では，昨年度あたりからVR(Virtual Reality)に関する共同研究やコンテンツ作りのお話をいただくようになりました．
その活動が地方紙や地方局で取り上げられたり，先生方が研究室紹介の際にお話しをされた影響からか，私の一つ下の学年からVRやAR(Augmented Reality)関連の研究を希望してくる学生が増えてきました．
そこで本記事ではそんな流れに乗ってVRの初歩的な実装について書いていきたいと思います．

#VRとは
VRとは仮想現実~~この訳し方については多少議論がされたようですが~~と訳される通り，”私たちがいる世界”とは”別の世界”に五感を刺激されることであたかも”別の世界”が”私たちがいる世界”のように感じさせる技術のことです．
昔から研究が行われている分野ではありますが，ハードの飛躍的な発展により一般にも普及してきた影響で，かなり知名度が上がったのではないでしょうか．
特にスマホの存在が普及に一役買ったように思います．

#目的
今回は一般に普及しつつあるものの，まだまだちゃんとしたVR機材は値段が高いので，多くの方が持っているであろうスマホでVR映像を実装するにはどうすればいいのかについてお話ししたいと思います．

#必要なもの
当然スマホが1台必要になります．
ただ残念ながらスマホ単体では立体的に見れないので，VRとしてきちんと確認したい方はVRゴーグルをお買い求めください．2000~3000円くらいあればそれなりにいいものが買えたはずです．
VRゴーグルを自作しているブログ記事とかもあるので本当の一から作ってみるのもありかもしれません．

#開発環境
Unity Version 2017.2.0f3 Personal

#シーンの作成
人間が物体との距離を知覚する手段の一つに，左右の目が離れていることで生じる視差があります．
VRはこの視差を利用して平面の映像を立体的に見せているわけです．
この視差が生じている映像をスマホを2分割するように，左右に配置すればとりあえずはそれっぽい映像が作れます．
少々わかりづらいですが，真ん中を基準に左目用の映像と右目用の映像になっています．

<img width="750" alt="スクリーンショット 2017-12-08 23.19.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/153217/3a661382-e65b-8a2a-9910-16f46b86c088.png">　　
　　
　　
どういった設定になっているかというと，シーン上で単純に水平方向にずらしているだけです．
各自の瞳孔間距離に合わせてカメラ間の距離を設定します．ちなみにUnityは1Unit=1メートルに設定されています．
Viewport Rectの項目のところで左右のカメラ映像を実行時にどこの位置にどの幅で表示するか設定できます．
私の設定は以下のスクショのようにしています．

<img width="277" alt="スクリーンショット 2017-12-08 22.59.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/153217/c00a48fb-e3ed-a3ed-0285-78566105900a.png"><img width="277" alt="スクリーンショット 2017-12-08 22.59.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/153217/5f7a8852-7b0e-3900-9fff-f6f7b8bbb218.png">
　　

<img width="750" alt="スクリーンショット 2017-12-08 22.58.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/153217/bfffa6c4-3e0d-e33f-f72c-e243cfc17ffa.png">
　　

実際にスマホVRの映像を見てもらうとわかりやすいのですが，映像に魚眼レンズのような歪みが生じています．きちんとした文献を読んだわけではないので想像になってしまうのですが，眼球形状に合わせるためにこのような映像になっているのではないかと思われます．
この映像を再現するためにPost Processing Stackを使った方が良いのですが，ImageEffectsという前バージョン時代のアセットの中にFisheyeというそのまんまなScriptがあるので使用します．
適切な値がわからなかったのでそれっぽい値を入れているだけなので，適宜変えてください．
これを左右のカメラに適用します．

<img width="276" alt="スクリーンショット 2017-12-08 23.38.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/153217/c9defe54-7110-4b99-7939-350eaba65443.png">

#プログラム
映像の方があらかた完成したので，残りの作業として頭部の変動に合わせて映像も変えるためにジャイロを適用していきます．合わせて瞳孔間の距離を入れるとカメラの距離が変わるようにScriptを書いていきます．

<img width="245" alt="スクリーンショット 2017-12-08 23.50.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/153217/aea74e53-638e-c8f1-3ca5-9c5486808809.png">

```C#:VRmodule.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class VRmodule : MonoBehaviour {

	public GameObject headSet;
	public Camera rightCam;
	public Camera leftCam;

	public float parallax = 0.06f;

	void Start () {

	// ジャイロを有効にする
	#if UNITY_EDITOR
	Input.gyro.enabled = false;
	#elif UNITY_ANDROID
	Input.gyro.enabled = true;
	#elif UNITY_IOS
	Input.gyro.enabled = true;
	#endif
	}

	void Update () {
	if (Input.gyro.enabled)
	{
	Quaternion quate = Input.gyro.attitude;

	// androidとiosで座標系が違うのでデバイスごとに設定
	#if UNITY_EDITOR

	#elif UNITY_ANDROID
	headSet.transform.localRotation = Quaternion.Euler(90, 0, 0) * (new Quaternion(-quate.x,-quate.y, quate.z, quate.w));
	#elif UNITY_IOS
	headSet.transform.localRotation = Quaternion.Euler(90, 0, 0) * (new Quaternion(-quate.x,-quate.z, -quate.y, quate.w));
	#endif
	}
	}

	void OnValidate() {
	// 視差の変更があったら反映する
	rightCam.transform.localPosition = new Vector3(-parallax, 0, 0)/2;
	leftCam.transform.localPosition = new Vector3(parallax, 0, 0)/2;
	}
}
```

手元にios端末がないので確認できていないですが，andoridとiosでは座標系が異なるようです．

#実行結果
![testVR.mp4.gif](https://qiita-image-store.s3.amazonaws.com/0/153217/99721ded-75f9-c63d-3e61-c306e465d6da.gif)

ビルドするときにスマホが常に横向きになるように設定しておきましょう．


#終わりに
今回は映像を映すだけの実装になりましたが，アプリストアでリリースされているスマホVRのアプリを見てみると，画面の中心に選択用のポインターがあったり，歩行の際に生じるスマホの上下移動を検知してVR空間内の視点を移動させたりするものもあります．
それについてもそのうち書けたらいいなと思います．今は思うだけです．
また，再来週にARを画像処理レベルから実装してみようってことをやるのでよろしければそちらもご覧ください．

さて，明日のアドベントカレンダーは<a herf="https://qiita.com/shimokp">shimokp</a>くんです.
同じ研究室の優秀な後輩なので，多大なる期待をした上で明日の記事を待ちましょう．

#参考にした記事
>
><a herf="https://qiita.com/fuqunaga/items/b1a3e38af71f062f0781">UnityでiOSのジャイロの使い方をちゃんと説明する</a>
><a herf="https://qiita.com/fumishitan/items/c2b023b9b9655d0a7d46">VRゲーム用にスマホジャイロセンサとMainCameraを同期させる</a>
>