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の保全作業が完了です。
リモートワークについてのありがたいお話 for 朝礼
はじめに
持ち回りで朝礼でありがたいお話をすることになっているので、明日の話をつくりました。
社内向けの構成ですがせっかくなので公開しておきます。
なお、明日は会社に行けそうにないのでリモートで話しますw
RemoteWork について
社会的に
https://ideasforgood.jp/2019/03/18/remote-work-eco/
- CO2削減
平均的な会社員の仕事関連の二酸化炭素排出量の90%以上は、通勤で占められている - 電力消費量削減
従業員は、会社にいるときよりも自宅にいる時のほうが電力消費に敏感になる ごみ削減
自宅で働く人は会社で働く人に比べ、テイクアウトをせずに自分で朝食や昼食を作る傾向があり、包装ごみの削減に貢献する参考:東京都への通勤人口は300万人くらい http://www.metro.tokyo.jp/tosei/hodohappyo/press/2018/03/20/12.html
この規模でエネルギーと時間が節約されたら、、しゅごい、、
会社的に
- 東京オフィス狭いw
全員の常設のデスクだけでいっぱいで広い場所が必要なxR系の開発には不向き - 海外との仕事が増えるとそもそも時間は合わないので同じ場所、時間に集まるということは減っていくであろう
→シアトルにいるときはホテルで就寝前に翌日のデイタイムを生きている日本のチームと仕事して実感w
個人的に
- 子どもが小さいうちはできるだけ一緒に時間を過ごしたい
- お子様の急な発熱ははんぱないよ!
→無用に家から離れるというリスクを減らしたい
導入の課題
- 場所と時間の縛りが消えるので 成果縛り になる
- というわけで向いてる人と向いてない人がいる
→(リモートワークはうまくできる人がひとつのツールとして使うものだと思ってる)
ひとつの解決策: xR
- xRの価値ってなに?
→ 場所と時間を超越できること
(xRチームとしては世の中に先行して場所と時間を乗り越えるノウハウを持ち、ナレッジを提供していければと思ってる)
事例
NEUTRANS : https://neutrans.space/
VR空間で協働できるサービス
ご近所五反田のSynamonさんがやってる。VRChat: https://twitter.com/kohack_v/status/958213238545526784
VR空間で仕事してる坪倉さんにリアルアバターで会いに行ってみました。爺ちゃんにまた会うアプリ https://twitter.com/holoadventure/status/1082563304768163840
故人の生前のデータをもとに再会こんにちわ赤ちゃんノマド
http://satoshi-maemoto.hatenablog.com/entry/2017/12/23/122813
いつでも誕生の瞬間に駆けつけられるように広島県内2か月にわたる海と山、ネットカフェから奥さんの実家にまたがるフルノマドに挑戦。様々な知見を得た。
なんでも聞いてください!
GitリポジトリをまたいだUnityプロジェクト間のソース共有
今日Twitter上でちょっと話題が出たUnityプロジェクト間でのソース共有について今運用している方法を書きます。
Unityアプリのソース管理ってどうしてますか?
— 中村 薫 (@kaorun55) January 30, 2019
サブモジュール的な共通機能をどうやって管理してるのかわからん。
Unityプロジェクト間でのソース共有でよくあるのが下記パターンかなと思います。
- ファイルコピー
- Unityパッケージをエクスポート/インポート
いずれも人力なので事故りやすくちょっと21世紀のやり方じゃないような気がしますね(笑)
そこでgit submodule を使ってみました。
プロジェクトの構成
共有する方のUnityプロジェクトSharedLibと共有される方のUnityプロジェクトAppがあるとします。
それぞれは別のGitリポジトリに保存されているとします。
Unityプロジェクトを git submodule した際の問題点
この場合App/Assets配下のフォルダに普通にgit submodule するとSharedLibのProjectSettings等もpullしてしまうのでおかしなことになります。
特定のフォルダ配下のみをpullする設定
git submoduleすると共に特定のフォルダ配下のみをpullする設定 "core.sparsecheckout" を有効にし、指定フォルダ名を設定ファイルに書き込んだあとpullします。 この一連の処理は開発メンバー1人1人がローカルで行う必要があるので、下記のようなバッチファイルを作って共有しました。
git submodule add --force https://github.com/HogeHoge/SharedLib.git Assets/SharedLib git commit -m "add module" cd Assets/SharedLib git config core.sparsecheckout true echo /Assets/SharedLib/ > ../../.git/modules/Assets/SharedLib/info/sparse-checkout git read-tree -mu HEAD
こうすると必要なファイルだけpullすることができるようになります。
最新状態に同期する
git submoduleというのはリポジトリのあるcommitに対してリンクを貼るものなので、SharedLibリポジトリにその後入った変更が自動的にAppリポジトリに反映されることはありません。
最新版に同期したいときは下記のコマンドを利用します。
これも開発メンバーが個々にローカルで行う必要があるのでバッチファイルにして配布しています。
git submodule foreach git pull origin master
このバッチを走らせてね~!っていう通達は人力なので「勝手にやらない」「やるときはみんなやる」っていうのを気を付けないといけないといえばいけないですが、勝手にしても自分にビルドエラーが出るくらいの軽傷で済むのではないでしょうか。
ちなみにバッチファイルはAppリポジトリに含めることで配布し、README.mdに説明を書いておきました。
便利なバッチファイルを作りました。
- AddModule.bat - submoduleを追加します。1度だけ使ってください。
- UpdateModule.bat - 最新のFrameworkに更新します。何度でも使えます。
まとめ
GitリポジトリをまたいだUnityプロジェクト間のソース共有を git submodule で行う方法を書きました。
今のところ無事故でうまくいっているのでよかったら参考にしてください。