フューチャー技術ブログ

HoloLensの空間共有サービスを使ってみよう

はじめに

こんにちは。HoloLens記事第二弾です。

  • 前回の記事はこちらをご参照ください。

今回は、HoloLensでこれから多く使われるであろう空間共有サービス(以下シェアリングサービス)の特性と実装方法の触りについて紹介します。

HoloLensの空間共有サービスとは

HoloLensの醍醐味は、現実世界と仮想世界が融合することです。つまりHoloLensゴーグルをかけると、スマホを通さずとも視野の中にに様々な情報や仮想的なオブジェクトを出現させることができます。

ARとして流行ったポケモンGOを例にすると、スマホの画面上で現実の背景の上にポケモンの画像が合成されて表示されていましたよね? HoloLensを身につけると、散歩中にふと公園を見ると「ピカチュウが花壇から出てきた!」という風景を自然に見ることができます。また、HoloLensは現実世界のオブジェクトをスキャンしてマッピングを行っているので、「ピカチュウが滑り台を滑る」といったことが比較的容易に実現できちゃいます。

でも悲しいかな、HoloLensをそのまま装着しても、みんな別々、パラレルワールドのマサラタウンにいるのです。ある人はそこにピカチュウがいるというけれど、私には見えない、そんな状態です。それでは盛り上がりもかけますし、結局は自分にしか見えない仮想的なものなんだという儚い気持ちになってしまいますよね。

そこで登場HoloLensの**シェアリングサービスです。これを使うと、あるHoloLensで見ているものが、他のHoloLensでも同様に見ることができ、空間の共有が行えます。**「ベンチの上でピカチュウがバク転した! かわゆす」というのを皆で一緒に見ることが可能となります。

閑話(仮想現実と現実に対する哲学的考察をちょっと)

みんながそこにピカチュウがいると言う。これはもはやピカチュウが現実となったと言えるのではないでしょうか。

「この世は人間に記録(観測)されるまでは存在しない」とどこかの偉い方が言いましたが、逆に認識されればそれは現実の世界になるのです。HoloLensを通さないと見えないから現実の世界なわけないじゃんというあなた、あなたは視覚を司る「目」という器官を通して世界を認識しています。この世界も目が無いと、見えなくなるから現実な世界なわけないと言っているのと同じかなと思います。

昆虫は紫外線を見ることができるため、人間とは全く別の世界が広がっていますが、HoleLensが映し出す世界も似たようなものではないでしょうか。また、実際触れるじゃないかと思ったあなた、触れるという現象も触覚も持って感知しているにすぎず、本当の意味でそこに物体があるかどうかなんてだれにもわからないのではないかなと思います。

シェアリングサービスの仕組み

哲学の話は置いて、本題に戻りましょう。
シェアリングを行うためには、お互いの空間情報を中継するためのサーバー(PC)が必要です。

アプリケーションの構成によりますが、本記事ではMicrosoftがHoloLensの開発ノウハウを公開しているHolographicAcademyにてシェアリングサービスを紹介しているHolograms240の仕組みを説明します。

まず試したいという方は、Holograms240の公式サイトに実行方法が記載されています。
もしくは日本語で実施方法を記載してくださっている方がいるのでここを見ると良いと思います。

Holograms240では**HoloToolKit-UnityというUnity上でHoloLensの機能を使用するためのツール群を利用しています。このHoloToolKit-Unityにはシェアリングサービスが含まれています。開発者はこれを使用して、複数HoloLens間でのシェアリングを行うことができます。またHoloToolKit-Unityの中には、サーバーで起動するためのシェアリングサービスの実行ファイルが準備されている**ので、シェアリングを行うためにはこれをPC上で実行して利用できます。

Holograms240は以下のような仕組みで動いています(下図)。
まずはアプリ起動時にアンカー情報と呼ばれる、現実世界のマッピングとUnity上での座標を紐づけるための目印を共有します。

次に、アプリ内で共有が必要なデータを逐次送受信して、同じオブジェクトの同期を行っています。

実空間とのマッピングがされているのはアンカーだけです。

実空間をスキャンした情報とアンカーとのマッピング情報はデータ量が多いです。同期するのに長いと1分以上の時間がかかります! その後の共有情報はデータ量は少ないですが、各オブジェクトやアクションごとにデータの型や、送信処理、受信時の動作を実装してあげなくてはいけません。さすがに楽々と視野すべてを一括で同期して、というのはできないのですね…。今後もっと便利なライブラリができることを期待します。

HoloToolKit-Unityの仕組みを少しだけ紹介

次にHoloToolKit-Unity内で何が行われているのか、かいつまんで紹介します。

ImportExportAnchorManager

Holograms240ではこれを、HologramCollection(HoloLensで扱うオブジェクトの大親)につけてますね。このスクリプトは1機目ではアンカー(Unity上のオブジェクト)を物理的に固定(現実世界にマッピング)して、そのアンカー情報を共有サーバーにエクスポートしています。また2機目以降は、1機目のアンカー情報をインポートして、自身の現実世界のマッピング情報上にアンカーを設置します。これによってUnityの世界では1機目と2機目の座標軸がアンカーを中心として一致します。

CustomMessages

デバイス間で共有するデータの形式の定義と、送受信時の処理が記載されていますこれはアプリの仕様ごとに、がっつり個別実装する必要があります。
以下の流れで見ていきます。

  1. 送受信用のデータ形式
  2. 送信処理について
  3. 受信処理について

1. 送受信用のデータ形式

TestMessageIDはデバイス間で共有(送受信)するデータを定義しています。
オブジェクトやアクションの種類ごとに定義しています。

public enum TestMessageID : byte
{
HeadTransform = MessageID.UserMessageIDStart,
UserAvatar,
UserHit,
ShootProjectile,
StageTransform,
ResetStage,
ExplodeTarget,
Max
}

2. 送信処理について

Sendメソッドは送信処理を実装しています。
例えばSendShootProjectleはユーザーがゲーム内で発射するオブジェクトの送信処理です。
起点となるpositionと打ち出される方向directionをアンカーからの相対位置に変換して送信しています。

public void SendShootProjectile(Vector3 position, Vector3 direction)
{
// If we are connected to a session, broadcast our head info
if (this.serverConnection != null && this.serverConnection.IsConnected())
{
// Create an outgoing network message to contain all the info we want to send
NetworkOutMessage msg = CreateMessage((byte)TestMessageID.ShootProjectile);

AppendVector3(msg, position + (direction * 0.016f));
AppendVector3(msg, direction);

// Send the message as a broadcast, which will cause the server to forward it to all other users in the session.
this.serverConnection.Broadcast(
msg,
MessagePriority.Immediate,
MessageReliability.Reliable,
MessageChannel.Avatar);
}
}

3. 受信処理について

受信時の処理はMessageHandlersを使って登録しています。
例えば上記で送信している発射物の受信時の処理はProjectileLauncher.csに記載されています。下記のコードで受信時イベントの登録を行っています。

CustomMessages.Instance.MessageHandlers[CustomMessages.TestMessageID.ShootProjectile] = this.ProcessRemoteProjectile;

そして、実際の受信処理は下記のProcessRemoteProjectileです。受信した発射物のアンカーからの位置情報(Position)と方向(Direction)から同じ位置にオブジェクトを生成しています。

void ProcessRemoteProjectile(NetworkInMessage msg)
{
// Parse the message
long userID = msg.ReadInt64();
Vector3 remoteProjectilePosition = CustomMessages.Instance.ReadVector3(msg);

Vector3 remoteProjectileDirection = CustomMessages.Instance.ReadVector3(msg);

Transform anchor = ImportExportAnchorManager.Instance.gameObject.transform;
ShootProjectile(anchor.TransformPoint(remoteProjectilePosition), anchor.TransformDirection(remoteProjectileDirection), userID);
}

このように、現実世界上の位置情報として同期されるのはアンカーだけで、その他のオブジェクトを同期したい場合は、個別に定義して処理を記載する必要があります。

まとめ

  • シェアリングサービスはHoloToolKit-Unityを使うことで実装できる
  • 実空間に固定されているのはアンカーだけであり、アンカーの同期には時間がかかる
  • その他のアプリ内情報は個別に連携データの形式と送受信処理を実装する必要がある

おまけ~最新(2017/04/03時点)特ダネ情報~