つくるの大好き。

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

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

リモートワークについてのありがたいお話 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チームとしては世の中に先行して場所と時間を乗り越えるノウハウを持ち、ナレッジを提供していければと思ってる)

事例

GitリポジトリをまたいだUnityプロジェクト間のソース共有

今日Twitter上でちょっと話題が出たUnityプロジェクト間でのソース共有について今運用している方法を書きます。

Unityプロジェクト間でのソース共有でよくあるのが下記パターンかなと思います。

  1. ファイルコピー
  2. Unityパッケージをエクスポート/インポート

いずれも人力なので事故りやすくちょっと21世紀のやり方じゃないような気がしますね(笑)
そこでgit submodule を使ってみました。

プロジェクトの構成

共有する方のUnityプロジェクトSharedLibと共有される方のUnityプロジェクトAppがあるとします。
それぞれは別のGitリポジトリに保存されているとします。

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

Unityプロジェクトを git submodule した際の問題点

この場合App/Assets配下のフォルダに普通にgit submodule するとSharedLibのProjectSettings等もpullしてしまうのでおかしなことになります。

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

特定のフォルダ配下のみを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することができるようになります。

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

最新状態に同期する

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 で行う方法を書きました。
今のところ無事故でうまくいっているのでよかったら参考にしてください。