Bagikan melalui


Sistem koordinat di DirectX

Catatan

Artikel ini berkaitan dengan API asli WinRT warisan. Untuk proyek aplikasi asli baru, sebaiknya gunakan OpenXR API.

Sistem koordinat membentuk dasar untuk pemahaman spasial yang ditawarkan oleh API Windows Mixed Reality.

Perangkat VR atau VR satu kamar yang duduk saat ini membangun satu sistem koordinat utama untuk ruang terlacak mereka. Mixed Reality perangkat seperti HoloLens dirancang untuk lingkungan besar yang tidak terdefinisi, dengan perangkat menemukan dan mempelajari lingkungannya saat pengguna berjalan-jalan. Perangkat beradaptasi untuk terus meningkatkan pengetahuan tentang kamar pengguna, tetapi menghasilkan sistem koordinat yang mengubah hubungan mereka satu sama lain selama masa pakai aplikasi. Windows Mixed Reality mendukung spektrum perangkat yang luas, mulai dari headset imersif yang duduk melalui bingkai referensi yang terpasang di dunia.

Catatan

Cuplikan kode dalam artikel ini saat ini menunjukkan penggunaan C++/CX daripada C++17-compliant C++/WinRT seperti yang digunakan dalam templat proyek holografik C++. Konsepnya setara untuk proyek C++/WinRT, meskipun Anda harus menerjemahkan kode.

Sistem koordinat spasial di Windows

Jenis inti yang digunakan untuk alasan tentang sistem koordinat dunia nyata di Windows adalah SpatialCoordinateSystem. Instans jenis ini mewakili sistem koordinat arbitrer, menyediakan metode untuk mendapatkan data matriks transformasi yang dapat Anda gunakan untuk mengubah antara dua sistem koordinat tanpa memahami detail masing-masing.

Metode yang mengembalikan informasi spasial akan menerima parameter SpatialCoordinateSystem untuk memungkinkan Anda memutuskan sistem koordinat di mana paling berguna bagi koordinat tersebut untuk dikembalikan. Informasi spasial direpresentasikan sebagai titik, sinar, atau volume di sekitar pengguna, dan unit untuk koordinat ini akan selalu dalam meter.

SpatialCoordinateSystem memiliki hubungan dinamis dengan sistem koordinat lainnya, termasuk yang mewakili posisi perangkat. Kapan saja, perangkat dapat menemukan beberapa sistem koordinat dan bukan yang lain. Untuk sebagian besar sistem koordinat, aplikasi Anda harus siap menangani periode waktu di mana mereka tidak dapat ditemukan.

Aplikasi Anda tidak boleh membuat SpatialCoordinateSystems secara langsung - melainkan harus dikonsumsi melalui API Persepsi. Ada tiga sumber utama sistem koordinat di API Perception, yang masing-masing memetakan ke konsep yang dijelaskan di halaman Sistem koordinat :

Semua sistem koordinat yang dikembalikan oleh objek ini ditangani dengan tangan kanan, dengan +y ke atas, +x di sebelah kanan dan +z mundur. Anda dapat mengingat arah mana titik sumbu z positif dengan mengarahkan jari tangan kiri atau kanan Anda ke arah x positif dan menggulungnya ke arah y positif. Arah titik jempol Anda, baik menuju atau menjauh dari Anda, adalah arah yang ditunjukkan sumbu z positif untuk sistem koordinat tersebut. Ilustrasi berikut menunjukkan kedua sistem koordinat ini.

Sistem koordinat tangan kiri dan kanan
Sistem koordinat tangan kiri dan kanan

Gunakan kelas SpatialLocator untuk membuat bingkai referensi yang terpasang atau stasioner ke bootstrap ke dalam SpatialCoordinateSystem berdasarkan posisi HoloLens. Lanjutkan ke bagian berikutnya untuk mempelajari selengkapnya tentang proses ini.

Tempatkan hologram di dunia menggunakan tahap spasial

Sistem koordinat untuk headset buram Windows Mixed Reality imersif diakses menggunakan properti SpatialStageFrameOfReference::Current statis. API ini menyediakan:

  • Sistem koordinat
  • Informasi tentang apakah pemutar duduk atau seluler
  • Batas area aman untuk berjalan-jalan jika pemutar ponsel
  • Indikasi apakah headset terarah.
  • Penanganan aktivitas untuk pembaruan tahap spasial.

Pertama, kita mendapatkan tahap spasial dan berlangganan pembaruan untuk itu:

Kode untuk inisialisasi tahap spasial

SpatialStageManager::SpatialStageManager(
    const std::shared_ptr<DX::DeviceResources>& deviceResources, 
    const std::shared_ptr<SceneController>& sceneController)
    : m_deviceResources(deviceResources), m_sceneController(sceneController)
{
    // Get notified when the stage is updated.
    m_spatialStageChangedEventToken = SpatialStageFrameOfReference::CurrentChanged +=
        ref new EventHandler<Object^>(std::bind(&SpatialStageManager::OnCurrentChanged, this, _1));

    // Make sure to get the current spatial stage.
    OnCurrentChanged(nullptr);
}

Dalam metode OnCurrentChanged, aplikasi Anda harus memeriksa tahap spasial dan memperbarui pengalaman pemutar. Dalam contoh ini, kami menyediakan visualisasi batas panggung dan posisi mulai yang ditentukan oleh pengguna dan rentang tampilan tahap dan rentang properti pergerakan. Kami juga kembali ke sistem koordinat stasium kami sendiri, ketika tahapan tidak dapat disediakan.

Kode untuk pembaruan tahap spasial

void SpatialStageManager::OnCurrentChanged(Object^ /*o*/)
{
    // The event notifies us that a new stage is available.
    // Get the current stage.
    m_currentStage = SpatialStageFrameOfReference::Current;

    // Clear previous content.
    m_sceneController->ClearSceneObjects();

    if (m_currentStage != nullptr)
    {
        // Obtain stage geometry.
        auto stageCoordinateSystem = m_currentStage->CoordinateSystem;
        auto boundsVertexArray = m_currentStage->TryGetMovementBounds(stageCoordinateSystem);

        // Visualize the area where the user can move around.
        std::vector<float3> boundsVertices;
        boundsVertices.resize(boundsVertexArray->Length);
        memcpy(boundsVertices.data(), boundsVertexArray->Data, boundsVertexArray->Length * sizeof(float3));
        std::vector<unsigned short> indices = TriangulatePoints(boundsVertices);
        m_stageBoundsShape =
            std::make_shared<SceneObject>(
                    m_deviceResources,
                    reinterpret_cast<std::vector<XMFLOAT3>&>(boundsVertices),
                    indices,
                    XMFLOAT3(DirectX::Colors::SeaGreen),
                    stageCoordinateSystem);
        m_sceneController->AddSceneObject(m_stageBoundsShape);

        // In this sample, we draw a visual indicator for some spatial stage properties.
        // If the view is forward-only, the indicator is a half circle pointing forward - otherwise, it
        // is a full circle.
        // If the user can walk around, the indicator is blue. If the user is seated, it is red.

        // The indicator is rendered at the origin - which is where the user declared the center of the
        // stage to be during setup - above the plane of the stage bounds object.
        float3 visibleAreaCenter = float3(0.f, 0.001f, 0.f);

        // Its shape depends on the look direction range.
        std::vector<float3> visibleAreaIndicatorVertices;
        if (m_currentStage->LookDirectionRange == SpatialLookDirectionRange::ForwardOnly)
        {
            // Half circle for forward-only look direction range.
            visibleAreaIndicatorVertices = CreateCircle(visibleAreaCenter, 0.25f, 9, XM_PI);
        }
        else
        {
            // Full circle for omnidirectional look direction range.
            visibleAreaIndicatorVertices = CreateCircle(visibleAreaCenter, 0.25f, 16, XM_2PI);
        }

        // Its color depends on the movement range.
        XMFLOAT3 visibleAreaColor;
        if (m_currentStage->MovementRange == SpatialMovementRange::NoMovement)
        {
            visibleAreaColor = XMFLOAT3(DirectX::Colors::OrangeRed);
        }
        else
        {
            visibleAreaColor = XMFLOAT3(DirectX::Colors::Aqua);
        }

        std::vector<unsigned short> visibleAreaIndicatorIndices = TriangulatePoints(visibleAreaIndicatorVertices);

        // Visualize the look direction range.
        m_stageVisibleAreaIndicatorShape =
            std::make_shared<SceneObject>(
                    m_deviceResources,
                    reinterpret_cast<std::vector<XMFLOAT3>&>(visibleAreaIndicatorVertices),
                    visibleAreaIndicatorIndices,
                    visibleAreaColor,
                    stageCoordinateSystem);
        m_sceneController->AddSceneObject(m_stageVisibleAreaIndicatorShape);
    }
    else
    {
        // No spatial stage was found.
        // Fall back to a stationary coordinate system.
        auto locator = SpatialLocator::GetDefault();
        if (locator)
        {
            m_stationaryFrameOfReference = locator->CreateStationaryFrameOfReferenceAtCurrentLocation();

            // Render an indicator, so that we know we fell back to a mode without a stage.
            std::vector<float3> visibleAreaIndicatorVertices;
            float3 visibleAreaCenter = float3(0.f, -2.0f, 0.f);
            visibleAreaIndicatorVertices = CreateCircle(visibleAreaCenter, 0.125f, 16, XM_2PI);
            std::vector<unsigned short> visibleAreaIndicatorIndices = TriangulatePoints(visibleAreaIndicatorVertices);
            m_stageVisibleAreaIndicatorShape =
                std::make_shared<SceneObject>(
                    m_deviceResources,
                    reinterpret_cast<std::vector<XMFLOAT3>&>(visibleAreaIndicatorVertices),
                    visibleAreaIndicatorIndices,
                    XMFLOAT3(DirectX::Colors::LightSlateGray),
                    m_stationaryFrameOfReference->CoordinateSystem);
            m_sceneController->AddSceneObject(m_stageVisibleAreaIndicatorShape);
        }
    }
}

Kumpulan simpul yang menentukan batas tahap disediakan dalam urutan searah jajaran. Shell Windows Mixed Reality menggambar pagar di batas ketika pengguna mendekatinya, tetapi Anda mungkin ingin segitiga area yang dapat dilalui untuk tujuan Anda sendiri. Algoritma berikut dapat digunakan untuk segitiga tahap.

Kode untuk segitiga tahap spasial

std::vector<unsigned short> SpatialStageManager::TriangulatePoints(std::vector<float3> const& vertices)
{
    size_t const& vertexCount = vertices.size();

    // Segments of the shape are removed as they are triangularized.
    std::vector<bool> vertexRemoved;
    vertexRemoved.resize(vertexCount, false);
    unsigned int vertexRemovedCount = 0;

    // Indices are used to define triangles.
    std::vector<unsigned short> indices;

    // Decompose into convex segments.
    unsigned short currentVertex = 0;
    while (vertexRemovedCount < (vertexCount - 2))
    {
        // Get next triangle:
        // Start with the current vertex.
        unsigned short index1 = currentVertex;

        // Get the next available vertex.
        unsigned short index2 = index1 + 1;

        // This cycles to the next available index.
        auto CycleIndex = [=](unsigned short indexToCycle, unsigned short stopIndex)
        {
            // Make sure the index does not exceed bounds.
            if (indexToCycle >= unsigned short(vertexCount))
            {
                indexToCycle -= unsigned short(vertexCount);
            }

            while (vertexRemoved[indexToCycle])
            {
                // If the vertex is removed, go to the next available one.
                ++indexToCycle;

                // Make sure the index does not exceed bounds.
                if (indexToCycle >= unsigned short(vertexCount))
                {
                    indexToCycle -= unsigned short(vertexCount);
                }

                // Prevent cycling all the way around.
                // Should not be needed, as we limit with the vertex count.
                if (indexToCycle == stopIndex)
                {
                    break;
                }
            }

            return indexToCycle;
        };
        index2 = CycleIndex(index2, index1);

        // Get the next available vertex after that.
        unsigned short index3 = index2 + 1;
        index3 = CycleIndex(index3, index1);

        // Vertices that may define a triangle inside the 2D shape.
        auto& v1 = vertices[index1];
        auto& v2 = vertices[index2];
        auto& v3 = vertices[index3];

        // If the projection of the first segment (in clockwise order) onto the second segment is 
        // positive, we know that the clockwise angle is less than 180 degrees, which tells us 
        // that the triangle formed by the two segments is contained within the bounding shape.
        auto v2ToV1 = v1 - v2;
        auto v2ToV3 = v3 - v2;
        float3 normalToV2ToV3 = { -v2ToV3.z, 0.f, v2ToV3.x };
        float projectionOntoNormal = dot(v2ToV1, normalToV2ToV3);
        if (projectionOntoNormal >= 0)
        {
            // Triangle is contained within the 2D shape.

            // Remove peak vertex from the list.
            vertexRemoved[index2] = true;
            ++vertexRemovedCount;

            // Create the triangle.
            indices.push_back(index1);
            indices.push_back(index2);
            indices.push_back(index3);

            // Continue on to the next outer triangle.
            currentVertex = index3;
        }
        else
        {
            // Triangle is a cavity in the 2D shape.
            // The next triangle starts at the inside corner.
            currentVertex = index2;
        }
    }

    indices.shrink_to_fit();
    return indices;
}

Tempatkan hologram di dunia menggunakan bingkai referensi stasium

Kelas SpatialStationaryFrameOfReference mewakili bingkai referensi yang tetap stasioner relatif terhadap lingkungan pengguna saat pengguna bergerak. Bingkai referensi ini memprioritaskan menjaga koordinat tetap stabil di dekat perangkat. Salah satu penggunaan utama SpatialStationaryFrameOfReference adalah bertindak sebagai sistem koordinat dunia yang mendasarinya dalam mesin penyajian saat merender hologram.

Untuk mendapatkan SpatialStationaryFrameOfReference, gunakan kelas SpatialLocator dan panggil CreateStationaryFrameOfReferenceAtCurrentLocation.

Dari kode templat aplikasi Holografik Windows:

           // The simplest way to render world-locked holograms is to create a stationary reference frame
           // when the app is launched. This is roughly analogous to creating a "world" coordinate system
           // with the origin placed at the device's position as the app is launched.
           referenceFrame = locator.CreateStationaryFrameOfReferenceAtCurrentLocation();
  • Bingkai referensi stasensi dirancang untuk memberikan posisi yang paling pas relatif terhadap ruang keseluruhan. Posisi individual dalam bingkai referensi tersebut diizinkan untuk sedikit menyimpang. Ini normal karena perangkat mempelajari lebih lanjut tentang lingkungan.
  • Ketika penempatan hologram individu yang tepat diperlukan, SpatialAnchor harus digunakan untuk menjangkar hologram individu ke posisi di dunia nyata - misalnya, titik yang ditunjukkan pengguna untuk menarik perhatian khusus. Posisi jangkar tidak menyimpang, tetapi dapat diperbaiki; jangkar akan menggunakan posisi yang dikoreksi mulai dari bingkai berikutnya setelah koreksi terjadi.

Tempatkan hologram di dunia menggunakan jangkar spasial

Jangkar spasial adalah cara yang bagus untuk menempatkan hologram di tempat tertentu di dunia nyata, dengan sistem memastikan jangkar tetap di tempat dari waktu ke waktu. Topik ini menjelaskan cara membuat dan menggunakan jangkar, dan cara bekerja dengan data jangkar.

Anda dapat membuat SpatialAnchor pada posisi dan orientasi apa pun dalam SpatialCoordinateSystem yang Anda pilih. Perangkat harus dapat menemukan sistem koordinat itu saat ini, dan sistem tidak boleh mencapai batas jangkar spasialnya.

Setelah didefinisikan, sistem koordinat SpatialAnchor terus menyesuaikan untuk menjaga posisi dan orientasi yang tepat dari lokasi awalnya. Anda kemudian dapat menggunakan SpatialAnchor ini untuk merender hologram yang akan muncul diperbaiki di lingkungan pengguna di lokasi yang tepat.

Efek penyesuaian yang menjaga jangkar tetap di tempat diperbesar seiring dengan meningkatnya jarak dari jangkar. Anda harus menghindari penyajian konten relatif terhadap jangkar yang lebih dari sekitar 3 meter dari asal jangkar tersebut.

Properti CoordinateSystem mendapatkan sistem koordinat yang memungkinkan Anda menempatkan konten relatif terhadap jangkar, dengan kemudahan yang diterapkan saat perangkat menyesuaikan lokasi tepat jangkar.

Gunakan properti RawCoordinateSystem dan peristiwa RawCoordinateSystemAdjusted yang sesuai untuk mengelola penyesuaian ini sendiri.

Pertahankan dan bagikan jangkar spasial

Anda dapat mempertahankan SpatialAnchor secara lokal menggunakan kelas SpatialAnchorStore dan kemudian mendapatkannya kembali di sesi aplikasi mendatang pada perangkat HoloLens yang sama.

Dengan menggunakan Azure Spatial Anchors, Anda dapat membuat jangkar cloud tahan lama dari SpatialAnchor lokal, yang kemudian dapat ditemukan aplikasi Anda di beberapa perangkat HoloLens, iOS, dan Android. Dengan berbagi jangkar spasial umum di beberapa perangkat, setiap pengguna dapat melihat konten yang dirender relatif terhadap jangkar tersebut di lokasi fisik yang sama secara real time.

Anda juga dapat menggunakan Azure Spatial Anchors untuk persistensi hologram asinkron di seluruh perangkat HoloLens, iOS, dan Android. Dengan berbagi jangkar spasial cloud yang tahan lama, beberapa perangkat dapat mengamati hologram persisten yang sama dari waktu ke waktu, bahkan jika perangkat tersebut tidak hadir bersama-sama pada saat yang sama.

Untuk mulai membangun pengalaman bersama di aplikasi HoloLens Anda, cobalah mulai cepat Azure Spatial Anchors HoloLens 5 menit.

Setelah anda siap dan berjalan dengan Azure Spatial Anchors, Anda kemudian dapat membuat dan menemukan jangkar di HoloLens. Panduan juga tersedia untuk Android dan iOS , memungkinkan Anda berbagi jangkar yang sama di semua perangkat.

Membuat SpatialAnchors untuk konten holografik

Untuk sampel kode ini, kami memodifikasi templat aplikasi Holografik Windows untuk membuat jangkar saat gerakan Ditekan terdeteksi. Kubus kemudian ditempatkan di jangkar selama lulus render.

Karena beberapa jangkar didukung oleh kelas pembantu, kita dapat menempatkan kubus sebanyak yang ingin kita gunakan sampel kode ini!

Catatan

ID untuk jangkar adalah sesuatu yang Anda kontrol di aplikasi Anda. Dalam contoh ini, kami telah membuat skema penamaan yang berurutan berdasarkan jumlah jangkar yang saat ini disimpan dalam koleksi jangkar aplikasi.

   // Check for new input state since the last frame.
   SpatialInteractionSourceState^ pointerState = m_spatialInputHandler->CheckForInput();
   if (pointerState != nullptr)
   {
       // Try to get the pointer pose relative to the SpatialStationaryReferenceFrame.
       SpatialPointerPose^ pointerPose = pointerState->TryGetPointerPose(currentCoordinateSystem);
       if (pointerPose != nullptr)
       {
           // When a Pressed gesture is detected, the anchor will be created two meters in front of the user.

           // Get the gaze direction relative to the given coordinate system.
           const float3 headPosition = pointerPose->Head->Position;
           const float3 headDirection = pointerPose->Head->ForwardDirection;

           // The anchor position in the StationaryReferenceFrame.
           static const float distanceFromUser = 2.0f; // meters
           const float3 gazeAtTwoMeters = headPosition + (distanceFromUser * headDirection);

           // Create the anchor at position.
           SpatialAnchor^ anchor = SpatialAnchor::TryCreateRelativeTo(currentCoordinateSystem, gazeAtTwoMeters);

           if ((anchor != nullptr) && (m_spatialAnchorHelper != nullptr))
           {
               // In this example, we store the anchor in an IMap.
               auto anchorMap = m_spatialAnchorHelper->GetAnchorMap();

               // Create an identifier for the anchor.
               String^ id = ref new String(L"HolographicSpatialAnchorStoreSample_Anchor") + anchorMap->Size;

               anchorMap->Insert(id->ToString(), anchor);
           }
       }
   }

Secara asinkron memuat, dan cache, SpatialAnchorStore

Mari kita lihat cara menulis kelas SampleSpatialAnchorHelper yang membantu menangani persistensi ini, termasuk:

  • Menyimpan kumpulan jangkar dalam memori, yang diindeks oleh kunci Platform::String.
  • Memuat jangkar dari SpatialAnchorStore sistem, yang dipisahkan dari koleksi dalam memori lokal.
  • Menyimpan koleksi jangkar dalam memori lokal ke SpatialAnchorStore saat aplikasi memilih untuk melakukannya.

Berikut cara menyimpan objek SpatialAnchor di SpatialAnchorStore.

Ketika kelas dimulai, kami meminta SpatialAnchorStore secara asinkron. Ini melibatkan I/O sistem karena API memuat penyimpanan jangkar, dan API ini dibuat asinkron sehingga I/O tidak memblokir.

   // Request the spatial anchor store, which is the WinRT object that will accept the imported anchor data.
   return create_task(SpatialAnchorManager::RequestStoreAsync())
       .then([](task<SpatialAnchorStore^> previousTask)
   {
       std::shared_ptr<SampleSpatialAnchorHelper> newHelper = nullptr;

       try
       {
           SpatialAnchorStore^ anchorStore = previousTask.get();

           // Once the SpatialAnchorStore has been loaded by the system, we can create our helper class.

           // Using "new" to access private constructor
           newHelper = std::shared_ptr<SampleSpatialAnchorHelper>(new SampleSpatialAnchorHelper(anchorStore));

           // Now we can load anchors from the store.
           newHelper->LoadFromAnchorStore();
       }
       catch (Exception^ exception)
       {
           PrintWstringToDebugConsole(
               std::wstring(L"Exception while loading the anchor store: ") +
               exception->Message->Data() +
               L"\n"
               );
       }

       // Return the initialized class instance.
       return newHelper;
   });

Anda akan diberi SpatialAnchorStore yang dapat Anda gunakan untuk menyimpan jangkar. Ini adalah IMapView yang mengaitkan nilai kunci yang merupakan String, dengan nilai data yang SpatialAnchors. Dalam kode sampel kami, kami menyimpan ini dalam variabel anggota kelas privat yang dapat diakses melalui fungsi publik kelas pembantu kami.

   SampleSpatialAnchorHelper::SampleSpatialAnchorHelper(SpatialAnchorStore^ anchorStore)
   {
       m_anchorStore = anchorStore;
       m_anchorMap = ref new Platform::Collections::Map<String^, SpatialAnchor^>();
   }

Catatan

Jangan lupa untuk menghubungkan peristiwa tangguhan/lanjutkan untuk menyimpan dan memuat penyimpanan jangkar.

   void HolographicSpatialAnchorStoreSampleMain::SaveAppState()
   {
       // For example, store information in the SpatialAnchorStore.
       if (m_spatialAnchorHelper != nullptr)
       {
           m_spatialAnchorHelper->TrySaveToAnchorStore();
       }
   }
   void HolographicSpatialAnchorStoreSampleMain::LoadAppState()
   {
       // For example, load information from the SpatialAnchorStore.
       LoadAnchorStore();
   }

Menyimpan konten ke penyimpanan jangkar

Saat sistem menangguhkan aplikasi, Anda perlu menyimpan jangkar spasial ke penyimpanan jangkar. Anda juga dapat memilih untuk menyimpan jangkar ke penyimpanan jangkar di lain waktu, karena Anda merasa perlu untuk implementasi aplikasi Anda.

Ketika Anda siap untuk mencoba menyimpan jangkar dalam memori ke SpatialAnchorStore, Anda dapat mengulang koleksi Anda dan mencoba menyimpan masing-masing.

   // TrySaveToAnchorStore: Stores all anchors from memory into the app's anchor store.
   //
   // For each anchor in memory, this function tries to store it in the app's AnchorStore. The operation will fail if
   // the anchor store already has an anchor by that name.
   //
   bool SampleSpatialAnchorHelper::TrySaveToAnchorStore()
   {
       // This function returns true if all the anchors in the in-memory collection are saved to the anchor
       // store. If zero anchors are in the in-memory collection, we will still return true because the
       // condition has been met.
       bool success = true;

       // If access is denied, 'anchorStore' will not be obtained.
       if (m_anchorStore != nullptr)
       {
           for each (auto& pair in m_anchorMap)
           {
               auto const& id = pair->Key;
               auto const& anchor = pair->Value;

               // Try to save the anchors.
               if (!m_anchorStore->TrySave(id, anchor))
               {
                   // This may indicate the anchor ID is taken, or the anchor limit is reached for the app.
                   success=false;
               }
           }
       }

       return success;
   }

Memuat konten dari penyimpanan jangkar saat aplikasi dilanjutkan

Anda dapat memulihkan jangkar yang disimpan di AnchorStore dengan mentransfernya dari IMapView penyimpanan jangkar ke database dalam memori SpatialAnchors Anda sendiri saat aplikasi Anda dilanjutkan atau kapan saja.

Untuk memulihkan jangkar dari SpatialAnchorStore, pulihkan masing-masing jangkar yang Anda minati ke koleksi dalam memori Anda sendiri.

Anda memerlukan database SpatialAnchors dalam memori Anda sendiri untuk mengaitkan String dengan SpatialAnchors yang Anda buat. Dalam kode sampel kami, kami memilih untuk menggunakan Windows::Foundation::Collections::IMap untuk menyimpan jangkar, yang memudahkan untuk menggunakan kunci dan nilai data yang sama untuk SpatialAnchorStore.

   // This is an in-memory anchor list that is separate from the anchor store.
   // These anchors may be used, reasoned about, and so on before committing the collection to the store.
   Windows::Foundation::Collections::IMap<Platform::String^, Windows::Perception::Spatial::SpatialAnchor^>^ m_anchorMap;

Catatan

Jangkar yang dipulihkan mungkin tidak dapat dilokalkan segera. Misalnya, mungkin jangkar di ruang terpisah atau di bangunan yang berbeda sama sekali. Jangkar yang diambil dari AnchorStore harus diuji untuk ditemukan sebelum menggunakannya.


Catatan

Dalam contoh kode ini, kita mengambil semua jangkar dari AnchorStore. Ini bukan persyaratan; aplikasi Anda juga dapat memilih dan memilih subset jangkar tertentu dengan menggunakan nilai kunci String yang bermakna bagi implementasi Anda.

   // LoadFromAnchorStore: Loads all anchors from the app's anchor store into memory.
   //
   // The anchors are stored in memory using an IMap, which stores anchors using a string identifier. Any string can be used as
   // the identifier; it can have meaning to the app, such as "Game_Leve1_CouchAnchor," or it can be a GUID that is generated
   // by the app.
   //
   void SampleSpatialAnchorHelper::LoadFromAnchorStore()
   {
       // If access is denied, 'anchorStore' will not be obtained.
       if (m_anchorStore != nullptr)
       {
           // Get all saved anchors.
           auto anchorMapView = m_anchorStore->GetAllSavedAnchors();
           for each (auto const& pair in anchorMapView)
           {
               auto const& id = pair->Key;
               auto const& anchor = pair->Value;
               m_anchorMap->Insert(id, anchor);
           }
       }
   }

Bersihkan penyimpanan jangkar, jika diperlukan

Terkadang, Anda perlu menghapus status aplikasi dan menulis data baru. Berikut adalah cara Anda melakukannya dengan SpatialAnchorStore.

Menggunakan kelas pembantu kami, hampir tidak perlu untuk membungkus fungsi Clear. Kami memilih untuk melakukannya dalam implementasi sampel kami, karena kelas pembantu kami diberi tanggung jawab untuk memiliki instans SpatialAnchorStore.

   // ClearAnchorStore: Clears the AnchorStore for the app.
   //
   // This function clears the AnchorStore. It has no effect on the anchors stored in memory.
   //
   void SampleSpatialAnchorHelper::ClearAnchorStore()
   {
       // If access is denied, 'anchorStore' will not be obtained.
       if (m_anchorStore != nullptr)
       {
           // Clear all anchors from the store.
           m_anchorStore->Clear();
       }
   }

Contoh: Menghubungkan sistem koordinat jangkar ke sistem koordinat bingkai referensi stasioner

Katakanlah Anda memiliki jangkar, dan Anda ingin menghubungkan sesuatu dalam sistem koordinat jangkar Anda dengan SpatialStationaryReferenceFrame yang sudah Anda gunakan untuk konten Anda yang lain. Anda dapat menggunakan TryGetTransformTo untuk mendapatkan transformasi dari sistem koordinat jangkar ke bingkai referensi stasioner:

   // In this code snippet, someAnchor is a SpatialAnchor^ that has been initialized and is valid in the current environment.
   float4x4 anchorSpaceToCurrentCoordinateSystem;
   SpatialCoordinateSystem^ anchorSpace = someAnchor->CoordinateSystem;
   const auto tryTransform = anchorSpace->TryGetTransformTo(currentCoordinateSystem);
   if (tryTransform != nullptr)
   {
       anchorSpaceToCurrentCoordinateSystem = tryTransform->Value;
   }

Proses ini berguna bagi Anda dengan dua cara:

  1. Ini memberi tahu Anda jika dua bingkai referensi dapat dipahami relatif satu sama lain, dan;
  2. Jika demikian, ini memberi Anda transformasi untuk langsung dari satu sistem koordinat ke sistem koordinat lainnya.

Dengan informasi ini, Anda memiliki pemahaman tentang hubungan spasial antara objek antara dua bingkai referensi.

Untuk penyajian, Anda sering kali dapat memperoleh hasil yang lebih baik dengan mengelompokkan objek sesuai dengan bingkai referensi atau jangkar aslinya. Lakukan pass gambar terpisah untuk setiap grup. Matriks tampilan lebih akurat untuk objek dengan transformasi model yang awalnya dibuat menggunakan sistem koordinat yang sama.

Membuat hologram menggunakan bingkai referensi yang terpasang pada perangkat

Ada kalanya Anda ingin merender hologram yang tetap melekat pada lokasi perangkat, misalnya panel dengan informasi penelusuran kesalahan atau pesan informasi ketika perangkat hanya dapat menentukan orientasinya dan bukan posisinya di ruang. Untuk mencapai hal ini, kami menggunakan bingkai referensi terlampir.

Kelas SpatialLocatorAttachedFrameOfReference mendefinisikan sistem koordinat, yang relatif terhadap perangkat daripada ke dunia nyata. Bingkai ini memiliki judul tetap relatif terhadap lingkungan pengguna yang menunjuk ke arah yang dihadapi pengguna saat bingkai referensi dibuat. Sejak saat itu, semua orientasi dalam bingkai referensi ini relatif terhadap judul tetap tersebut, bahkan saat pengguna memutar perangkat.

Untuk HoloLens, asal sistem koordinat bingkai ini terletak di tengah rotasi kepala pengguna, sehingga posisinya tidak terpengaruh oleh rotasi kepala. Aplikasi Anda dapat menentukan offset relatif terhadap hologram titik ini ke posisi di depan pengguna.

Untuk mendapatkan SpatialLocatorAttachedFrameOfReference, gunakan kelas SpatialLocator dan panggil CreateAttachedFrameOfReferenceAtCurrentHeading.

Ini berlaku untuk seluruh rentang perangkat Windows Mixed Reality.

Menggunakan bingkai referensi yang terpasang pada perangkat

Bagian-bagian ini berbicara tentang apa yang kami ubah di templat aplikasi Windows Holographic untuk mengaktifkan bingkai referensi yang terpasang pada perangkat menggunakan API ini. Hologram "terpasang" ini akan bekerja bersama hologram stasioner atau berlabuh, dan juga dapat digunakan ketika perangkat untuk sementara tidak dapat menemukan posisinya di dunia.

Pertama, kami mengubah templat untuk menyimpan SpatialLocatorAttachedFrameOfReference alih-alih SpatialStationaryFrameOfReference:

Dari HolographicTagAlongSampleMain.h:

   // A reference frame attached to the holographic camera.
   Windows::Perception::Spatial::SpatialLocatorAttachedFrameOfReference^   m_referenceFrame;

Dari HolographicTagAlongSampleMain.cpp:

   // In this example, we create a reference frame attached to the device.
   m_referenceFrame = m_locator->CreateAttachedFrameOfReferenceAtCurrentHeading();

Selama pembaruan, kita sekarang mendapatkan sistem koordinat pada stempel waktu yang diperoleh dari dengan prediksi bingkai.

   // Next, we get a coordinate system from the attached frame of reference that is
   // associated with the current frame. Later, this coordinate system is used for
   // for creating the stereo view matrices when rendering the sample content.
   SpatialCoordinateSystem^ currentCoordinateSystem =
       m_referenceFrame->GetStationaryCoordinateSystemAtTimestamp(prediction->Timestamp);

Dapatkan pose penunjuk spasial, dan ikuti Tatapan pengguna

Kami ingin contoh hologram kami mengikuti tatapan pengguna, mirip dengan bagaimana shell holografik dapat mengikuti tatapan pengguna. Untuk ini, kita perlu mendapatkan SpatialPointerPose dari stempel waktu yang sama.

SpatialPointerPose^ pose = SpatialPointerPose::TryGetAtTimestamp(currentCoordinateSystem, prediction->Timestamp);

SpatialPointerPose ini memiliki informasi yang diperlukan untuk memposisikan hologram sesuai dengan judul pengguna saat ini.

Untuk kenyamanan pengguna, kami menggunakan interpolasi linier ("lerp") untuk memuluskan perubahan posisi selama jangka waktu tertentu. Ini lebih nyaman bagi pengguna daripada mengunci hologram ke tatapan mereka. Melemahkan posisi hologram tag-along juga memungkinkan kita untuk menstabilkan hologram dengan meredam gerakan. Jika kita tidak melakukan peredaman ini, pengguna akan melihat jitter hologram karena apa yang biasanya dianggap sebagai gerakan kepala pengguna yang tidak terlihat.

Dari StationaryQuadRenderer::P ositionHologram:

   const float& dtime = static_cast<float>(timer.GetElapsedSeconds());

   if (pointerPose != nullptr)
   {
       // Get the gaze direction relative to the given coordinate system.
       const float3 headPosition  = pointerPose->Head->Position;
       const float3 headDirection = pointerPose->Head->ForwardDirection;

       // The tag-along hologram follows a point 2.0m in front of the user's gaze direction.
       static const float distanceFromUser = 2.0f; // meters
       const float3 gazeAtTwoMeters = headPosition + (distanceFromUser * headDirection);

       // Lerp the position, to keep the hologram comfortably stable.
       auto lerpedPosition = lerp(m_position, gazeAtTwoMeters, dtime * c_lerpRate);

       // This will be used as the translation component of the hologram's
       // model transform.
       SetPosition(lerpedPosition);
   }

Catatan

Dalam kasus panel penelusuran kesalahan, Anda dapat memilih untuk memposisikan ulang hologram ke sisi sedikit agar tidak menghalangi tampilan Anda. Berikut adalah contoh bagaimana Anda dapat melakukannya.

Untuk StationaryQuadRenderer::P ositionHologram:

       // If you're making a debug view, you might not want the tag-along to be directly in the
       // center of your field of view. Use this code to position the hologram to the right of
       // the user's gaze direction.
       /*
       const float3 offset = float3(0.13f, 0.0f, 0.f);
       static const float distanceFromUser = 2.2f; // meters
       const float3 gazeAtTwoMeters = headPosition + (distanceFromUser * (headDirection + offset));
       */

Putar hologram untuk menghadap kamera

Tidak cukup untuk memposisikan hologram, yang dalam hal ini adalah quad; kita juga harus memutar objek untuk menghadapi pengguna. Rotasi ini terjadi di ruang dunia, karena jenis billboarding ini memungkinkan hologram untuk tetap menjadi bagian dari lingkungan pengguna. Billboarding ruang lihat tidak senyaman karena hologram menjadi terkunci ke orientasi tampilan; dalam hal ini, Anda juga harus menginterpolasi antara matriks tampilan kiri dan kanan untuk memperoleh transformasi billboard ruang pandang yang tidak mengganggu penyajian stereo. Di sini, kami memutar sumbu X dan Z untuk menghadapi pengguna.

Dari StationaryQuadRenderer::Update:

   // Seconds elapsed since previous frame.
   const float& dTime = static_cast<float>(timer.GetElapsedSeconds());

   // Create a direction normal from the hologram's position to the origin of person space.
   // This is the z-axis rotation.
   XMVECTOR facingNormal = XMVector3Normalize(-XMLoadFloat3(&m_position));

   // Rotate the x-axis around the y-axis.
   // This is a 90-degree angle from the normal, in the xz-plane.
   // This is the x-axis rotation.
   XMVECTOR xAxisRotation = XMVector3Normalize(XMVectorSet(XMVectorGetZ(facingNormal), 0.f, -XMVectorGetX(facingNormal), 0.f));

   // Create a third normal to satisfy the conditions of a rotation matrix.
   // The cross product  of the other two normals is at a 90-degree angle to
   // both normals. (Normalize the cross product to avoid floating-point math
   // errors.)
   // Note how the cross product will never be a zero-matrix because the two normals
   // are always at a 90-degree angle from one another.
   XMVECTOR yAxisRotation = XMVector3Normalize(XMVector3Cross(facingNormal, xAxisRotation));

   // Construct the 4x4 rotation matrix.

   // Rotate the quad to face the user.
   XMMATRIX rotationMatrix = XMMATRIX(
       xAxisRotation,
       yAxisRotation,
       facingNormal,
       XMVectorSet(0.f, 0.f, 0.f, 1.f)
       );

   // Position the quad.
   const XMMATRIX modelTranslation = XMMatrixTranslationFromVector(XMLoadFloat3(&m_position));

   // The view and projection matrices are provided by the system; they are associated
   // with holographic cameras, and updated on a per-camera basis.
   // Here, we provide the model transform for the sample hologram. The model transform
   // matrix is transposed to prepare it for the shader.
   XMStoreFloat4x4(&m_modelConstantBufferData.model, XMMatrixTranspose(rotationMatrix * modelTranslation));

Merender hologram terlampir

Untuk contoh ini, kami juga memilih untuk merender hologram dalam sistem koordinat SpatialLocatorAttachedReferenceFrame, yang merupakan tempat kami memposisikan hologram. (Jika kami memutuskan untuk merender menggunakan sistem koordinat lain, kami perlu memperoleh transformasi dari sistem koordinat bingkai referensi yang terpasang pada perangkat ke sistem koordinat tersebut.)

Dari HolographicTagAlongSampleMain::Render:

   // The view and projection matrices for each holographic camera will change
   // every frame. This function refreshes the data in the constant buffer for
   // the holographic camera indicated by cameraPose.
   pCameraResources->UpdateViewProjectionBuffer(
       m_deviceResources,
       cameraPose,
       m_referenceFrame->GetStationaryCoordinateSystemAtTimestamp(prediction->Timestamp)
       );

Itu saja! Hologram sekarang akan "mengejar" posisi yang 2 meter di depan arah tatapan pengguna.

Catatan

Contoh ini juga memuat konten tambahan - lihat StationaryQuadRenderer.cpp.

Menangani kehilangan pelacakan

Ketika perangkat tidak dapat menemukan dirinya di dunia, aplikasi mengalami "kehilangan pelacakan". Windows Mixed Reality aplikasi harus dapat menangani gangguan tersebut pada sistem pelacakan posisi. Gangguan ini dapat diamati, dan respons yang dibuat, dengan menggunakan peristiwa LocatabilityChanged pada SpatialLocator default.

Dari AppMain::SetHolographicSpace:

   // Be able to respond to changes in the positional tracking state.
   m_locatabilityChangedToken =
       m_locator->LocatabilityChanged +=
           ref new Windows::Foundation::TypedEventHandler<SpatialLocator^, Object^>(
               std::bind(&HolographicApp1Main::OnLocatabilityChanged, this, _1, _2)
               );

Saat aplikasi Anda menerima peristiwa LocatabilityChanged, aplikasi dapat mengubah perilaku sesuai kebutuhan. Misalnya, dalam status PositionalTrackingInhibited, aplikasi Anda dapat menjeda operasi normal dan merender hologram tag-along yang menampilkan pesan peringatan.

Templat aplikasi Windows Holographic dilengkapi dengan handler LocatabilityChanged yang sudah dibuat untuk Anda. Secara default, ini menampilkan peringatan di konsol debug saat pelacakan posisional tidak tersedia. Anda dapat menambahkan kode ke handler ini untuk memberikan respons sesuai kebutuhan dari aplikasi Anda.

Dari AppMain.cpp:

   void HolographicApp1Main::OnLocatabilityChanged(SpatialLocator^ sender, Object^ args)
   {
       switch (sender->Locatability)
       {
       case SpatialLocatability::Unavailable:
           // Holograms cannot be rendered.
           {
               String^ message = L"Warning! Positional tracking is " +
                                           sender->Locatability.ToString() + L".\n";
               OutputDebugStringW(message->Data());
           }
           break;

       // In the following three cases, it is still possible to place holograms using a
       // SpatialLocatorAttachedFrameOfReference.
       case SpatialLocatability::PositionalTrackingActivating:
           // The system is preparing to use positional tracking.

       case SpatialLocatability::OrientationOnly:
           // Positional tracking has not been activated.

       case SpatialLocatability::PositionalTrackingInhibited:
           // Positional tracking is temporarily inhibited. User action may be required
           // in order to restore positional tracking.
           break;

       case SpatialLocatability::PositionalTrackingActive:
           // Positional tracking is active. World-locked content can be rendered.
           break;
       }
   }

Pemetaan spasial

API pemetaan spasial menggunakan sistem koordinat untuk mendapatkan transformasi model untuk jala permukaan.

Lihat juga