C++/NDK で Azure Spatial Anchors を使用してアンカーを作成して配置する方法
Azure Spatial Anchors を使用して、世界中の異なるデバイス間でアンカーを共有できます。 これは複数の異なる開発環境をサポートしています。 この記事では、Azure Spatial Anchors SDK を C++/NDK で使用する方法について詳しく説明します。次のことを行います。
- Azure Spatial Anchors セッションを正しく設定して管理する。
- ローカル アンカーのプロパティを作成して設定する。
- それらをクラウドにアップロードする。
- クラウドの空間アンカーを検索して削除する。
前提条件
このガイドを完了するには、次のことが必要です。
- 「Azure Spatial Anchors の概要」を読んでいる。
- 5 分間のクイック スタートのいずれかを完了している。
- C++ と Android ネイティブ開発キットについての基本的な知識。
- ARCore についての基本的な知識。
セッションの初期化
SDK のメイン エントリ ポイントは、セッションを表すクラスです。 通常は、ビューとネイティブ AR セッションを管理するクラス内のフィールドを宣言します。
CloudSpatialAnchorSession 構造体の詳細を確認してください。
std::shared_ptr<CloudSpatialAnchorSession> cloudSession_;
// In your view handler
cloudSession_ = std::make_shared<CloudSpatialAnchorSession>();
認証の設定
サービスにアクセスするには、アカウント キー、アクセス トークン、または Microsoft Entra 認証トークンを提供する必要があります。 この詳細については、認証の概念に関するページも参照してください。
アカウント キー
アカウント キーは、アプリケーションが Azure Spatial Anchors サービスで認証できるようにするための資格情報です。 アカウント キーの使用目的は、すぐに開始できるようにサポートすることです。 特にアプリケーションの Azure Spatial Anchors との統合の開発フェーズ時などがこれに該当します。 そのようなものとして、開発中にクライアント アプリケーションにアカウント キーを埋め込んで使用できます。 開発の先の段階に進むときには、本番稼働レベルであるか、アクセス トークンによりサポートされるか、または Microsoft Entra ユーザー認証である認証メカニズムに移行することが強く推奨されます。 開発のためにアカウント キーを取得するには、Azure Spatial Anchors アカウントにアクセスし、[キー] タブに移動します。
SessionConfiguration 構造体の詳細を確認してください。
auto configuration = cloudSession_->Configuration();
configuration->AccountKey(R"(MyAccountKey)");
アクセス トークン
アクセス トークンは、Azure Spatial Anchors で認証するためのより堅牢な方法です。 特に運用環境デプロイメントのアプリケーションを準備するきにはそのように言えます。 このアプローチの概要は、クライアント アプリケーションが安全に認証できるバックエンド サービスを設定することです。 バック エンド サービスは、実行時に AAD と連動し、Azure Spatial Anchors の Secure Token Service と連動してアクセス トークンを要求します。 このトークンは、クライアント アプリケーションに配信され、SDK で Azure Spatial Anchors で認証するために使用されます。
auto configuration = cloudSession_->Configuration();
configuration->AccessToken(R"(MyAccessToken)");
アクセス トークンが設定されていない場合は、TokenRequired
イベントを処理するか、デリゲート プロトコルに tokenRequired
メソッドを実装する必要があります。
イベント引数のプロパティを設定することで、イベントを同期的に処理できます。
TokenRequiredDelegate デリゲートの詳細を確認してください。
auto accessTokenRequiredToken = cloudSession_->TokenRequired([](auto&&, auto&& args) {
args->AccessToken(R"(MyAccessToken)");
});
ハンドラーで非同期操作を実行する必要がある場合は、次の例のように deferral
オブジェクトを要求してこれを完了することによって、トークンの設定を延期することができます。
auto accessTokenRequiredToken = cloudSession_->TokenRequired([this](auto&&, auto&& args) {
std::shared_ptr<CloudSpatialAnchorSessionDeferral> deferral = args->GetDeferral();
MyGetTokenAsync([&deferral, &args](std::string const& myToken) {
if (myToken != nullptr) args->AccessToken(myToken);
deferral->Complete();
});
});
Microsoft Entra 認証
Azure Spatial Anchors を使うと、アプリケーションはユーザーの Microsoft Entra ID (Active Directory) トークンでも認証できるようになります。 たとえば、Microsoft Entra トークンを使って Azure Spatial Anchors と統合することができます。 企業が Microsoft Entra ID でユーザーを管理している場合は、Azure Spatial Anchors SDK でユーザーの Microsoft Entra トークンを提供できます。 これにより、同じ Microsoft Entra テナントの一部であるアカウントについて、Azure Spatial Anchors サービスに対して直接認証できるようになります。
auto configuration = cloudSession_->Configuration();
configuration->AuthenticationToken(R"(MyAuthenticationToken)");
アクセス トークンの場合と同様に、Microsoft Entra トークンが設定されていない場合は、TokenRequired イベントを処理するか、デリゲート プロトコルに tokenRequired メソッドを実装する必要があります。
イベント引数のプロパティを設定することで、イベントを同期的に処理できます。
auto accessTokenRequiredToken = cloudSession_->AccessTokenRequired([](auto&&, auto&& args) {
args->AuthenticationToken(R"(MyAuthenticationToken)");
});
ハンドラーで非同期操作を実行する必要がある場合は、次の例のように deferral
オブジェクトを要求してこれを完了することによって、トークンの設定を延期することができます。
auto accessTokenRequiredToken = cloudSession_->TokenRequired([this](auto&&, auto&& args) {
std::shared_ptr<CloudSpatialAnchorSessionDeferral> deferral = args->GetDeferral();
MyGetTokenAsync([&deferral, &args](std::string const& myToken) {
if (myToken != nullptr) args->AuthenticationToken(myToken);
deferral->Complete();
});
});
セッションの設定
セッションにおいて環境データを処理できるようにするには、Start()
を呼び出します。
セッションによって発生したイベントを処理するには、イベント ハンドラーをアタッチします。
Start メソッドの詳細を確認してください。
cloudSession_->Session(ar_session_);
cloudSession_->Start();
セッションにフレームを提供する
空間アンカー セッションは、ユーザーの周りに空白をマップすることによって機能します。 そうすることで、アンカーの場所を決定しやすくなります。 モバイル プラットフォーム (iOS と Android) には、プラットフォームの AR ライブラリからフレームを取得するためにカメラ フィードへのネイティブ呼び出しが必要です。 これに対し、HoloLens は環境を継続的にスキャンするため、モバイル プラットフォームの場合と異なり特定の呼び出しは必要ありません。
ProcessFrame メソッドの詳細を確認してください。
cloudSession_->ProcessFrame(ar_frame_);
ユーザーへのフィードバックの提供
セッションが更新されるイベントを処理するために、コードを記述できます。 セッションによるユーザーの環境の理解が深まるたびに、このイベントが発生します。 これにより、次のことが可能になります。
UserFeedback
クラスを使用して、デバイスが移動し、セッションで環境の把握状況が変わったら、ユーザーにフィードバックを提供する。 これを行うには、次の手順を実行します。- 空間アンカーを作成するのに十分な追跡済み空間データを確保できるのはどの時点かを判断する。 これは
ReadyForCreateProgress
またはRecommendedForCreateProgress
で判断します。ReadyForCreateProgress
が 1 を超えたら、クラウド空間アンカーを保存するのに十分なデータが得られています。ただし、RecommendedForCreateProgress
が 1 を超えるまで待機することをお勧めします。
SessionUpdatedDelegate デリゲートの詳細を確認してください。
auto sessionUpdatedToken = cloudSession_->SessionUpdated([this](auto&&, auto&& args) {
auto status = args->Status();
if (status->UserFeedback() == SessionUserFeedback::None) return;
std::ostringstream str;
str << std::fixed << std::setw(2) << std::setprecision(0)
<< R"(Feedback: )" << FeedbackToString(status.UserFeedback()) << R"( -)"
<< R"( Recommend Create=)" << (status->RecommendedForCreateProgress() * 100) << R"(%)";
feedback_ = str.str();
});
クラウド空間アンカーの作成
クラウド空間アンカーを作成するには、最初にプラットフォームの AR システムにアンカーを作成した後、対応するアンカーをクラウド上に作成します。 CreateAnchorAsync()
メソッドを使用します。
CloudSpatialAnchor 構造体の詳細を確認してください。
// Create a local anchor, perhaps by hit-testing and creating an ARAnchor
ArAnchor* localAnchor;
ArHitResultList* hit_result_list = nullptr;
ArHitResultList_create(ar_session_, &hit_result_list);
CHECK(hit_result_list);
ArFrame_hitTest(ar_session_, ar_frame_, 0.5, 0.5, hit_result_list);
int32_t hit_result_list_size = 0;
ArHitResultList_getSize(ar_session_, hit_result_list, &hit_result_list_size);
if (hit_result_list_size == 0) {
ArHitResultList_destroy(hit_result_list);
return;
}
ArHitResult* ar_hit = nullptr;
ArHitResult_create(ar_session_, &ar_hit);
// The hitTest method sorts the resulting list by increasing distance from the camera
// The first hit result will usually be the most relevant when responding to user input
ArHitResultList_getItem(ar_session_, hit_result_list, 0, ar_hit);
if (ArHitResult_acquireNewAnchor(ar_session_, ar_hit, &localAnchor) != AR_SUCCESS) return;
ArTrackingState tracking_state = AR_TRACKING_STATE_STOPPED;
ArAnchor_getTrackingState(ar_session_, localAnchor, &tracking_state);
if (tracking_state != AR_TRACKING_STATE_TRACKING) {
ArAnchor_release(localAnchor);
ArHitResult_destroy(ar_hit);
return;
}
ArHitResult_destroy(ar_hit);
ar_hit = nullptr;
ArHitResultList_destroy(hit_result_list);
hit_result_list = nullptr;
// If the user is placing some application content in their environment,
// you might show content at this anchor for a while, then save when
// the user confirms placement.
std::shared_ptr<CloudSpatialAnchor> cloudAnchor = std::make_shared<CloudSpatialAnchor>();
cloudAnchor->LocalAnchor(localAnchor);
cloudSession_->CreateAnchorAsync(cloudAnchor, [this, cloudAnchor](Status status) {
std::ostringstream str;
if (status != Status::OK) {
str << "Save Failed: " << std::to_string(static_cast<uint32_t>(status));
feedback_ = str.str();
return;
}
str << R"(Created a cloud anchor with ID=)" << cloudAnchor->Identifier();
feedback_ = str.str();
});
前述のように、新しいクラウド空間アンカーの作成を試みる前に、十分な環境データが取得されている必要があります。 これは、ReadyForCreateProgress
が 1 を超えている必要があることを意味しますが、Microsoft では RecommendedForCreateProgress
が 1 を超えるまで待機することをお勧めします。
GetSessionStatusAsync メソッドの詳細を確認してください。
cloudSession_->GetSessionStatusAsync([this](Status status, const std::shared_ptr<SessionStatus>& value) {
if (status != Status::OK) {
std::ostringstream str;
str << "Session status error: " << std::to_string(static_cast<uint32_t>(status));
feedback_ = str.str();
return;
}
if (value->RecommendedForCreateProgress() < 1.0f) return;
// Issue the creation request ...
});
プロパティの設定
クラウド空間アンカーを保存するときには、いくつかのプロパティを追加することを選択できます。 これは保存されるオブジェクトの型や、相互作用に対して有効にすべきかどうかなどの基本プロパティなどです。 そのようにすると検出時に役立つ場合があります。空のコンテンツの画像フレームなど、ユーザー向けのオブジェクトをすぐにレンダリングできます。 次に、バックグラウンドでの別のダウンロードにより、フレームに表示されるピクチャなどの、追加の状態の詳細が取得されます。
AppProperties メソッドの詳細を確認してください。
std::shared_ptr<CloudSpatialAnchor> cloudAnchor = std::make_shared<CloudSpatialAnchor>();
cloudAnchor->LocalAnchor(localAnchor);
auto properties = cloudAnchor->AppProperties();
properties->Insert(R"(model-type)", R"(frame)");
properties->Insert(R"(label)", R"(my latest picture)");
cloudSession_->CreateAnchorAsync(cloudAnchor, [this, cloudAnchor](Status status) {
// ...
});
プロパティを更新する
アンカーでプロパティを更新するには、UpdateAnchorProperties()
メソッドを使用します。 2 つ以上のデバイスが同じアンカーのプロパティを同時に更新する場合は、オプティミスティック同時実行制御モデルを使用します。 これは、最初の書き込みが優先されることを意味します。 その他のすべての書き込みには、「同時実行」エラーが発生します。再試行する前に、プロパティの更新が必要になります。
UpdateAnchorPropertiesAsync メソッドの詳細を確認してください。
std::shared_ptr<CloudSpatialAnchor> anchor = /* locate your anchor */;
auto properties = anchor->AppProperties();
properties->Insert(R"(last-user-access)", R"(just now)");
cloudSession_->UpdateAnchorPropertiesAsync(anchor, [this](Status status) {
if (status != Status::OK) {
std::ostringstream str;
str << "Updating Properties Failed: " << std::to_string(static_cast<uint32_t>(status));
feedback_ = str.str();
}
});
アンカーがサービス上で作成された後は、アンカーの位置を更新することはできません。新しい位置を追跡するには、新しいアンカーを作成し、古いアンカーを削除する必要があります。
プロパティを更新するためにアンカーを探知する必要がない場合は、CloudSpatialAnchor
オブジェクトをプロパティと共に返す GetAnchorPropertiesAsync()
メソッドを使用できます。
GetAnchorPropertiesAsync メソッドの詳細を確認してください。
cloudSession_->GetAnchorPropertiesAsync(R"(anchorId)", [this](Status status, const std::shared_ptr<CloudSpatialAnchor>& anchor) {
if (status != Status::OK) {
std::ostringstream str;
str << "Getting Properties Failed: " << std::to_string(static_cast<uint32_t>(status));
feedback_ = str.str();
return;
}
if (anchor != nullptr) {
auto properties = anchor->AppProperties();
properties->Lookup(R"(last-user-access)") = R"(just now)";
cloudSession_->UpdateAnchorPropertiesAsync(anchor, [this](Status status) {
// ...
});
}
});
有効期限の設定
未来の特定の日付で有効期限が自動的に切れるようにアンカーを構成することもできます。 有効期限が切れたアンカーは、特定も更新もできなくなります。 有効期限を設定できるのはアンカーを作成しているときだけです。それをクラウドに保存する前に設定してください。 後で有効期限を更新することはできません。 アンカーの作成中に有効期限が設定されていない場合、アンカーは手動で削除されたときにのみ有効期限が切れます。
Expiration メソッドの詳細を確認してください。
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::chrono::system_clock::time_point oneWeekFromNow = now + std::chrono::hours(7 * 24);
const int64_t oneWeekFromNowUnixEpochTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(oneWeekFromNow.time_since_epoch()).count();
cloudAnchor->Expiration(oneWeekFromNowUnixEpochTimeMs);
クラウド空間アンカーを見つける
Azure Spatial Anchors を使用する主な理由の 1 つに、以前に保存したクラウド空間アンカーを検索できることが挙げられます。 このためには、"Watcher" を使用しています。 一度に使用できる Watcher は 1 つだけです。複数の Watcher はサポートされていません。 Watcher を使用してクラウド空間アンカーを見つける方法はいくつかあります (アンカー検索戦略とも呼ばれます)。 Watcher では一度に 1 つの方法を使用することができます。
注意
アンカーを配置するたびに、Azure Spatial Anchors は、収集された環境データを使用して、アンカーのビジュアル情報を増強しようとします。 アンカーを配置するのに問題がある場合は、アンカーを作成した後、さまざまな角度や照明条件から何度か配置してみるといいでしょう。
クラウド空間アンカーを識別子で検索する場合は、クラウド空間アンカー識別子をアプリケーションのバックエンド サービスに格納しておけば、それに対して適切に認証できるすべてのデバイスからアクセスできます。 この例のついては、「チュートリアル: デバイス間で空間アンカーを共有する」をご覧ください。
AnchorLocateCriteria
オブジェクトをインスタンス化し、検索する識別子を設定します。次に AnchorLocateCriteria
を指定することでセッション上の CreateWatcher
メソッドを呼び出します。
CreateWatcher メソッドの詳細を確認してください。
auto criteria = std::make_shared<AnchorLocateCriteria>();
criteria->Identifiers({ R"(id1)", R"(id2)", R"(id3)" });
auto cloudSpatialAnchorWatcher = cloudSession_->CreateWatcher(criteria);
監視が作成された後、要求されたすべてのアンカーに対して AnchorLocated
イベントが発生します。 このイベントは、アンカーが探知されたとき、またはアンカーを探知できなかった場合に発生します。 このような状況が発生した場合は、その理由が状態に示されます。 監視のすべてのアンカーが (探知されたかどうかを問わず) 処理された後、LocateAnchorsCompleted
イベントが発生します。 識別子の上限は、Watcher 1 つあたり 35 個です。
AnchorLocated デリゲートの詳細を確認してください。
auto anchorLocatedToken = cloudSession_->AnchorLocated([this](auto&&, auto&& args) {
switch (args->Status()) {
case LocateAnchorStatus::Located: {
std::shared_ptr<CloudSpatialAnchor> foundAnchor = args->Anchor();
// Go add your anchor to the scene...
}
break;
case LocateAnchorStatus::AlreadyTracked:
// This anchor has already been reported and is being tracked
break;
case LocateAnchorStatus::NotLocatedAnchorDoesNotExist:
// The anchor was deleted or never existed in the first place
// Drop it, or show UI to ask user to anchor the content anew
break;
case LocateAnchorStatus::NotLocated:
// The anchor hasn't been found given the location data
// The user might in the wrong location, or maybe more data will help
// Show UI to tell user to keep looking around
break;
}
});
アンカーの削除
使用されなくなったアンカーの削除は、開発のプロセスおよびプラクティスに早い段階で取り入れて、常時 Azure リソースをクリーンアップすることをお勧めします。
DeleteAnchorAsync メソッドの詳細を確認してください。
cloudSession_->DeleteAnchorAsync(cloudAnchor, [this](Status status) {
// Perform any processing you may want when delete finishes
});
セッションの一時停止、リセット、停止
セッションを一時的に停止するために、Stop()
を呼び出すことができます。 そうすることで、ProcessFrame()
を呼び出した場合でも、あらゆるウォッチャーや環境処理を停止できます。 次に Start()
を呼び出して、処理を再開できます。 再開するとき、既にセッションでキャプチャされた環境データは保持されます。
Stop メソッドの詳細を確認してください。
cloudSession_->Stop();
セッションでキャプチャされた環境データをリセットするために、Reset()
を呼び出すことができます。
Reset メソッドの詳細を確認してください。
cloudSession_->Reset();
セッションがすべての参照を解放した後に適切にクリーンアップするようにします。
cloudSession_ = nullptr;
次のステップ
このガイドでは、Azure Spatial Anchors SDK を使用してアンカーを作成および探知する方法について学習しました。 アンカーのリレーションシップの詳細については、次のガイドに進んでください。