Deep dive into the ARCore Depth Lab #1
ARCore Depth API を学ぶにはサンプルコードとして公開されている ARCore Depth Lab サンプルを見るのが良いのですが、このサンプルは完成度も高くコード量も多く、抽象化もされているのでどこで処理が繋がっているのかわかりにくいところもあります。それで、サクッと理解しようと思うと少し大変だと思うかも知れません。
ぼくが内部解析をした過程を書きますのでこれから学ぶという方の参考になれば嬉しいです!
なおこのサンプルはGoogle Play Storeからもダウンロード可能です。 単純に遊ぶだけでも楽しめる高度なサンプルプログラムなので、Deep Diveすると色々勉強になりました!
シーン構成
/Assets/ARRealismDemos/DemoCarousel/Scenes/DemoCarousel シーンがエントリポイントになります。
これはBuild Settingsより確認できます。
そして、いくつかのサブシーンからこのサンプルは構成されていることもわかります。
シーン切り替え
DemoCarousel シーンはサブシーンをロードするためのボタンを持つシーンです。
このボタンからどのシーンをロードするかは、ボタンのGameObject配下のquadにアタッチされているScene ButtonコンポーネントのScene Nameフィールドに文字列で指定されています。
ボタンのキャプションとロードされるシーン名は一致していないのですが、マッピングポイントはここです。
また、ボタンにマッピングされていないデモシーンも多数あるのでマッピングを書き換えればそれらを呼び出すこともできますね。
Depthデータハンドリング(浅め)
このサンプルでは DepthSource コンポーネントがアタッチされているオブジェクトがDepthデータを読み取り、DepthTargetコンポーネントをもつオブジェクトにDepthデータを送るようになっています。
DepthTargetコンポーネントは複数のオブジェクトにアタッチでき、全てに対してDepthデータがTextureとして毎フレーム更新されます。
このDepthSourceやARCoreのカメラを含む有用なオブジェクトは /Assets/ARRealismDemos/Common/Prefabs/DepthARComonents プレファブとして用意されていますので、通常のアプリ開発ではこのプレファブをシーンに配置し、Depthデータを受け取りたいオブジェクトにDepthTargetコンポーネントをアタッチするだけでTextureデータとしてDepthデータを受け取ることができます。
例えばDepthEffects シーンでは下記のようにDepthMapVisualizationオブジェクトにDepthTargetコンポーネントがアタッチされ、Textureデータを受け取るようになっています。
Depthデータハンドリング(深め)
もう少し深く分け入って、Depthデータをどのように取得しているのか見てみましょう。
DepthData処理の実装クラス
DepthSource.Start() では"DepthDataSourceConfig"という名前のリソースをロードしています。
arcore-depth-lab/DepthSource.cs at d56cafdb2421513dcefaeac82d6eded9e9089aa9 · googlesamples/arcore-depth-lab · GitHub
DepthDataSourceConfigを見ると、DepthSourceClassNameフィールドに、クラス名が文字列で指定されています。
この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に入れて動作させるまでの手順を書きます。
SunnySideUp UnityChan
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で動作します。
このプロジェクトにSunnySideUp UnityChanのURP版を導入してみましょう。
パッケージのインストール
SunnySideUp UnityChan URPプロジェクトは下記のパッケージを利用しています。 同じバージョンのパッケージをインストールしましょう。
ただし、この中で下記のものはSunnySideUp UnityChan URPプロジェクトのPackagesフォルダ内に含まれているので、そこからコピーしてインストールしました。
- Unity Chan Spring Bone
- Universal Toon Shader for URP
ビルドを通すための修正
Unity Chan Spring BoneはUWPプロジェクトを書き出すときに大量のビルドエラーを出します。
これはUnity Editor用のスクリプトがビルド対象に含まれているからです。
パッケージ内のAssemly Definitionの設定を下記のようにEditor限定に変更する必要があります。
なお、このビルドエラーは別プロジェクトでAndroid ARM64(IL2CPP)ビルドをした時にも発生し、同様の対処が必要でした。
Add packages · SystemFriend/HoloLensUnityChan@9caee43 · GitHub
SunnySideUp UnityChanアセットのインストール
依存関係はフォルダ内で完結しているので、UnityChanSSUフォルダを丸ごとインポートすればOKです。
HoloLensUnityChanプロジェクトはURP対応ではないので、ここで UnityChanSSU_DynCol プレファブをシーンに配置しても何もレンダリングされません。
ここから色々ありましたw
URPに切り替える
URPのプロファイルを新規作成します。
設定は軽めに。
[Project Settings]-[Graphics]-[Scriptable Render Pipeline Settings]にアタッチします。
これでSunnySideUp UnityChanがレンダリングされるようになりました。
既存ShaderからURP対応のShaderへ切り替える
プロジェクトのレンダリングがURPに切り替わったところで、レガシーShaderを使っているCandy Rock Star Unityちゃんは悲しみの紫になります。
二股を掛けることは許されないのです。
地道にCandy Rock Star UnityちゃんのShaderを "Universal Render Pipeline/Toon" へ切り替えてゆきます。
このShaderは設定がめちゃくちゃたくさんあります!
マニュアルは読んでいないのですがハイライトが強すぎてエッジが真っ白になってしまっていたのを下記を0にすることで対応しました。
また、髪の毛のシャドウ部分の色がグレーになっておかしいと思いましたので、ここだけ色を設定しました。
また、Directional Lightを使うと光が強すぎて白飛びするので削除してPoint Lightを設置しました。
上記諸々の変更がこちらのコミットです。
Add Unity-chan SSU · SystemFriend/HoloLensUnityChan@949ab70 · GitHub
Unity EditorのGameビューにレンダリングされない
ここでPlayすると、Gameビューに編集中は行われていたレンダリングが行われず真っ黒になります。
これは [Project Settings]-[Player]-[XR Settings]にあるレガシィなシステムでURPを使う場合起こる現象らしいです。
レガシィを卒業して新しいXR Plug-in Frameworkを使いなさいということなので切り替えます。
そしてMRTKも XRSDK の接頭辞がつくプロファイルへの切り替えが必要です。
大変な大工事になっています!
寝る前にちょっとやてみようと思って始めた作業ですが、既に午前3時ですよ。
Unity - Manual: XR Plug-in Framework
こちらはトライアンドエラーで設定を変えていったので、詳細はGitHub上のプロジェクトをご覧ください。
Using the Unity XR SDK · SystemFriend/HoloLensUnityChan@a7c2d37 · GitHub
なんとか移行できた
というわけで動きました。
URPの設定見直しでパフォーマンスは少し良くなった。#HoloLens2 #unitychan pic.twitter.com/emMLNAkvZp
— Satoshi Maemoto (@peugeot106s16) 2020年7月12日
下記課題は残りました。
引き続き取り組んでゆきたいので良い情報があればぜひ教えてください。
- 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が使えるようになりました。 早速ハックしてみたいと思います(笑)
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データが格納されます。
Shader内でdepth値を求める
ここまでの解説をまとめると、ARCore Depth API for Unityのdepthデータ値の表現とRGB565テクスチャへのマッピングは下記となっています。
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です(笑)
まとめ
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_ 君がヒントをくれました! 確かにこの手があったか~。シンプルで素敵。
これ、ButtonCofigついているオブジェクトにButtonConfigのOnClickをリッスンするコンポーネントつけて、UnityEvent<GameObject>にラップしてイベント飛ばすとかだと微妙です?
— Decoc (@deco_c_) 2020年6月6日
こちらのケースもサンプルプロジェクトに追加しました。
問題
OnClick()イベントはパラメータを持たないので送信元の情報などは一切通知されません。
(senderが欲しいだけだったのですがかなり調べることにww)
case01: Post detect
サンプルプロジェクトの構成
- MRTK v2.4.0 を利用
https://github.com/microsoft/MixedRealityToolkit-Unity/releases/tag/v2.4.0 - ProfileはHoloLens2用
- Menu上に3つのボタンが配置されている
- それぞれのボタンのButtonConfigHelper.OnClick() はすべて同一のイベントハンドラにリンクされている
- イベントハンドラではクリックされたボタンの名称をツールチップに表示する
プロジェクト一式
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; } }
こんな感じに動きます。
case02: Event wrap
サンプルプロジェクトの構成
- MRTK v2.4.0 を利用
https://github.com/microsoft/MixedRealityToolkit-Unity/releases/tag/v2.4.0 - ProfileはHoloLens2用
- Menu上に3つのボタンが配置されている
- それぞれのボタンにButtonConfigHelper.OnClick() を拡張する自作コンポーネントをアタッチし、アプリケーションはそのOnClickedにハンドラをリンクしている
- イベントハンドラではクリックされたボタンの名称をツールチップに表示する
解決策
各ボタンにアタッチした 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); }); } }
こんな感じにcase01と同様に動きます。
MRTK Button Sender Sample take2
まとめ
複数UIコンポーネントでイベントハンドラを共有するという構成はコードをシンプルにするためによく使われるものだと思うのでSDKにきっと機能が用意されてているに違いないと探しまくったのですがどうやら無さそうなのでひと手間必要そうです(笑)
UnityネイティブプラグインをVisual Studio でステップ実行デバッグする
Unityのネイティブプラグインを開発していると、Unityで実行中にプラグイン内部のコードや変数の状態をデバッグしたくなる時がよくありますよね。
これは簡単な手順で行うことができるので手順を書いておきます。
デバッグ実行できる条件
つまり自作プラグインやオープンソースのものであればデバッグ可能です。
反対に .dllファイルだけ提供されているようなプラグインはデバッグできないということです。
デバッグ実行するための手順
ネイティブプラグインをDebugビルドする
Debug構成でプラグインをビルドします。
ビルドが成功すると .dllファイルと .pdbファイルが出力されます。
Unityのプラグインフォルダに必要なファイルを配置する
.dllファイルと .pdbファイルをUnity内のPluginsフォルダに配置します。
Visual StidoからUnityにアタッチする
Unityプロジェクトを開いたのち、プラグインプロジェクトを開いているVisual Studio の [デバッグ]-[プロセスにアタッチ]メニューを選択します。
"プロセスにアタッチ"ダイアログが表示されるのでUnityのプロセスを選択して[アタッチ]します。
プロセスはたくさん列挙されますが"unity"で絞り込めば見つけやすいです。
ブレークポイントの設定を行う
ステップ実行したい部分にブレークポイントを設定しましょう。
UnityでPlayする
この状態でUnityでPlayすると、なんということでしょう。
プラグイン側のソースコード上のブレークポイントが効きましたね。
もちろん、普通にステップ実行ができますし、変数の内容もウォッチすることができます。
ブレークポイントが効かない時は
プラグインプロジェクト側とUnity側で .dllファイルと .pdbファイルのバージョンが一致していないことが原因として考えられます。
再ビルドとUnityへのファイル更新を再度行ったり、Unityの再起動を行ってみてください。
まとめ
これでプラグインを絡めてUnityアプリケーションを開発するスピードが爆上がりですね(笑)!
Microsoft MVP を再受賞しました!
今年も Microsot MVP Awardを再受賞することができました!
カテゴリーはWindows Development でMixed Reality分野が引き続き主な活動領域です。
ぼくが活動の指針としているのは、 高度な技術をギークのためだけではなく、一般の方々や子どもたちでも親しめるかたちにして知ってもらう という点です。
2019年度も引き続きこの指針で活動してゆこうと思っています。
今年はAzure KinectやHoloLens 2も出てくるので楽しい1年になりそうです。
子どももやんちゃ盛りでなかなかまとまった活動時間は取れませんが、それはそれ。その中での気づきやソリューションにきっと出会えるはず。
できる範囲でまた楽しくがんばります。
HoloLensにサインをもらった時の保護塗装について
今年も 5/29, 30日に「de:code 2019」 に参加しました!
2年前に続き、HoloLensの産みの親、Alex Kipmanがこの日本にやってきてくれました。
これは本当に貴重な事です!
そしてぼくたちHoloLensコミュニティのために特別に直接面会する機会を設けてくれ、なんとHoloLensにサインを頂いてしまいました!
サインを頂いたら保護しましょう
この貴重なサイン、できるだけ綺麗な状態を保ちたいですよね。
手が良く触れる場所なのでぜひ保護塗装をしましょう。
1. 必要なもの
- マスキングテープ
- HoloLensを包み込めるサイズの袋
- 艶消しクリアーのスプレー塗料
2. 塗装面は綺麗に
塗装面が油等で汚れていると塗料の乗りが悪くなりますので、乾いた布やティッシュ等で綺麗にしましょう。
3. マスキング
マスキングテープを使ってキッチリとマスキングを行います。ふちが浮かないように気を付けましょう。
また、塗料がグラス面やHoloLens内部に入り込まないように塗装面を除き、全体をカバーします。
今回はちょうどde:code2019の紙袋があったので使ってみました。
(これは上質な紙が使われている関係で硬かったので、あまりマスキング向きではありませんでした(笑))
4. 吹く
スプレーは思い切って一気にムラなく塗ることが大切です。弱気でもムラになりますし、強気すぎるとタレが発生するので気を付けましょう。
スプレー缶はよく振って、練習で試し吹きして感じを掴みましょう。
さあでは気持ちを決めたら、慎重かつ大胆に吹いてしましましょう!
塗装面全体に均一に塗料が乗れば成功です!
5. 乾燥!乾燥!乾燥!
乾燥はとても大切な工程です。塗装が済んだら触らずじっくりと乾燥させましょう。
この時空気が湿っていると時間がかかりますし気泡が発生しやすいので、そもそも塗装作業は好天が続く空気の乾燥した時に行うのがおすすめです。
では、しっかり乾燥させたらマスキングを取りましょう!
はい!ちょっと乱暴にマスキングを取ってしまったので見事に左上の塗膜がはがれてしましましたー!!
乾燥時間を1日しかとらなかったのも敗因かもしれませんw
というわけでじっくり乾燥させる事と慎重なマスキングはがしは本当に大切です。皆さん気を付けましょう(笑)
なお、すこしふちがけば立っていますが、しばらくするとなじんだり削れたりして目立たなくなってゆきます。
まとめ
というわけで、きっと世界に2台とないAlex KipmanダブルサインのHoloLensの保全作業が完了です。