つくるの大好き。

つくるのが大好きな人の記録。

Azure Spatial Anchors のすぐビルドできるプロジェクト

f:id:peugeot-106-s16:20201206115603p:plain

What's?

Azure Spatial Anchors の勉強をしたのですが、チュートリアルをなぞってビルドしてゆくにはそれなりに時間がかかって大変でした(笑)
すぐビルドできるプロジェクトを公開したので良かったらお使いください。

下記が特徴です。

  • アカウント情報を設定すればすぐビルドできます。
  • UWP(Windows Mixed Reality), Android with ARCore, iOS with ARKit の3プラットフォームに対し、プラットフォーム切り替えだけでビルドでき、動作します
  • Coarse Relocalizationに対応しています
    (うまく動かせていないプラットフォームもあります。リポジトリのReadmeを参照ください)
  • 操作がシンプルなので、営業してくれる人に渡すデモアプリ等に使うと便利です(生々しいw)

github.com

Demo videos

参考リンク

宮浦さん、ありがとうございます! qiita.com

Deep dive into the ARCore Depth Lab #1

ARCore Depth API を学ぶにはサンプルコードとして公開されている ARCore Depth Lab サンプルを見るのが良いのですが、このサンプルは完成度も高くコード量も多く、抽象化もされているのでどこで処理が繋がっているのかわかりにくいところもあります。それで、サクッと理解しようと思うと少し大変だと思うかも知れません。
ぼくが内部解析をした過程を書きますのでこれから学ぶという方の参考になれば嬉しいです!

github.com

なおこのサンプルはGoogle Play Storeからもダウンロード可能です。 単純に遊ぶだけでも楽しめる高度なサンプルプログラムなので、Deep Diveすると色々勉強になりました!

play.google.com

シーン構成

/Assets/ARRealismDemos/DemoCarousel/Scenes/DemoCarousel シーンがエントリポイントになります。
これはBuild Settingsより確認できます。
そして、いくつかのサブシーンからこのサンプルは構成されていることもわかります。

f:id:peugeot-106-s16:20200726103056p:plain

シーン切り替え

DemoCarousel シーンはサブシーンをロードするためのボタンを持つシーンです。 f:id:peugeot-106-s16:20200726103943p:plain

このボタンからどのシーンをロードするかは、ボタンのGameObject配下のquadにアタッチされているScene ButtonコンポーネントのScene Nameフィールドに文字列で指定されています。
ボタンのキャプションとロードされるシーン名は一致していないのですが、マッピングポイントはここです。

f:id:peugeot-106-s16:20200726104417p:plain

また、ボタンにマッピングされていないデモシーンも多数あるのでマッピングを書き換えればそれらを呼び出すこともできますね。

Depthデータハンドリング(浅め)

このサンプルでは DepthSource コンポーネントがアタッチされているオブジェクトがDepthデータを読み取り、DepthTargetコンポーネントをもつオブジェクトにDepthデータを送るようになっています。
DepthTargetコンポーネントは複数のオブジェクトにアタッチでき、全てに対してDepthデータがTextureとして毎フレーム更新されます。

このDepthSourceやARCoreのカメラを含む有用なオブジェクトは /Assets/ARRealismDemos/Common/Prefabs/DepthARComonents プレファブとして用意されていますので、通常のアプリ開発ではこのプレファブをシーンに配置し、Depthデータを受け取りたいオブジェクトにDepthTargetコンポーネントをアタッチするだけでTextureデータとしてDepthデータを受け取ることができます。

例えばDepthEffects シーンでは下記のようにDepthMapVisualizationオブジェクトにDepthTargetコンポーネントがアタッチされ、Textureデータを受け取るようになっています。
f:id:peugeot-106-s16:20200726111115p:plain

Depthデータハンドリング(深め)

もう少し深く分け入って、Depthデータをどのように取得しているのか見てみましょう。

DepthData処理の実装クラス

DepthSource.Start() では"DepthDataSourceConfig"という名前のリソースをロードしています。
arcore-depth-lab/DepthSource.cs at d56cafdb2421513dcefaeac82d6eded9e9089aa9 · googlesamples/arcore-depth-lab · GitHub

DepthDataSourceConfigを見ると、DepthSourceClassNameフィールドに、クラス名が文字列で指定されています。 f:id:peugeot-106-s16:20200726111740p:plain
このMotionStereoDepthDataSourceクラスがDepthデータ取得を行う実装クラスとなります。
(ここを差し替えればダミーデータを差し込んだり、全く違うデバイスのDepthデータを突っ込めたり、、ワクワクしてきたw)

よって低レベルよりの処理はこちらを読んでゆけば読み解くことができます。

arcore-depth-lab/MotionStereoDepthDataSource.cs at master · googlesamples/arcore-depth-lab · GitHub

つづく

というところで、これから出かけるで今回はここまでです(笑)
次回、MotionStereoDepthDataSource.csやShader周りにDeep dive してゆきたいと思います!
需要があれば、またお会いしましょうw!

SunnySideUp UnityChan を HoloLens2 の既存プロジェクトに入れて動かしてみた

SunnySideUp UnityChanがリリースされたというニュースを見てさっそく試してみました!
PCでは当たり前に動くのでHoloLens2に入れて動作させるまでの手順を書きます。

f:id:peugeot-106-s16:20200712171402p:plain

SunnySideUp UnityChan

unity-chan.com

UnityのHDRP (High Definition Render Pipeline)とURP(Universal Render Pipeline)によるレンダリングに対応した新世代のUnityちゃんアセットです。
HDRPはPC等のハイパフォーマンスな環境用、URPは様々なデバイスで動作できる高い互換性とクオリティを併せ持つレンダリングシステムです。

新世代というのも、HDRPやURPに切り替えると既存のシェーダーはそのまま使えません。全部悲しみの紫になります。
移行するには清水の舞台から飛び降りる覚悟が要りそうですが、魅力的なアセットが使えるとなれば飛べる気がする方も多いのではないでしょうか!

既存プロジェクトの更新作業

ベースプロジェクトはこちら。Candy Rock Star Unityちゃんが現実世界に出現するというぼくが始めてHoloLensで作ったアプリです。
4年以上前に作ったものですが、地道に最新環境にあわせてアップデートされており、MRTK2.4+HoloLens2で動作します。

github.com

このプロジェクトにSunnySideUp UnityChanのURP版を導入してみましょう。

パッケージのインストール

SunnySideUp UnityChan URPプロジェクトは下記のパッケージを利用しています。 同じバージョンのパッケージをインストールしましょう。

f:id:peugeot-106-s16:20200712162223p:plain

ただし、この中で下記のものはSunnySideUp UnityChan URPプロジェクトのPackagesフォルダ内に含まれているので、そこからコピーしてインストールしました。

  • Unity Chan Spring Bone
  • Universal Toon Shader for URP

ビルドを通すための修正
Unity Chan Spring BoneはUWPプロジェクトを書き出すときに大量のビルドエラーを出します。
これはUnity Editor用のスクリプトがビルド対象に含まれているからです。
パッケージ内のAssemly Definitionの設定を下記のようにEditor限定に変更する必要があります。

f:id:peugeot-106-s16:20200712171848p:plain

なお、このビルドエラーは別プロジェクトでAndroid ARM64(IL2CPP)ビルドをした時にも発生し、同様の対処が必要でした。

Add packages · SystemFriend/HoloLensUnityChan@9caee43 · GitHub

SunnySideUp UnityChanアセットのインストール

依存関係はフォルダ内で完結しているので、UnityChanSSUフォルダを丸ごとインポートすればOKです。

HoloLensUnityChanプロジェクトはURP対応ではないので、ここで UnityChanSSU_DynCol プレファブをシーンに配置しても何もレンダリングされません。
ここから色々ありましたw

URPに切り替える

URPのプロファイルを新規作成します。 f:id:peugeot-106-s16:20200712163650p:plain

設定は軽めに。   f:id:peugeot-106-s16:20200712163823p:plain

[Project Settings]-[Graphics]-[Scriptable Render Pipeline Settings]にアタッチします。   f:id:peugeot-106-s16:20200712164018p:plain

これでSunnySideUp UnityChanがレンダリングされるようになりました。

f:id:peugeot-106-s16:20200712164308p:plain

既存ShaderからURP対応のShaderへ切り替える

プロジェクトのレンダリングがURPに切り替わったところで、レガシーShaderを使っているCandy Rock Star Unityちゃんは悲しみの紫になります。
二股を掛けることは許されないのです。

地道にCandy Rock Star UnityちゃんのShaderを "Universal Render Pipeline/Toon" へ切り替えてゆきます。
このShaderは設定がめちゃくちゃたくさんあります!
マニュアルは読んでいないのですがハイライトが強すぎてエッジが真っ白になってしまっていたのを下記を0にすることで対応しました。

f:id:peugeot-106-s16:20200712165028p:plain

また、髪の毛のシャドウ部分の色がグレーになっておかしいと思いましたので、ここだけ色を設定しました。 f:id:peugeot-106-s16:20200712165148p:plain

また、Directional Lightを使うと光が強すぎて白飛びするので削除してPoint Lightを設置しました。

上記諸々の変更がこちらのコミットです。

Add Unity-chan SSU · SystemFriend/HoloLensUnityChan@949ab70 · GitHub

Unity EditorのGameビューにレンダリングされない

ここでPlayすると、Gameビューに編集中は行われていたレンダリングが行われず真っ黒になります。
これは [Project Settings]-[Player]-[XR Settings]にあるレガシィなシステムでURPを使う場合起こる現象らしいです。

https://forum.unity.com/threads/urp-in-hololens-app-changes-perspective-of-camera-in-game-view.874522/

レガシィを卒業して新しいXR Plug-in Frameworkを使いなさいということなので切り替えます。
そしてMRTKも XRSDK の接頭辞がつくプロファイルへの切り替えが必要です。
大変な大工事になっています!
寝る前にちょっとやてみようと思って始めた作業ですが、既に午前3時ですよ。

Unity - Manual: XR Plug-in Framework

こちらはトライアンドエラーで設定を変えていったので、詳細はGitHub上のプロジェクトをご覧ください。

Using the Unity XR SDK · SystemFriend/HoloLensUnityChan@a7c2d37 · GitHub

なんとか移行できた

というわけで動きました。

下記課題は残りました。
引き続き取り組んでゆきたいので良い情報があればぜひ教えてください。

  • MRTKのShaderがURPに切り替えと全て悲しみの紫になる。
  • HandInputが動いていないっぽい
  • HandMenuが息をしていないっぽい
  • キャラクターの足裏の高さが、Spatial Awareness で認識している床面より高い位置になっているっぽい
  • アプリのパフォーマンスは移行前と比べるとかなり重い

お知らせ

SunnySideUp UnityChan について詳しい説明がCGWORLD (シージーワールド) 2020年 08月号に掲載されているそうなので良かったらぜひ!
amzn.to

ARCore Depth API for Unity でdepth値をShaderで扱う

ARCore v1.18.0がリリースされ、Depth APIが使えるようになりました。 早速ハックしてみたいと思います(笑)

f:id:peugeot-106-s16:20200628225056j:plain

ARCore Depth API for Unityの説明はこちら developers.google.com

Depthデータへのアクセス

https://developers.google.com/ar/develop/unity/depth/overview#depth-maps

Frame.CameraImage.UpdateDepthTexture() から取得することができます。 Texture2Dでの提供です。

なお、CPUでデータを扱いたい場合はこちらが使えます。
https://github.com/google-ar/arcore-unity-sdk/blob/fbc21676ef0bb305c775fa3be593b8c46a62c211/Assets/GoogleARCore/SDK/Scripts/Api/Wrappers/FrameApi.cs#L352-L353

Depthデータのフォーマット

https://developers.google.com/ar/develop/unity/depth/overview#depth-map-format

ARCore Depth APIのデータフォーマットはDEPTH16形式と呼ばれるもので、13bitのdepth値(mm単位)と3bitのconfidence値(信頼性)を組み合わせた16bit値です。
ただしARCore Depth APIではconfidenceは常に0がセットされるようなので取得できないようです。

https://developer.android.com/reference/android/graphics/ImageFormat#DEPTH16

Textureデータのフォーマット

Frame.CameraImage.UpdateDepthTexture() で取得できるTextureのフォーマットはRGB565形式で16bitデータが格納されます。

https://github.com/google-ar/arcore-unity-sdk/blob/fbc21676ef0bb305c775fa3be593b8c46a62c211/Assets/GoogleARCore/SDK/Scripts/Api/Wrappers/FrameApi.cs#L269-L270

Shader内でdepth値を求める

ここまでの解説をまとめると、ARCore Depth API for Unityのdepthデータ値の表現とRGB565テクスチャへのマッピングは下記となっています。 f:id:peugeot-106-s16:20200628222732p:plain

RGB565テクスチャのピクセルはr, g, bのフィールドからそれぞれ範囲(0.0-1.0)の数値として取得できるので、整数値に変換し、ビットシフトして合計すれば元の16bit値を復元することができます。

抜粋するとこんな感じです。

#define ARCORE_MAX_DEPTH_MM 8191.0
#define ARCORE_FLOAT_TO_5BITS 31        // (0.0, 1.0) -> (0, 31)
#define ARCORE_FLOAT_TO_6BITS 63        // (0.0, 1.0) -> (0, 63)
#define ARCORE_RGB565_RED_SHIFT 2048    // left shift 11 bits
#define ARCORE_RGB565_GREEN_SHIFT 32    // left shift 5 bits

float depth = (pixel.r * ARCORE_FLOAT_TO_5BITS * ARCORE_RGB565_RED_SHIFT)
    + (pixel.g * ARCORE_FLOAT_TO_6BITS * ARCORE_RGB565_GREEN_SHIFT)
    + (pixel.b * ARCORE_FLOAT_TO_5BITS);
depth = min(depth, ARCORE_MAX_DEPTH_MM);
  • Rを0-31範囲に変換し、11bitシフトする
  • Gを0-64範囲に変換し、5bitシフトする
  • Bを0-31範囲に変換する
  • 3つの数値を足し合わせる
  • ARCore Depth API では8メートルまでの距離を扱うことができるので8191を超えた値は8191に収める

とすることで、depth値を求めることができます。

なお、実はこの処理はARCoreDepth.cginc 内で ArCoreDepth_GetMeters() として用意されているので、普通はこちらを使えばOKです(笑)

https://github.com/google-ar/arcore-unity-sdk/blob/fbc21676ef0bb305c775fa3be593b8c46a62c211/Assets/GoogleARCore/SDK/Materials/ARCoreDepth.cginc#L45-L55

まとめ

ARCore Depth API for Unity で取得できるdepthフォーマットとShader内でのデータ変換について書きました。

しかし、このぼんやりとしたdepthはどうにかならないんでしょうか。。w (フィルタ無しのRAWデータ触りたい)

MRTK v2.4.0 ボタンクリックイベントの送信元を取得する (Update)

しばらくアウトプットする暇も取れない感じでしたが久しぶりの投稿(小ネタ)です!

MRTKのUIコンポーネントを利用し、複数ボタンのOnClickイベントを同一のイベントハンドラにリンクさせました。
この時OnClickイベントはイベント送信元の情報をパラメータで渡すようにはなっていないため、どのボタンからイベントが送信されたか判断できず困ってしまいました。
ボタンごとにイベントハンドラを書くなんて嫌ですよね笑!

解決した方法をサンプルプロジェクトとしてシェアします。
(もっとスマートな方法が用意されているんじゃないのかなぁ。。)

2020/06/07 Update

すぐにホロラボの @deco_c_ 君がヒントをくれました! 確かにこの手があったか~。シンプルで素敵。

こちらのケースもサンプルプロジェクトに追加しました。

問題

OnClick()イベントはパラメータを持たないので送信元の情報などは一切通知されません。
(senderが欲しいだけだったのですがかなり調べることにww)

f:id:peugeot-106-s16:20200606225317p:plain https://github.com/microsoft/MixedRealityToolkit-Unity/blob/72ea542dff0ab5cbd229d9c93a4f138f37172cc5/Assets/MRTK/SDK/Features/UX/Scripts/Buttons/ButtonConfigHelper.cs#L87-L99

case01: Post detect

サンプルプロジェクトの構成

f:id:peugeot-106-s16:20200606223602p:plain
Multi buttons click event are linked to an event handler

プロジェクト一式
github.com

解決策

イベントハンドラ内でFocusProviderよりフォーカスが当たっているInteractableを取得する。
そこにたどり着くまでに色々とステップがある(下記 GetEventSender() メソッド参照)

using Microsoft.MixedReality.Toolkit;
using Microsoft.MixedReality.Toolkit.UI;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    public ToolTip toolTip;

    public void ButtonClicked()
    {
        this.toolTip.ToolTipText = this.GetEventSender()?.name;
    }

    private GameObject GetEventSender()
    {
        foreach (var inputSource in CoreServices.InputSystem.DetectedInputSources)
        {
            foreach (var pointer in inputSource.Pointers)
            {
                if (pointer.IsInteractionEnabled)
                {
                    var sender = CoreServices.InputSystem.FocusProvider.GetFocusedObject(pointer);
                    if (sender?.GetComponent<Interactable>() != null)
                    {
                        return sender;
                    }
                }
            }
        }
        return null;
    }
}

https://github.com/satoshi-maemoto/MRTKButtonSenderSample/blob/master/Assets/Scripts/NewBehaviourScript.cs

こんな感じに動きます。


MRTK Button Sender Sample

case02: Event wrap

サンプルプロジェクトの構成

f:id:peugeot-106-s16:20200607110420p:plain
OnClick extender are attached to each buttons

解決策

各ボタンにアタッチした ClickEventRouter コンポーネントが、ButtonConfigHelper.OnClickイベントをフックして自オブジェクトをパラメータに追加したかたちでInvoke()する。

using Microsoft.MixedReality.Toolkit.UI;
using System;
using UnityEngine;
using UnityEngine.Events;

public class ClickEventRouter : MonoBehaviour
{
    [Serializable]
    public class InternalButtonClickedEvent : UnityEvent<GameObject> { }

    public InternalButtonClickedEvent OnClick;

    void Start()
    {
        this.GetComponent<ButtonConfigHelper>()?.OnClick.AddListener(() =>
        {
            this.OnClick?.Invoke(this.gameObject);
        });
    }
}

https://github.com/satoshi-maemoto/MRTKButtonSenderSample/blob/master/Assets/Scripts/ClickEventRouter.cs

こんな感じにcase01と同様に動きます。


MRTK Button Sender Sample take2

まとめ

複数UIコンポーネントイベントハンドラを共有するという構成はコードをシンプルにするためによく使われるものだと思うのでSDKにきっと機能が用意されてているに違いないと探しまくったのですがどうやら無さそうなのでひと手間必要そうです(笑)

UnityネイティブプラグインをVisual Studio でステップ実行デバッグする

Unityのネイティブプラグインを開発していると、Unityで実行中にプラグイン内部のコードや変数の状態をデバッグしたくなる時がよくありますよね。
これは簡単な手順で行うことができるので手順を書いておきます。

デバッグ実行できる条件

つまり自作プラグインオープンソースのものであればデバッグ可能です。
反対に .dllファイルだけ提供されているようなプラグインデバッグできないということです。

デバッグ実行するための手順

ネイティブプラグインをDebugビルドする

Debug構成でプラグインをビルドします。 f:id:peugeot-106-s16:20190730153551p:plain

ビルドが成功すると .dllファイルと .pdbファイルが出力されます。 f:id:peugeot-106-s16:20190730153721p:plain

Unityのプラグインフォルダに必要なファイルを配置する

.dllファイルと .pdbファイルをUnity内のPluginsフォルダに配置します。 f:id:peugeot-106-s16:20190730153944p:plain

Visual StidoからUnityにアタッチする

Unityプロジェクトを開いたのち、プラグインプロジェクトを開いているVisual Studio の [デバッグ]-[プロセスにアタッチ]メニューを選択します。
"プロセスにアタッチ"ダイアログが表示されるのでUnityのプロセスを選択して[アタッチ]します。
プロセスはたくさん列挙されますが"unity"で絞り込めば見つけやすいです。 f:id:peugeot-106-s16:20190730154349p:plain

ブレークポイントの設定を行う

ステップ実行したい部分にブレークポイントを設定しましょう。

UnityでPlayする

この状態でUnityでPlayすると、なんということでしょう。
プラグイン側のソースコード上のブレークポイントが効きましたね。
もちろん、普通にステップ実行ができますし、変数の内容もウォッチすることができます。 f:id:peugeot-106-s16:20190730155029p:plain

ブレークポイントが効かない時は

プラグインプロジェクト側とUnity側で .dllファイルと .pdbファイルのバージョンが一致していないことが原因として考えられます。
再ビルドとUnityへのファイル更新を再度行ったり、Unityの再起動を行ってみてください。

まとめ

これでプラグインを絡めてUnityアプリケーションを開発するスピードが爆上がりですね(笑)!

Microsoft MVP を再受賞しました!

今年も Microsot MVP Awardを再受賞することができました!
カテゴリーはWindows Development でMixed Reality分野が引き続き主な活動領域です。

f:id:peugeot-106-s16:20190704150300p:plain
MVP Logo

mvp.microsoft.com

mvp.microsoft.com

ぼくが活動の指針としているのは、 高度な技術をギークのためだけではなく、一般の方々や子どもたちでも親しめるかたちにして知ってもらう という点です。
2019年度も引き続きこの指針で活動してゆこうと思っています。

今年はAzure KinectやHoloLens 2も出てくるので楽しい1年になりそうです。
子どももやんちゃ盛りでなかなかまとまった活動時間は取れませんが、それはそれ。その中での気づきやソリューションにきっと出会えるはず。
できる範囲でまた楽しくがんばります。