Создание и поиск привязок с помощью Пространственных привязок Azure в C++ (WinRT)

Пространственные привязки Azure позволяют вам совместно использовать привязки на различных устройствах. Служба поддерживает несколько сред разработки. Из этой статьи вы узнаете, как использовать пакет SDK Пространственных привязок Azure в C++/WinRT, чтобы выполнять следующие действия:

  • правильно настраивать сеанс Пространственных привязок Azure и управлять им;
  • создавать и настраивать свойства в локальных привязках;
  • передавать их в облако;
  • обнаруживать и удалять пространственные привязки в облаке.

Необходимые компоненты

Для работы с руководством вам потребуется следующее:

Cross Platform

Инициализация сеанса

Основной точкой входа для пакета SDK является класс, представляющий ваш сеанс. Обычно нужно объявить поле в классе, который управляет представлением и нативным сеансом AR.

Дополнительные сведения см. в статье о классе CloudSpatialAnchorSession.

    CloudSpatialAnchorSession m_cloudSession{ nullptr };

    m_cloudSession = CloudSpatialAnchorSession();

Настройка проверки подлинности

Чтобы получить доступ к службе, необходимо предоставить ключ учетной записи, маркер доступа или маркер проверки подлинности Microsoft Entra. Дополнительные сведения об этом можно также получить на странице с описанием концепции проверки подлинности.

Ключи учетных записей

Ключи учетных записей — это учетные данные, позволяющие приложению проходить проверку подлинности в службе "Пространственные привязки Azure". Назначение ключей учетных записей — помочь быстро приступить к работе. Это важно на этапе разработки интеграции приложения с Пространственными привязками Azure. Таким образом, вы можете использовать ключи учетной записи, внедряя их в клиентские приложения во время разработки. По мере развития настоятельно рекомендуется перейти к механизму проверки подлинности, который является рабочим уровнем, поддерживаемым маркерами доступа или проверкой подлинности пользователя Microsoft Entra. Чтобы получить ключ учетной записи для разработки, откройте учетную запись Пространственных привязок Azure и перейдите на вкладку "Ключи".

Дополнительные сведения см. в статье о классе SessionConfiguration.

    auto configuration = m_cloudSession.Configuration();
    configuration.AccountKey(LR"(MyAccountKey)");

Маркеры доступа

Маркеры доступа — это более надежный метод проверки подлинности с помощью Пространственных привязок Azure. Такой метод наиболее актуален при подготовке приложения к рабочему развертыванию. Краткое изложение этого подхода заключается в настройке внутренней службы, с помощью которой клиентское приложение может безопасно выполнять проверку подлинности. Внутренняя служба взаимодействует с AAD во время выполнения и со службой маркера безопасности Пространственных привязок Azure для запроса маркера доступа. Затем этот маркер доставляется клиентскому приложению и используется в пакете SDK для проверки подлинности с помощью Пространственных привязок Azure.

    auto configuration = m_cloudSession.Configuration();
    configuration.AccessToken(LR"(MyAccessToken)");

Если маркер доступа не задан, необходимо выполнить обработку события TokenRequired или реализовать метод tokenRequired для протокола делегирования.

Событие можно обрабатывать синхронно, задав свойство аргументов события.

Дополнительные сведения см. в статье о делегате TokenRequiredDelegate.

    m_accessTokenRequiredToken = m_cloudSession.TokenRequired(winrt::auto_revoke, [](auto&&, auto&& args) {
        args.AccessToken(LR"(MyAccessToken)");
    });

Если нужно выполнить асинхронные операции в обработчике, можете отложить установку маркера, запросив объект deferral, а затем завершить установку, как показано в следующем примере.

    m_accessTokenRequiredToken = m_cloudSession.TokenRequired(winrt::auto_revoke, [this](auto&&, auto&& args) {
        auto deferral = args.GetDeferral();
        MyGetTokenAsync([&deferral, &args](winrt::hstring const& myToken) {
            if (!myToken.empty()) args.AccessToken(myToken);
            deferral.Complete();
        });
    });

Проверка подлинности Microsoft Entra

Пространственные привязки Azure также позволяют приложениям проходить проверку подлинности с помощью маркеров Microsoft Entra ID (Active Directory). Например, можно использовать маркеры Microsoft Entra для интеграции с пространственными привязками Azure. Если корпоративный поддерживает пользователей в идентификаторе Microsoft Entra, вы можете предоставить маркер Microsoft Entra в пакете SDK для пространственных привязок Azure. Это позволяет выполнять проверку подлинности непосредственно в службе пространственных привязок Azure для учетной записи, которая входит в тот же клиент Microsoft Entra.

    auto configuration = m_cloudSession.Configuration();
    configuration.AuthenticationToken(LR"(MyAuthenticationToken)");

Как и с маркерами доступа, если маркер Microsoft Entra не задан, необходимо обработать событие TokenRequired или реализовать метод tokenRequired в протоколе делегата.

Событие можно обрабатывать синхронно, задав свойство аргументов события.

    m_accessTokenRequiredToken = m_cloudSession.TokenRequired(winrt::auto_revoke, [](auto&&, auto&& args) {
        args.AuthenticationToken(LR"(MyAuthenticationToken)");
    });

Если нужно выполнить асинхронные операции в обработчике, можете отложить установку маркера, запросив объект deferral, а затем завершить установку, как показано в следующем примере.

    m_accessTokenRequiredToken = m_cloudSession.TokenRequired(winrt::auto_revoke, [this](auto&&, auto&& args) {
        auto deferral = args.GetDeferral();
        MyGetTokenAsync([&deferral, &args](winrt::hstring const& myToken) {
            if (!myToken.empty()) args.AuthenticationToken(myToken);
            deferral.Complete();
        });
    });

Настройка сеанса

Вызовите Start(), чтобы разрешить обработку данных среды в сеансе.

Для обработки событий, вызванных сеансом, подключите обработчик событий.

Дополнительные сведения см. в статье о методе Start.

    m_cloudSession.Start();

Предоставление кадров для сеанса

Сеанс пространственной привязки работает путем сопоставления пространства вокруг пользователя. Это помогает определить, где находятся привязки. Для мобильных платформ (iOS или Android) требуется собственный вызов канала камеры для получения кадров из библиотеки AR вашей платформы. HoloLens, напротив, постоянно сканирует среду, поэтому нет необходимости в конкретном вызове, например на мобильных платформах.

Дополнительные сведения см. в статье о методе ProcessFrame.

    m_cloudSession->ProcessFrame(ar_frame_);

Предоставление отзыва пользователю

Вы можете написать код для управления событием обновления сеанса. Это событие срабатывает каждый раз, когда сеанс улучшает распознавание окружающей среды. Это позволит вам:

  • использовать класс UserFeedback для формирования обратной связи с пользователем при перемещении устройства и обновлении сеансом его распознавания среды. Для этого сделайте следующее.
  • Определите, на каком этапе отслеживаемых пространственных данных достаточно для создания пространственных привязок. Это можно определить с помощью ReadyForCreateProgress или RecommendedForCreateProgress. Когда ReadyForCreateProgress больше 1, у нас достаточно данных для сохранения облачной пространственной привязки, но мы рекомендуем подождать, пока 1 не превысит RecommendedForCreateProgress.

Дополнительные сведения см. в статье о делегате SessionUpdatedDelegate.

    m_sessionUpdatedToken = m_cloudSession.SessionUpdated(winrt::auto_revoke, [this](auto&&, auto&& args)
    {
        auto status = args.Status();
        if (status.UserFeedback() == SessionUserFeedback::None) return;
        m_feedback = LR"(Feedback: )" + FeedbackToString(status.UserFeedback()) + LR"( -)" +
            LR"( Recommend Create=)" + FormatPercent(status.RecommendedForCreateProgress() * 100.f);
    });

Создание облачной пространственной привязки

Чтобы создать облачную пространственную привязку, сначала создайте привязку в системе AR на своей платформе, а затем создайте прототип облака. Воспользуйтесь методом CreateAnchorAsync().

Дополнительные сведения см. в статье о классе CloudSpatialAnchor.

    // Initialization
    SpatialStationaryFrameOfReference m_stationaryReferenceFrame = nullptr;
    HolographicDisplay defaultHolographicDisplay = HolographicDisplay::GetDefault();
    SpatialLocator spatialLocator = defaultHolographicDisplay.SpatialLocator();
    m_stationaryReferenceFrame = spatialLocator.CreateStationaryFrameOfReferenceAtCurrentLocation();

    // Create a local anchor, perhaps by positioning a SpatialAnchor a few meters in front of the user
    SpatialAnchor localAnchor{ nullptr };
    PerceptionTimestamp timestamp = PerceptionTimestampHelper::FromHistoricalTargetTime(DateTime::clock::now());
    SpatialCoordinateSystem currentCoordinateSystem = m_attachedReferenceFrame.GetStationaryCoordinateSystemAtTimestamp(timestamp);
    SpatialPointerPose pose = SpatialPointerPose::TryGetAtTimestamp(currentCoordinateSystem, timestamp);

    // Get the gaze direction relative to the given coordinate system.
    const float3 headPosition = pose.Head().Position();
    const float3 headDirection = pose.Head().ForwardDirection();

    // The anchor is positioned two meter(s) along the user's gaze direction.
    constexpr float distanceFromUser = 2.0f; // meters
    const float3 gazeAtTwoMeters = headPosition + (distanceFromUser * headDirection);

    localAnchor = SpatialAnchor::TryCreateRelativeTo(currentCoordinateSystem, gazeAtTwoMeters);

    // 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.
    CloudSpatialAnchor cloudAnchor = CloudSpatialAnchor();
    cloudAnchor.LocalAnchor(localAnchor);
    co_await m_cloudSession.CreateAnchorAsync(cloudAnchor);
    m_feedback = LR"(Created a cloud anchor with ID=)" + cloudAnchor.Identifier();

Как было сказано ранее, прежде чем создавать новую пространственную привязку в облаке необходимо получить достаточное количество данных об окружающей среде. Это означает, что ReadyForCreateProgress должен быть больше 1, поэтому мы рекомендуем подождать, пока RecommendedForCreateProgress превысит 1.

Дополнительные сведения см. в статье о методе GetSessionStatusAsync.

    SessionStatus status = co_await m_cloudSession.GetSessionStatusAsync();
    if (value.RecommendedForCreateProgress() < 1.0f) return;
    // Issue the creation request ...

Задание свойств

Вы можете добавить некоторые свойства при сохранении пространственных привязок облака. Это может быть тип сохраняемого объекта или основные свойства, например, следует ли их включать для взаимодействия. Это может быть полезно при обнаружении: можно сразу же отобразить объект для пользователя, например, кадр с пустым содержимым. Затем в фоновом режиме загружается дополнительная информация о состоянии, например изображение, отображаемое в кадре.

Дополнительные сведения см. в статье о методе AppProperties.

    CloudSpatialAnchor cloudAnchor = CloudSpatialAnchor();
    cloudAnchor.LocalAnchor(localAnchor);
    auto properties = m_cloudAnchor.AppProperties();
    properties.Insert(LR"(model-type)", LR"(frame)");
    properties.Insert(LR"(label)", LR"(my latest picture)");
    co_await m_cloudSession.CreateAnchorAsync(cloudAnchor);

Обновление свойств

Обновить свойства привязки можно с помощью метода UpdateAnchorProperties(). Если два или более устройств пытаются одновременно обновить свойства одной и той же привязки, мы используем модель оптимистической блокировки. Это означает, что преимущество отдается первой операции записи. Для всех остальных операций записи поступит сообщение об ошибке параллелизма. Прежде чем повторять попытку, нужно будет обновить свойства.

Дополнительные сведения см. в статье о методе UpdateAnchorPropertiesAsync.

    CloudSpatialAnchor anchor = /* locate your anchor */;
    anchor.AppProperties().Insert(LR"(last-user-access)", LR"(just now)");
    co_await m_cloudSession.UpdateAnchorPropertiesAsync(anchor);

Невозможно обновить расположение привязки после ее создания в службе. Чтобы отслеживать новое расположение, создайте новую привязку и удалите старую.

Если вам не нужно искать привязку для обновления ее свойств, вы можете использовать метод GetAnchorPropertiesAsync(), который возвращает объект CloudSpatialAnchor со свойствами.

Дополнительные сведения см. в статье о методе GetAnchorPropertiesAsync.

    CloudSpatialAnchor anchor = co_await m_cloudSession.GetAnchorPropertiesAsync(LR"(anchorId)");
    if (anchor != nullptr)
    {
        anchor.AppProperties().Insert(LR"(last-user-access)", LR"(just now)");
        co_await m_cloudSession.UpdateAnchorPropertiesAsync(anchor);
    }

Срок действия

Можно также настроить для привязки автоматическое истечение срока действия в определенный момент времени в будущем. После истечения срока действия привязку больше нельзя будет найти или обновить. Срок действия можно задать только при создании привязки и перед ее сохранением в облаке. После этого изменить срок действия нельзя. Если во время создания привязки не задать срок ее действия, то она продолжит действовать, пока не будетудалена вручную.

Дополнительные сведения см. в статье о методе Expiration.

    const int64_t oneWeekFromNowInHours = 7 * 24;
    const DateTime oneWeekFromNow = DateTime::clock::now() + std::chrono::hours(oneWeekFromNowInHours);
    cloudAnchor.Expiration(oneWeekFromNow);

Поиск облачной пространственной привязки

Одна из основных причин использования Пространственных привязок Azure — возможность искать ранее сохраненную облачную пространственную привязку. Для этого мы используем "Наблюдатели". Одновременно можно использовать только один наблюдатель; Несколько наблюдателей не поддерживаются. Существует несколько различных способов (также известных как стратегии поиска привязки) наблюдатель может найти облачную пространственную привязку. За один раз можно использовать одну стратегию для наблюдателя.

  • Нахождение привязок по идентификатору.
  • Нахождение привязок, подключенных к ранее найденной привязке. Больше узнать о связях привязок можно здесь.
  • Нахождение привязки, используя Простое уточнение расположения.

Примечание.

Каждый раз при обнаружении привязки Пространственные привязки Azure попытаются использовать собранные данные среды, чтобы расширить визуальные сведения о привязке. Если у вас возникли проблемы с нахождением привязки, можно создать привязку, а затем определить ее несколько раз, используя разные углы и условия освещения.

При поиске пространственных привязок облака по идентификатору можно сохранить идентификатор пространственной привязки облака в серверной службе приложения и сделать его доступным для всех устройств, которые могут правильно пройти проверку подлинности. Пример этого см. в руководстве по совместному использованию пространственных привязок на устройствах.

Создайте экземпляр объекта AnchorLocateCriteria, задайте идентификаторы, которые вы ищете, и в сеансе вызовите метод CreateWatcher, предоставив AnchorLocateCriteria.

Дополнительные сведения см. в статье о методе CreateWatcher.

    AnchorLocateCriteria criteria = AnchorLocateCriteria();
    criteria.Identifiers({ LR"(id1)", LR"(id2)", LR"(id3)" });
    auto cloudSpatialAnchorWatcher = m_cloudSession.CreateWatcher(criteria);

Когда вы создадите наблюдатель, для каждой запрошенной привязки будет срабатывать событие AnchorLocated. Это событие срабатывает, если привязка обнаружена или если ее не удалось найти. В последнем случае причина будет указана в области состояния. Когда для наблюдателя будут обработаны, найдены или не найдены все привязки, сработает событие LocateAnchorsCompleted. Максимальное количество идентификаторов на наблюдателя — 35.

Дополнительные сведения см. в статье о делегате AnchorLocatedDelegate.

    m_anchorLocatedToken = m_cloudSession.AnchorLocated(winrt::auto_revoke, [this](auto&&, auto&& args)
    {
        switch (args.Status())
        {
            case LocateAnchorStatus::Located:
            {
                CloudSpatialAnchor foundAnchor = args.Anchor();
            }
                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.

    co_await m_cloudSession.DeleteAnchorAsync(cloudAnchor);
    // Perform any processing you may want when delete finishes

Приостановка, сброс или остановка сеанса

Чтобы временно остановить сеанс, можно вызвать Stop(). Это приведет к прерыванию обработки наблюдателей и среды даже при вызове ProcessFrame(). Затем можно вызвать Start(), чтобы возобновить обработку. При возобновлении обработанные в сеансе данные сохраняются.

Дополнительные сведения см. в статье о методе Stop.

    m_cloudSession.Stop();

Чтобы очистить данные среды, записанные в сеансе, можете вызвать функцию Reset().

Дополнительные сведения см. в статье о методе Reset.

    m_cloudSession.Reset();

Чтобы правильно выполнить очистку после выхода из сеанса, удалите все ссылки.

    m_cloudSession = nullptr;

Следующие шаги

Из этого руководства вы узнали о том, как создавать и находить привязки с использованием пакета SDK для службы "Пространственные привязки Azure". Чтобы узнать больше о связях между привязками, перейдите к следующему руководству.