まずARDKの概要情報です
Lightship ARDKとは、「Pokemon Go」で知られるNianticが2021年11月8日から正式に提供を開始したUnityベースのARアプリ開発キットです。この記事ではその機能や実装してみて気づいたことなどをまとめています。
基本的には下記のドキュメントに乗っている情報を基に書いております。
https://lightship.dev/docs/
ARDKの機能要約
1. Mapping
-
- カラー画像入力から、カメラと映ったモノの距離を深層学習で推定。LiDAR深度データは使用されない
-
オクリュージョン
深度を基に障害物があったら、3Dモデルを隠す
2. Understanding
-
セマンティック・セグメンテーション
- 画像内の特定の領域にクラスラベルを割り当てる。ピクセルごとにラベルを取得可能。
index | Channel名 | メモ |
---|---|---|
0 | sky | 雲が含まれています。霧は含まれていません。 |
1 | ground | 人工地盤(下)のすべてのものと、土、草、砂、およびその他の自然地盤タイプが含まれます。 植生や葉が多い地面は地面として検出されず、代わりに葉として検出されます。 |
2 | artificial_ground | 道路、歩道、線路、カーペット、敷物、フローリング、小道、砂利、およびいくつかの競技場が含まれます。 |
3 | water | 川、海、池、湖、プール、滝、いくつかの水たまりが含まれています。飲料水、カップ、ボウル、シンク、バスの水は含まれません。 反射の強い水は水として検出されない場合があります。 |
4 | building | 近代的および伝統的な住宅および商業ビルが含まれます。 壁と同義と見なされるべきではありません。 |
5 | foliage | 茂み、低木、木の緑豊かな部分、鉢植えの植物、花が含まれています。 |
6 | grass | 背の高い草ではなく、芝生などの芝生の地面。 |
3. Sharing
4. 開発ツール
Virtual Studio
アプリのビルド・デプロイに時間を費やさないようにテストするための環境
- Mock Mode
仮想で部屋を作り、その部屋で開発したARのテストを行う
- Remote Mode
スマホとPCを接続し、スマホのカメラで得た映像や深度を用いて、ARのテストを行う
リリース予定
料金体系
実装例
次は実装してみて重要そうだなと思ったことの羅列です
Virtual StudioのRemote Mode構築手順
Remote Feed Appのインストール
-
ARDKExamples/VirtualStudio/ARDK Remote Feed App.Sceneだけをビルド
- Build SettingsでAutoconnectProfiler, Develop Buildをチェック
- Remote Feed Appをインストール
Remote Feed AppとテストしたいSceneを開いたUnity Editorの接続
- PCとスマホを接続
- ARSessionを開始しているテストすべきSceneを開く
- Profilerでデバイスを選択
- Virtual StudioでRemote → USBを選択
- スマホでRemote Feed Appを開き、USBを選択する
- 接続に成功すれば[Connected!]と表示される
- Unity Editorでテストしたい挙動を行う
MeshとSemantic Segmentation
Mesh
実装例
Semantic Segmentation
実装例
- AR Semantic Segmentation ManagerをARSceneCameraプレハブに追加
- 下記のようなスクリプトを記述
// Update is called once per frame
void Update()
{
if (PlatformAgnosticInput.touchCount <= 0) { return; }
var touch = PlatformAgnosticInput.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
int x = (int)touch.position.x;
int y = (int)touch.position.y;
DebugLogSemanticsAt(x, y);
}
}
//examples of all the functions you can use to interogate the procider/biffers
void DebugLogSemanticsAt(int x, int y)
{
string[] channelsNamesInPixel = semanticManager.SemanticBufferProcessor.GetChannelNamesAt(x, y);
foreach (var i in channelsNamesInPixel)
{
Debug.Log($"{i}");
}
}
}
}
HLAPIとLocalization
メンバー同士での空間データの重ね合わせ
生成
位置同期
//NetworkBehaviourを継承しているクラスでの関数です
protected override void SetupSession(out Action initializer, out int order)
{
initializer = () =>
{
var auth = Owner.Auth;
var descriptor = auth.AuthorityToObserverDescriptor(TransportType.UnreliableUnordered);
// UnreliableBroadcastTransformPackerを作成するだけで、
// Transform同期のためのブロードキャスト/受信が設定されます
new UnreliableBroadcastTransformPacker
(
"netTransform",
transform,
descriptor,
TransformPiece.Position,
Owner.Group
);
};
order = 0;
}
- TransformType
- Relieable → 99.95%の信頼性を保証, UnRelieable → 99%で受け取らなかったメッセージはドロップ
- UnreliableUnorderedとUnreliableOrdered, ReliableUnordered とReliableOrderedがある
メッセージの送受信
- 実装例
void PerformActionForAllPeers(IMultipeerNetworking networking, byte[] gameData)
{
networking.BroadcastData(GameActionMessageTags.PerformSomeGameAction, gameData, TransportType.UnreliableUnordered);
}
void SubscribeToPeerDataReceived(IMultipeerNetworking networking)
{
networking.PeerDataReceived += OnPeerDataReceived;
}
// Since the Hlapi also uses byte messages, this will be fired each time an object is NetworkSpawned as well
void OnPeerDataReceived(PeerDataReceivedArgs args)
{
// Check that it is a message we care about, rather than an Hlapi message
if(args.Tag != GameActionMessageTags.PerformSomeGameAction)
{
return;
}
// We now know this is the message that this class cares about, so use the data to perform some update
}
※タグはイベントデータであるPeerDataRecievedArgsの中で指定できるuint
private void OnDidConnect(ConnectedArgs connectedArgs)
{
// ...
// Continuing method from second code snippet...
_hitStreamReplicator =
new MessageStreamReplicator<Vector3>
(
"hitMessageStream",
_arNetworking.Networking.AnyToAnyDescriptor(TransportType.ReliableOrdered),
group
);
_hitStreamReplicator.MessageReceived +=
(args) =>
{
Debug.Log("Ball was hit");
if (_auth.LocalRole != Role.Authority)
return;
_ballBehaviour.Hit(args.Message);
};
}
HLAPIの詳細(データの送受信の際に起きていることなど)
その他注意点
-
精度
- 下記動画のスマホのように位置のずれはあった
-
通信
- サーバーメッセージの遅延は数百ms ~ 数千msになるが、ピアツーピアメッセージの遅延は数十msになる場合がある(デバイスの近さによって異なる)。
- 下記の間にピアAからピアBにメッセージを送信した場合、ピアBはピアAをまだ有効なピアとしておらず、メッセージをドロップする。そのためPeerAddedイベントの度、数フレーム後に初期化メッセージが発生するようにするべき。
- ピアA ← ピアBからPeerAddedイベントを受信
ピアB ← ピアAからPeerAddedイベントをまだ受信していない
- ピアA ← ピアBからPeerAddedイベントを受信
- 現在、同じピアとしてセッションに再参加することはサポートされていない。再参加した場合、デバイスは完全に新しいピアと見なされる。
- ホストがセッションを離れる場合、新ホストは選択されない。ただし、ホストが検出したマップはセッションのタイムアウトまで残り、新しいピアは残ったマップに対してローカライズできる。
- 全ピアが退出した後、30秒後にタイムアウトする。この時セッションに再参加しようとすると、意図しない動作が発生する可能性がある。
おまけ①: 2021年11月30日現在、難しかったこと
※ドキュメントでの記述がないことなので、真偽不明です
-
WindowsのUnityでビルド + XCodeでiPhoneにインストールしようとするとエラー
→MacのUnityでビルド + XCodeでインストールすると大丈夫だった -
Virtual Studioはどちらのモードでもリアルタイム・メッシュ機能が使えなかった
→Depth機能とSemantic Segmentation, ネットワーク機能は使えた -
URPのままVirtual Studio・Mock Modeにすると、仮想環境と認識させるためにARDK_MockWorldレイヤーを設定しているGameObject全てが見えなくなってしまった
→その他ピンクシェーダーになる部分が多く、やはりまだ使うべきではなさそう
おまけ②: その他
- Q. マルチプレイのメッセージ容量100MBで大きい。どう実装しているか。
- A.
- メッセージ
- byte[] 10MB
- 永続的なKey,Value
- Key → string 4KB, Value → byte[] 100MB
- セッションがアクティブな限り、サーバーに保存されるデータ
- 更新されると各デバイスでイベントが発火する
- セッション開始時に全員が得るべきデータを保存するなどの目的で使う
- 実装例
- ※データ制限について
- メッセージ
- A.