戯言日記

ホロレンジャーの戦いの記録

Mixed Reality 250 翻訳 第3章

Chapter 3 - Sharing

youtu.be

目標

ネットワークが正しく構成されていること,及びデバイス間で空間アンカーがどのように共有されているかを詳しく確認する

ビルドするもの

プロジェクトをマルチプレイヤープロジェクトに変換します。ホストや参加者にUIとロジックの追加をします。HoloLensユーザは頭上にある雲でセッション内にいるお互いを確認でき,Immersiveデバイスユーザは,アンカー付近に雲を持っています。Immersiveデバイス内のユーザはシーンの原点から相対的な場所にいるHoloLensユーザを見ることができます。HoloLensユーザは全て同じ場所にあるHologramの島を見ることができます。気を付けるべき重要な点は,この章ではImmersiveデバイスユーザーは島の中におらず,島を俯瞰するHoloLensと同様にふるまいます。

Steps

  • IslandとVRRoomの除去
    • Hierarchy で Isaland を右クリックし,削除
    • Hierarchy で VRRoom を右クリックし,削除

f:id:haikage1755:20170727222154p:plain

  • Uslandの追加
    • AppPrefabsからUslandをHierarchyに置く

f:id:haikage1755:20170727222316p:plain

  • AppPrefabsから下記をHierarchyに置く
    • UNETSharingStage
    • UNetAnchorRoot
    • UIContainer
    • DebugPanelButton

f:id:haikage1755:20170727222529p:plain

  • HoloToolkit->Configure->Applied Mixed Reality Capability Settingsを開く

f:id:haikage1755:20170914185639p:plain

  • Internet Client にチェックを入れて,Applyを押す

f:id:haikage1755:20170914185817p:plain

  • 前回同様にSaveしてBuildする

コードを理解する

Assets\AppPrefabs\Support\SharingWithUnet\Scriptsを開き,UnetAnchorManager.csをダブルクリックする。HoloLensが別のHoloLensとトラッキング情報をシェアし両デバイスが同じ空間を共有できるようにする能力は,魔法のようです。2人以上で同じデジタルデータを利用して共同作業をする時,Mixed Realityのパワーが発揮されます。
 
このスクリプトで注目する点はいくつかあります
Start関数では,IsDisplayOpaqueをチェックしていることに注目してください。ここで,Anchorが確立されているフリをします。これは,ImmersiveデバイスにAnchorをImport/Exportする方法が公開されていないためです。一方,HoloLens側ではこのスクリプトはデバイス間のAnchorのシェアリングを実装しています。

f:id:haikage1755:20170812015917p:plain

セッションを開始したデバイスはExportするためにAnchorを生成します。セッションに参加したデバイスは,セッションを開始したデバイスにAnchorを要求します。

Exporting

ユーザーがセッションを生成すると,NetworkDiscoveryWithAnchors は UNETAnchorManagers内のCreateAnchor関数を呼び出します。CreateAnchorの流れを追ってみましょう。
まずいくつかのハウスキーピングを行い,以前に収集したであろうAnchorのデータを全て除去します。そして,次にロード用にキャッシュされたAnchorがあるかチェックします。Anchorデータはだいたい5~20MBほどでなので,キャッシュされたAnchorを再利用することで,ネットワーク経由で転送する必要のあるデータ量を省くことができます。後にこれがどのように動作するか確認します。Anchorを再利用している場合でも,新しい参加者がAnchorを持っていない場合に備えて,Anchorデータを準備する必要があります。

Anchorデータを準備する段階で,WorldAnchorTransferBatch Class は 他のデバイスやアプリに送信するAnchorデータを準備する機能と,AnchorデータをImportする機能を公開しています。Exportパスから,WorldAnchorTransferBatch へAnchorを追加し,ExportAsync 関数を呼び出します。

f:id:haikage1755:20170812021919p:plain

ExportAsync関数はExport向けのデータを生成する時にWriteBuffer callbackを呼び出します。

f:id:haikage1755:20170812030828p:plain

全てのデータがExportされると,ExportComplete が呼び出されます。WriteBuffer では,Export用に保持するリストデータチャンクを追加します。ExportComplete では,リストを配列に変換します。AnchorName 変数も同様に設定され,Anchorがない場合は,他のデバイスがAnchorを要求するようにTriggerします。

場合によっては,AnchorはExportされないか,極小のデータを生成するので,再度試みられます。ここではCreateAnchor が再度呼び出されます。

f:id:haikage1755:20170812023652p:plain

Exportパスの最後の機能は,AnchorFoundRemotelyです。他のデバイスがAnchorを見つけた時,デバイスはホストに通知し,ホストはそのAnchorが"good anchor"であり,キャッシュし得る信号であるとして扱います。

f:id:haikage1755:20170812022733p:plain

Importing

HoloLensがセッションに参加すると,AnchorのImportが必要になります。UNETAnchorManagerのUpdate関数では,AnchorName がポーリングされます。

f:id:haikage1755:20170812025022p:plain

Anchorの名前が変更されると,Importプロセスが開始されます。まず,Local Anchor Storeから指定された名前でAnchorをロードします。もし既にAnchorを持っていれば,データの再ダウンロードをせずにそれを利用します。もしなければ,Downloadを開始するWaitForAnchor を呼び出します。

f:id:haikage1755:20170812025333p:plain

Downloadが終了すると,NetworkTransmitter_dataReadyEvent が呼び出されます。これはダウンロードされたデータを用いて,ImportAsync を呼び出すようUpdate Loopに通知します。

f:id:haikage1755:20170812025636p:plain

ImportAsync はImportプロセスが終了するとImportComplete を呼び出します。

f:id:haikage1755:20170812030037p:plain

もしImportが成功していれば,AnchorはLocal player storeに保存されます。

f:id:haikage1755:20170812030113p:plain

PlayerController.csは実際にAnchorFoundRemotelyを呼び出して,良いAnchorが確率されたことをホストに通知します。

f:id:haikage1755:20170812030504p:plain
PlayerController内で,Anchorが確立すると呼び出される

進捗を楽しむ

今回,HoloLensユーザはStart settion button を用いてセッションを開始します。HoloLensまたはImmersiveデバイスを利用する他のユーザーは,セッションを選択し,join session buttonを押します。もし複数のHoloLensデバイスユーザーがいる場合,彼らの頭上には赤い雲が浮かびます。また,青い雲が各Immersiveデバイスユーザに現れますが,HoloLensデバイスと同じ作業空間を見つけようとしないため雲は頭上にはありません。
このプロジェクトのポイントは,シェアリングアプリケーションを包括することです。それほど多くはありませんし,ベースラインとして機能し得ます。次の章では,楽しめる体験を構築します。共有体験デザインの更なるガイダンスはここにあります。

Mixed Reality 250 翻訳 第2章

第2章 Interaction

youtu.be

目標

Windows Mixed Reality アプリケーションの入力を扱う方法を示します

ビルドするもの

第一章のアプリ上に基づき,ユーザがHologramsを掴み,HoloLens内で現実世界のサーフェスや,Immersiveデバイス内のバーチャルテーブルの上に置ける機能を追加します

Steps

  • InputManagerの追加

 ・ HoloToolkit > Input > PrefabsからInputManagerをHierarchyにあるManagersの子として置く
 ・ HoloToolkit > Input > Prefabs > Cursor から Cursor を Hierarchyに置く

f:id:haikage1755:20170727204945p:plain

  • Spatial Mappingの追加

 ・HoloToolkit > SpatialMapping > Prefabs から SpatialMapping を Hierarchyに置く

f:id:haikage1755:20170727205124p:plain

  • Virtual Playspaceの追加

 ・Hierarchy で MixedRealityCameraParentを展開し,Playspaceを選択

f:id:haikage1755:20170727205258p:plain

 ・InspectorでPlayspaceのチェックボックスを有効にする

f:id:haikage1755:20170727205234p:plain

 ・AppPrefabsからVRRoomをHierarchyに置く

f:id:haikage1755:20170727205325p:plain

  • WorldAnchorManagerの追加

 ・Hierarchy内で,Managersを選択
 ・Inspectorで,Addcomponentをクリック
 ・World Anchor Manager とタイプする
 ・World Anchor Manager を選択し,追加する

f:id:haikage1755:20170727210124p:plain

  • IslandへのTapToPlace の追加

 ・Hierarchy内で,Islandを展開する
 ・MixedRealityLandを選択する
 ・Inspectorで,Add Componentをクリック
 ・Tap To Place とタイプし,選択する
 ・Place Parent On Tap をチェックする
 ・PlaceGameObjectToPlaceにIslandを指定
 ・Placement Offset に (0, 0.1, 0)をセットする

f:id:haikage1755:20170727205906p:plain

コードを理解する

Script 1 - GamepadInput.cs

Assets\HoloToolkit\Input\Scripts\InputSourcesから,GamepadInput.csを開く。また,同一パス内にある GesturesInput.csもダブルクリックする。
尚,両スクリプトがBaseInputSourceという共通Classを持つことに注目して下さい。

f:id:haikage1755:20170812005540p:plain

BaseInputSourceはInputManagerへの参照を保持することで,スクリプトがイベントをトリガーできるようにします。この場合,InputClicked eventが関連しています。これは第2章でTapToPlaceを追加したときに重要になってきます。GamePadInputの場合,押されるコントローラのAボタンをポーリングし,そしてInputClickedイベントを立ち上げます。GesturesInputでは,TappedEventに反応してInputClickedイベントが立ち上がります。

f:id:haikage1755:20170812010519p:plain
Gamepadの場合

f:id:haikage1755:20170812010608p:plain
Gestureの場合

Script 2 - TapToPlace.cs

Assets\HoloToolkit\SpatialMapping\Scriptsから,TapToPlace.csを開く。Holographic アプリを作成する多くの開発者が最初に実装したいことは,ジェスチャー入力でHologramsを移動することです。そのため,我々はこのスクリプトにコメントを徹底的にするように努めました。このチュートリアルでは,いくつかの注目すべき点があります。
最初に,TapToPlaceがIInputClickHandlerを実装することに注目してください。IInputClickHandlerは,GamePadInput.csまたはGesturesInput.csによって生成されたInputClickedイベントを処理する関数を公開しています。

f:id:haikage1755:20170812011036p:plain
IInputClickHandlerの中身

OnInputClickedは,TapToPlaceを持つオブジェクトにフォーカスがあるときにBaseInputSourceがクリックを検出すると呼び出されます。HoloLensでAirTapをする,あるいはXboxコントローラのAボタンを押したときにイベントをトリガーします。

 次にUpdate内でサーフェスを見ているかどうかを監視しているので,ゲームオブジェクトをテーブルのようなサーフェスに置くことができます。

f:id:haikage1755:20170812013715p:plain

Immersiveデバイスは現実のサーフェスの概念はなく,テーブルトップを表すオブジェクト (Vroom > TableThingy > Cube)には SpatialMapping physics layerがマークされ,Update内のRayはVirtualテーブルトップと衝突します。

f:id:haikage1755:20170812014009p:plain

進捗を楽しむ

 今回は島を移動できるようになりました。HoloLensでは島を実際のサーフェスに従って移動し,Immersiveデバイスでは追加したVirutalテーブルへ移動できます。

Mixed Reality 250 翻訳 概要から第1章まで

Mixed Reality 250

UWPの持つ柔軟性により、複数のデバイスにまたがったアプリケーションが簡単に作成できます。この柔軟性により、各デバイスの持つ強みを活かした体験を創ることができます。このチュートリアルでは、HoloLensとWindows Mixed Reality Immersive Headsetsの両方で実行される基本的な共有体験について説明します。このコンテンツは、もともとワシントン州シアトルで開催されたMicrosoft Build 2017で配信されました。

このチュートリアルで行うこと

UNETを使用してネットワークを設定する

  • MRデバイス間でホログラムを共有する
  • MRデバイスごとに、異なるアプリケーションのビューを確立する
  • HoloLensユーザーが簡単なパズルを通してImmersive Headset ユーザーを導く共有体験を創ります

前提条件

  • HoloLensデバイスとImmersive Headset
  • UDPが利用可能なネットワーク

 (Unityのセキュリティ設定も変更する必要があります。)
dhero.hatenablog.com

プロジェクトファイル

環境構築

  • Window 10 Creators Update

https://www.microsoft.com/software-download/windows10
f:id:haikage1755:20170719015414p:plain

http://dev.windows.com/downloads
f:id:haikage1755:20170719020356p:plain

https://www.microsoft.com/en-us/software-download/windowsinsiderpreviewSDK
f:id:haikage1755:20170719032920p:plain

  • Unity Mixed Reality Technical Preview (推奨はUnity2017.2 beta11以降です。それ以下のバージョンではHoloLens用の環境とImmersive用のUnity環境が異なります。)

https://unity3d.com/jp/unity/beta
f:id:haikage1755:20170719040106p:plain

  • Holographic Template (DirectXでの開発者用) ※今回は必要ありません

https://go.microsoft.com/fwlink/?linkid=852626
f:id:haikage1755:20170719083248p:plain

  • Graphics driversの更新

https://developer.microsoft.com/en-us/windows/mixed-reality/updating_your_gpu_driver
f:id:haikage1755:20170719083656p:plain

第一章 Holo World

youtu.be

目標

シンプルなプロジェクトで開発環境の準備ができているのかを確認する

ビルドするもの

HoloLensまたはImmersiveデバイスのいずれかにHologramを表示するアプリケーション

ステップ

  • Unityを開く
    • 開くを選択
    • プロジェクトファイルを展開した場所に移動する
    • 「フォルダの選択」をクリック
    • Unityが最初にプロジェクトを処理するには少々時間がかかります

f:id:haikage1755:20170719084316p:plain

  • UnityでMixed Realityが有効になっているかを確認する
    • ビルド設定ダイアログ(Ctrl + Shift + Bまたはファイル>ビルド設定...)を開きます。
    • Windows Storeを選択し、Switch Platformをクリックします

f:id:haikage1755:20170719084917p:plain

  • プレーヤーの設定を選択します。
  • 右のインスペクタで、[その他の設定]を展開します
  • 右のインスペクタで、[XR Settings]を展開します
  • Virtual Realityのボックスをチェックしてください
  • Windows Mixed RealityはVirtual Reality SDKでなければなりません

f:id:haikage1755:20170719085319p:plain

  • シーンを作成する
    • HierarchyでMain Cameraを右クリックし、Deleteを選択します。
    • HoloToolkit>Input>PrefabsからMixedRealityCameraParentをHierarchyにドラッグ

f:id:haikage1755:20170719085501p:plain

  • Hologramsをシーンに追加する
    • AppPrefabsのSkyboxをScene Viewにドラッグ
    • AppPrefabsのManagersをHierarchyにドラッグ
    • AppPrefabsのIslandをHierarchyにドラッグ

f:id:haikage1755:20170719090307p:plain

  • SaveとBuild
    • Save (Control+S または File > Save Scene)
    • これは新しいシーンのため、名前を付ける必要があります。

名前は特に問題ではありませんがSharedMixedRealityとします

f:id:haikage1755:20170719090536p:plain

  • Visual StudioにExportする
    • ビルドメニュー(Ctrl + Shift + Bまたはファイル/ビルド設定)を開く
    • Add Open Scenesをクリックする
    • Unity C# Projectsにチェックを入れる
    • SDKをHoloLensの場合は10.0.14393.0に、Immersiveの場合は10.0.15063.0を選択する
    • Buildをクリックする

f:id:haikage1755:20170719091638p:plain

現在、Unity2017.2の既知の問題として以上の設定ではビルドができないようです。
(C# Project のチェックを外すとビルドはできます)
beta8までを使っている場合に起きるエラーです。
f:id:haikage1755:20170720093151j:plain:h100

  • ファイルエクスプローラが開くので、新しくAppというフォルダを作成します (HoloLens、Immersiveの両方のアプリを作る場合は、フォルダを分けてください。例)HoloLensApp, ImmersiveAppなど)
  • Appフォルダを1回押します
  • フォルダを選択を押します
  • ビルドとコンパイルを待ちます

f:id:haikage1755:20170719091949p:plain

  • Visual Studioからのビルド
    • トップツールバーを使用して、ターゲットをReleaseとx86に変更する
      • HoloLensの場合は、ローカルマシンの横にある矢印をクリックし、デバイスを選択する
      • Immersiveの場合は、ローカルマシンを選択する
    • Mixed realityデバイスに展開するためにデバイスの横にある矢印をクリックし、Local Machineを選択する
    • アプリケーションをスタートするためにDebug->Start Without DebuggingまたはCtr+F5をクリックする

コードを理解する

プロジェクトで、Assets\HoloToolkit\Input\Scripts\Utilitiesをダブルクリックし、MixedRealityCameraManager.cs を開いてください。

        void Start()
        {
            if (HolographicSettings.IsDisplayOpaque)
            {
                //TrueならImmersiveHMDの設定を適応
                ApplySettingsForOpaqueDisplay();
            }
            else
            {
                //FalseならHoloLensの設定を適応
                ApplySettingsForTransparentDisplay();
            }
        }

概要:MixedRealityCameraManager.csはデバイスに応じてクオリティレベルとBackgound設定を適応する簡単なスクリプトです。重要な点はHolographicSettings.IsDisplayOpaqueであり,これはデバイスがHoloLensかImmersiveHMDかを判定します。(IsDisplayOpaqueはHoloLensならfalse,immersiveならtrueを返します)

進捗を楽しむ

この時点で、アプリケーションはHologramsを描画します。HologramへのInteractionは後程追加します。両デバイスは同じHologramを描画します。Immersiveデバイスではまた青い空と雲も描画します。

HoloLensのスリープ時間を変更する

HoloLensは既定の設定範囲だとスリープタイマーは最大30分ですが、それでもやっぱり煩わしいのでスリープしないようにしました

(参考)
forums.hololens.com

本来デバイスポータルの値は、REST API (https://developer.microsoft.com/en-us/windows/mixed-reality/device_portal_api_reference)を叩いて操作するのですが、電源周りはAPIから呼び出すことができません。

では何をPOSTしたら変更が加わるのか、少し見てみましょう。ChromeならF12でデベロッパーツールを立ち上げてください。
そして設定を変更してみます。
f:id:haikage1755:20170717211753p:plain

すると、どのようなRequestがとんできているか分かるはずです。
f:id:haikage1755:20170717212334p:plain

つまり、ここにあるRequestと同じようにPOSTしてやれば変更できるということですね。

なぜ僕はUnity側でこれを実装しようと思ったのか謎なのですが、まあ手軽に試せるという意味ではいいのかな(適当)

■ ヤり方

1.InspectorからデバイスポータルにアクセスするURL、ユーザー名、パスワードを入れる
2.シーン起動
3."S"ボタンを押す
4.デバイスポータルを更新し、次のようになっていたら成功

f:id:haikage1755:20170717210617p:plain

  [Tooltip("DevicePortal URL")]
    public string url = "127.0.0.1:10080";
    [Tooltip("DevicePortal UserName")]
    public string usr = "USR";
    [Tooltip("DevicePortal Password")]
    public string pass = "PASS";

  void Update () {
        if (Input.GetKeyDown(KeyCode.S))
        {
            StartCoroutine(OnSetVideoIdle());
            StartCoroutine(OnSetStanbyIdle());
        }
  }

    IEnumerator OnSetVideoIdle()
    {
        WWWForm form = new WWWForm();
        var req = UnityWebRequest.Post("http://" + url + "/api/power/cfg/SCHEME_CURRENT/SUB_SLEEP/VIDEOIDLE?ValueAC=0&ValueDC=0", form);
        req.SetRequestHeader("AUTHORIZATION", Authenticate(usr, pass));
        yield return req.Send();
    }

    IEnumerator OnSetStanbyIdle()
    {
        WWWForm form = new WWWForm();
        var req = UnityWebRequest.Post("http://" + url + "/api/power/cfg/SCHEME_CURRENT/SUB_SLEEP/STANDBYIDLE?ValueAC=0&ValueDC=0", form);
        req.SetRequestHeader("AUTHORIZATION", Authenticate(usr, pass));
        yield return req.Send();
    }

    string Authenticate(string username, string password)
    {
        string auth = username + ":" + password;
        auth = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(auth));
        auth = "Basic " + auth;
        return auth;
    }

HoloLensでUDP受信

最近HoloLensからマップを見て遊んでいます。

GPS情報はHoloLensから直接取得はできないため,スマホからUDPGPS情報をHoloLensに送りそこからマップ上の位置を取得するという方法をとっています。

以下にサンプルをあげてあります。
Sample.csをオブジェクトにアタッチするだけで動くようになっていますので,すぐに試せるかと思います。
github.com


注意点として,セキュリティ設定で通信を許可していないとEditor上で動かないので忘れずに設定してください

【未解決】Holographic Remoting中におけるPlatform defineの挙動

HoloLensで例えばawait/asyncといった処理を行いたい場合,UnityEditor上ではUWPが動作しないため,dllを用いたり以下のようにplatformごとの処理を書くといった手段を取ります

#if !UNITY_EDITOR
   // UWP処理
#else
   // Unity処理
#endif


しかしこの方法,実機では動作するのですが,Holographic Remoting 時に呼ぶのはUWPではなくUnity Scriptなのです

凹さんのご回答

【Unityでハマったところ】ThreadとWhileLoop

久しぶりに書くので,リハビリがてらプチ記事を書きますー

Unityでの非同期処理は色々方法がありますが,中でもThreadはよく使う方法だと思います。
以下は”Threadでループ処理する”…よくある例です。

Thread thread;

Start(){
  thread = new Thread();
  thread = new Thread(new ThreadStart(ThreadMethod));
  thread.Start();
}

void ThreadMethod(){
  while(true){
  Debug.Log("Called Cnt");
  }
}

void OnApplicationQuit(){
  if(thread!=null){
    thread.Abort();
  }
}

ですが,この内容で実行すると1回目は実行されども2回目に再生した時Editorがフリーズすると思います。
そこで1つ手を加え以下のようにします。

Thread thread;
bool isFinished = false;

Start(){
  thread = new Thread();
  thread = new Thread(new ThreadStart(ThreadMethod));
  thread.Start();
}

void ThreadMethod(){
  while(true){
    if(isFinished){
       break;
    }

  Debug.Log("Called Cnt");
  }
}

void OnApplicationQuit(){
  if(thread!=null){
     isFinished = true;
     thread.Abort();
  }
}

フラグを追加し,アプリケーション終了時にループ処理をbreakして抜けています。
おそらくこれでEditorが固まるという問題は回避できると思います。良きUnityライフを!

[研究者になりたかった青二才の戯言日記]は、Amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、Amazonアソシエイト・プログラムの参加者です。」