つくるの大好き。

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

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年になりそうです。
子どももやんちゃ盛りでなかなかまとまった活動時間は取れませんが、それはそれ。その中での気づきやソリューションにきっと出会えるはず。
できる範囲でまた楽しくがんばります。

HoloLensにサインをもらった時の保護塗装について

今年も 5/29, 30日に「de:code 2019」 に参加しました!

www.microsoft.com

2年前に続き、HoloLensの産みの親、Alex Kipmanがこの日本にやってきてくれました。
これは本当に貴重な事です! f:id:peugeot-106-s16:20190605165644j:plain そしてぼくたちHoloLensコミュニティのために特別に直接面会する機会を設けてくれ、なんとHoloLensにサインを頂いてしまいました! f:id:peugeot-106-s16:20190605165829j:plain

サインを頂いたら保護しましょう

この貴重なサイン、できるだけ綺麗な状態を保ちたいですよね。
手が良く触れる場所なのでぜひ保護塗装をしましょう。

1. 必要なもの

  • マスキングテープ
  • HoloLensを包み込めるサイズの袋
  • 艶消しクリアーのスプレー塗料

f:id:peugeot-106-s16:20190605170240j:plain f:id:peugeot-106-s16:20190605170300j:plain

2. 塗装面は綺麗に

塗装面が油等で汚れていると塗料の乗りが悪くなりますので、乾いた布やティッシュ等で綺麗にしましょう。

3. マスキング

マスキングテープを使ってキッチリとマスキングを行います。ふちが浮かないように気を付けましょう。 f:id:peugeot-106-s16:20190605170546j:plain f:id:peugeot-106-s16:20190605170606j:plain

また、塗料がグラス面やHoloLens内部に入り込まないように塗装面を除き、全体をカバーします。
今回はちょうどde:code2019の紙袋があったので使ってみました。
(これは上質な紙が使われている関係で硬かったので、あまりマスキング向きではありませんでした(笑))
f:id:peugeot-106-s16:20190605170855j:plain

4. 吹く

スプレーは思い切って一気にムラなく塗ることが大切です。弱気でもムラになりますし、強気すぎるとタレが発生するので気を付けましょう。
スプレー缶はよく振って、練習で試し吹きして感じを掴みましょう。


HoloLens塗装 大

さあでは気持ちを決めたら、慎重かつ大胆に吹いてしましましょう!
塗装面全体に均一に塗料が乗れば成功です! f:id:peugeot-106-s16:20190605171942j:plain

5. 乾燥!乾燥!乾燥!

乾燥はとても大切な工程です。塗装が済んだら触らずじっくりと乾燥させましょう。
この時空気が湿っていると時間がかかりますし気泡が発生しやすいので、そもそも塗装作業は好天が続く空気の乾燥した時に行うのがおすすめです。

では、しっかり乾燥させたらマスキングを取りましょう!
f:id:peugeot-106-s16:20190605172505j:plain はい!ちょっと乱暴にマスキングを取ってしまったので見事に左上の塗膜がはがれてしましましたー!!
乾燥時間を1日しかとらなかったのも敗因かもしれませんw

というわけでじっくり乾燥させる事と慎重なマスキングはがしは本当に大切です。皆さん気を付けましょう(笑)

なお、すこしふちがけば立っていますが、しばらくするとなじんだり削れたりして目立たなくなってゆきます。

まとめ

というわけで、きっと世界に2台とないAlex KipmanダブルサインのHoloLensの保全作業が完了です。


IMG 0875