Sdílet prostřednictvím


Souřadnicové systémy v DirectX

Poznámka:

Tento článek se týká starších nativních rozhraní API WinRT. Pro nové nativní projekty aplikací doporučujeme použít rozhraní API OpenXR.

Souřadnicové systémy tvoří základ pro prostorové porozumění, které nabízí rozhraní API windows Mixed Reality.

Dnešní sesedaná virtuální realita nebo jednopokojová zařízení VR vytvářejí pro svůj sledovaný prostor jeden primární souřadnicový systém. Zařízení hybridní reality, jako je HoloLens, jsou navržená pro rozsáhlá nedefinovaná prostředí, přičemž zařízení zjišťuje okolí a učí se o něm, jak uživatel prochází. Zařízení se přizpůsobuje neustálému zlepšování znalostí o místnostech uživatele, ale vede ke souřadnicovým systémům, které mění jejich vztah mezi sebou v průběhu životnosti aplikací. Windows Mixed Reality podporuje široké spektrum zařízení, od sedících imerzivních náhlavních souprav prostřednictvím světově připojených referenčních snímků.

Poznámka:

Fragmenty kódu v tomto článku aktuálně demonstrují použití C++/CX místo C++17 kompatibilních s C++/WinRT, jak se používá v šabloně holografického projektu C++. Koncepty jsou ekvivalentní pro projekt C++/WinRT, i když budete muset kód přeložit.

Prostorové souřadnicové systémy ve Windows

Základním typem používaným k odůvodnění skutečných souřadnicových systémů ve Windows je SpatialCoordinateSystem. Instance tohoto typu představuje libovolný souřadnicový systém, který poskytuje metodu pro získání transformačních maticových dat, které můžete použít k transformaci mezi dvěma souřadnicovými systémy, aniž byste porozuměli podrobnostem každého z nich.

Metody, které vracejí prostorové informace, přijmou parametr SpatialCoordinateSystem, který vám umožní rozhodnout se o souřadnicovém systému, ve kterém je pro tyto souřadnice nejužitečnější. Prostorové informace jsou reprezentovány jako body, paprsky nebo objemy v okolí uživatele a jednotky pro tyto souřadnice budou vždy v metrech.

SpatialCoordinateSystem má dynamický vztah s jinými souřadnicovými systémy, včetně těch, které představují pozici zařízení. V každém okamžiku může zařízení vyhledat některé souřadnicové systémy, nikoli jiné. U většiny souřadnicových systémů musí být vaše aplikace připravená ke zpracování časových období, během kterých se nedají najít.

Vaše aplikace by neměla vytvářet spatialCoordinateSystems přímo – spíše by se měly využívat prostřednictvím rozhraní API pro vnímání. V rozhraních API pro vnímání existují tři primární zdroje souřadnicových systémů, z nichž každá mapuje na koncept popsaný na stránce Souřadnicové systémy :

Všechny souřadnicové systémy vrácené těmito objekty jsou rukou vpravo, s +y nahoru, +x doprava a +z zpět. Vzpomeňte si, který směr osy z ukazujete prsty na levé nebo pravé straně v kladném směru x a kuřičíte je do pozitivního směru y. Směr, kterým se palec nachází směrem k vám nebo od vás, je směr, kterým boduje osa Z pro daný souřadnicový systém. Následující obrázek znázorňuje tyto dva souřadnicové systémy.

Systémy souřadnic zleva a zprava
Systémy souřadnic zleva a zprava

Pomocí SpatialLocator třídy vytvořit buď připojený nebo statický rámec odkazu bootstrap do SpatialCoordinateSystem na základě pozice HoloLens. Další informace o tomto procesu najdete v další části.

Umístění hologramů na světě pomocí prostorové fáze

K systému souřadnic pro neprůhledné náhlavní soupravy Windows Mixed Reality se přistupuje pomocí statické vlastnosti SpatialStageFrameOfReference::Current . Toto rozhraní API poskytuje:

  • Souřadnicový systém
  • Informace o tom, zda je přehrávač sedí nebo mobilní
  • Hranice bezpečné oblasti pro procházky, pokud je hráč mobilní
  • Označení, jestli je náhlavní souprava směrová.
  • Obslužná rutina události pro aktualizace prostorové fáze.

Nejprve získáme prostorovou fázi a přihlásíme se k odběru aktualizací:

Inicializace prostorové fáze kódu

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);
}

V metodě OnCurrentChanged by vaše aplikace měla prozkoumat prostorovou fázi a aktualizovat prostředí přehrávače. V tomto příkladu poskytujeme vizualizaci hranice fáze a počáteční pozici určenou uživatelem a rozsahem zobrazení a rozsahu vlastností pohybu fáze. Také se vrátíme do vlastního statického souřadnicového systému, když se fáze nedá poskytnout.

Kód pro aktualizaci prostorové fáze

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);
        }
    }
}

Sada vrcholů, které definují hranici fáze, jsou k dispozici ve směru hodinových ručiček. Prostředí Windows Mixed Reality nakreslí plot na hranici, když se k němu uživatel přiblíží, ale možná budete chtít trojúhelníkovat choditelnou oblast pro vlastní účely. K triangularizaci fáze lze použít následující algoritmus.

Kód triangularizace prostorové fáze

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;
}

Umístěte hologramy na svět pomocí statického rámce odkazu.

SpatialStationaryFrameOfReference třída představuje rámec odkazu, který zůstává statický vzhledem k okolí uživatele při pohybu uživatele. Tento rámec odkazu určuje prioritu udržování souřadnic stabilních v blízkosti zařízení. Jedním z klíčových použití spatialStationaryFrameOfReference je jednat jako základní světový souřadnicový systém v vykreslovacím modulu při vykreslování hologramů.

Chcete-li získat SpatialStationaryFrameOfReference, použijte SpatialLocator třídy a volání CreateStationaryFrameOfReferenceAtCurrentLocation.

Z kódu šablony aplikace Pro Windows Holographic:

           // 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();
  • Statické referenční rámy jsou navrženy tak, aby poskytovaly nejvhodnější pozici vzhledem k celkovému prostoru. Jednotlivé pozice v rámci referenčního rámce se mohou mírně posunovat. To je normální, protože se zařízení dozví více o prostředí.
  • Pokud se vyžaduje přesné umístění jednotlivých hologramů, měl by být prostorovýanchor použit k ukotvení jednotlivých hologramů na pozici v reálném světě – například bod, který uživatel označuje jako zvláštní zájem. Pozice ukotvení nejsou posunuty, ale lze je opravit; kotva použije opravenou pozici začínající v dalším rámečku po dokončení opravy.

Umístění hologramů na světě pomocí prostorových ukotvení

Prostorové kotvy představují skvělý způsob, jak umístit hologramy na určité místo v reálném světě a systém zajišťuje, aby ukotvení zůstalo v čase. Toto téma vysvětluje, jak vytvořit a používat ukotvení a jak pracovat s daty ukotvení.

Prostorovýanchor můžete vytvořit na libovolné pozici a orientaci v rámci spatialCoordinateSystem podle vašeho výběru. Zařízení musí být schopno najít tento souřadnicový systém v tuto chvíli a systém nesmí dosáhnout svého limitu prostorových ukotvení.

Po definování se souřadnicový systém SpatialAnchor průběžně upravuje tak, aby zachoval přesnou polohu a orientaci jejího počátečního umístění. Pomocí tohoto spatialAnchoru pak můžete vykreslovat hologramy, které se zobrazí pevně v okolí uživatele v tomto přesném umístění.

Efekty úprav, které udržují ukotvení na místě, se zvětšují, protože se zvyšuje vzdálenost od ukotvení. Měli byste se vyhnout vykreslování obsahu vzhledem k ukotvení, které je více než asi 3 metry od původu této kotvy.

Vlastnost CoordinateSystem získá souřadnicový systém, který umožňuje umístit obsah vzhledem k ukotvení, s uvolněním, když zařízení upraví přesné umístění ukotvení.

Ke správě těchto úprav použijte RawCoordinateSystem Vlastnost a odpovídající RawCoordinateSystemAdjusted událost.

SpatialAnchor můžete zachovat místně pomocí třídy SpatialAnchorStore a pak ji získat zpět v budoucí relaci aplikace na stejném zařízení HoloLens.

Vytvoření prostorovýchanchorů pro holografický obsah

V této ukázce kódu jsme upravili šablonu aplikace Pro Windows Holographic tak, aby se při zjištění gesta stisknutí tlačítka vytvořily ukotvení. Datová krychle se pak umístí do ukotvení během průchodu vykreslení.

Vzhledem k tomu, že pomocná třída podporuje více ukotvení, můžeme umístit tolik krychlí, kolik chceme použít tuto ukázku kódu!

Poznámka:

ID ukotvení jsou něco, co v aplikaci řídíte. V tomto příkladu jsme vytvořili schéma pojmenování, které je sekvenční na základě počtu ukotvení aktuálně uložených v kolekci ukotvení aplikace.

   // 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);
           }
       }
   }

Asynchronně načítá a mezipaměť SpatialAnchorStore

Pojďme se podívat, jak napsat sampleSpatialAnchorHelper třídy, která pomáhá zvládnout tuto trvalost, včetně:

  • Uložení kolekce ukotvení v paměti indexované klíčem Platform::String
  • Načítání ukotvení ze systému SpatialAnchorStore, které je oddělené od místní kolekce v paměti.
  • Uložení místní kolekce ukotvení do SpatialAnchorStore, když se aplikace rozhodne.

Tady je postup, jak uložit SpatialAnchor objekty v SpatialAnchorStore.

Při spuštění třídy požádáme SpatialAnchorStore asynchronně. To zahrnuje vstupně-výstupní operace systému, protože rozhraní API načte úložiště ukotvení, a toto rozhraní API je vytvořené jako asynchronní, aby vstupně-výstupní operace neblokovaly.

   // 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;
   });

Dostanete prostorovýanchorStore, který můžete použít k uložení ukotvení. Toto je IMapView, který přidruží klíčové hodnoty, které jsou Strings, s datovými hodnotami, které jsou SpatialAnchors. V našem ukázkovém kódu to uložíme do proměnné člena privátní třídy, která je přístupná prostřednictvím veřejné funkce naší pomocné třídy.

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

Poznámka:

Nezapomeňte připojit události pozastavení/obnovení, abyste uložili a načetli úložiště ukotvení.

   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();
   }

Uložení obsahu do úložiště ukotvení

Když systém pozastaví aplikaci, musíte uložit prostorové kotvy do úložiště ukotvení. Můžete se také rozhodnout uložit kotvy do úložiště ukotvení v jiných případech, jak zjistíte, že je nezbytné pro implementaci vaší aplikace.

Až budete připraveni vyzkoušet uložení ukotvení v paměti do SpatialAnchorStore, můžete procházet kolekci a pokusit se je uložit.

   // 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;
   }

Načtení obsahu z úložiště ukotvení při obnovení aplikace

Uložené kotvy můžete obnovit v AnchorStore tak, že je přenesete z IMapView úložiště ukotvení do vlastní databáze prostorovýchanchorů v paměti, když se vaše aplikace obnoví nebo kdykoliv obnoví.

Pokud chcete obnovit ukotvení z SpatialAnchorStore, obnovte každou z nich, která vás zajímá, do vlastní kolekce v paměti.

K přidružení řetězců k prostorovýmanchorům, které vytvoříte, potřebujete vlastní databázi v paměti. V našem ukázkovém kódu se rozhodneme použít Windows::Foundation::Collections::IMap k uložení ukotvení, což usnadňuje použití stejného klíče a datové hodnoty pro 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;

Poznámka:

Kotva, která se obnoví, nemusí být okamžitě lokovatelná. Může to být například kotva v samostatné místnosti nebo v jiné budově úplně. Ukotvení načtená z AnchorStore by měla být před použitím testována na lokačnost.


Poznámka:

V tomto ukázkovém kódu načteme všechny kotvy z AnchorStore. To není požadavek; aplikace by mohla stejně dobře vybrat a zvolit určitou podmnožinu ukotvení pomocí hodnot klíče string, které jsou pro vaši implementaci smysluplné.

   // 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);
           }
       }
   }

V případě potřeby vymažte úložiště ukotvení.

Někdy potřebujete vymazat stav aplikace a napsat nová data. Tady je postup, jak to udělat pomocí SpatialAnchorStore.

Použití naší pomocné třídy je téměř zbytečné zabalit funkci Clear. V ukázkové implementaci se tak rozhodneme, protože naše pomocná třída má zodpovědnost za vlastnictví instance 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();
       }
   }

Příklad: Korelace souřadnicových systémů ukotvení k systémům souřadnic s statickým referenčním rámcem

Řekněme, že máte ukotvení a chcete něco v souřadnicovém systému ukotvení propojit s objektem SpatialStationaryReferenceFrame, který už používáte pro svůj další obsah. Pomocí TryGetTransformTo můžete získat transformaci ze souřadnicového systému ukotvení na pevný referenční rámec:

   // 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;
   }

Tento proces je užitečný dvěma způsoby:

  1. Řekne vám, jestli jsou dva referenční rámce srozumitelné vzhledem k sobě navzájem, a;
  2. Pokud ano, poskytuje transformaci, která se dá přejít přímo z jednoho souřadnicového systému na druhý.

S těmito informacemi rozumíte prostorovým vztahům mezi objekty mezi dvěma referenčními snímky.

Pro vykreslování můžete často získat lepší výsledky seskupením objektů podle jejich původního referenčního rámce nebo ukotvení. Pro každou skupinu proveďte samostatný průchod výkresem. Matice zobrazení jsou přesnější pro objekty s transformacemi modelu, které jsou vytvořeny původně pomocí stejného souřadnicového systému.

Vytvoření hologramů pomocí rámce odkazu připojeného zařízením

Existují situace, kdy chcete vykreslit hologram, který zůstane připojený k umístění zařízení, například panel s informacemi o ladění nebo informační zprávou, když zařízení dokáže určit pouze orientaci, a ne její pozici v prostoru. K tomu použijeme připojený rámec odkazu.

Třída SpatialLocatorAttachedFrameOfReference definuje souřadnicové systémy, které jsou relativní vzhledem k zařízení místo skutečného světa. Tento rámec má pevný nadpis vzhledem k okolí uživatele, který ukazuje ve směru, ke kterému měl uživatel přístup při vytváření referenčního rámce. Od té době jsou všechny orientace v tomto rámci odkazu relativní k danému pevnému nadpisu, i když uživatel zařízení otočí.

Pro HoloLens je původ souřadnicového systému tohoto rámce umístěn uprostřed otáčení hlavy uživatele, aby jeho pozice nebyla ovlivněna otočením hlavy. Aplikace může určit posun vzhledem k tomuto bodu, aby se hologramy umístily před uživatelem.

Chcete-li získat SpatialLocatorAttachedFrameOfReference, použijte SpatialLocator třída a volání CreateAttachedFrameOfReferenceAtCurrentHeading.

To platí pro celou řadu zařízení s Windows Mixed Reality.

Použití referenčního rámce připojeného k zařízení

Tyto části hovoří o tom, co jsme změnili v šabloně aplikace Pro Windows Holographic, aby bylo možné pomocí tohoto rozhraní API povolit referenční rámec připojený k zařízení. Tento "připojený" hologram bude fungovat společně se statickými nebo ukotvenými hologramy a může se použít také v případě, že zařízení dočasně nemůže najít svou pozici na světě.

Nejprve jsme šablonu změnili tak, aby ukládal spatialLocatorAttachedFrameOfReference místo SpatialStationaryFrameOfReference:

Z HolographicTagAlongSampleMain.h:

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

Z HolographicTagAlongSampleMain.cpp:

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

Během aktualizace teď získáme souřadnicový systém v časovém razítku získaném z predikce rámce.

   // 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);

Získání pozice prostorového ukazatele a sledování pohledu uživatele

Chceme, aby náš ukázkový hologram sledoval pohled uživatele, podobně jako holografické prostředí může sledovat pohled uživatele. K tomu potřebujeme získat SpatialPointerPose ze stejného časového razítka.

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

Tato spatialPointerPose obsahuje informace potřebné k umístění hologramu podle aktuálního nadpisu uživatele.

Pro pohodlí uživatelů používáme lineární interpolaci ("lerp") k vyhlazování změny pozice v určitém časovém období. To je pro uživatele pohodlnější než uzamknutí hologramu na pohled. Připnutí značky na pozici hologramu nám také umožňuje stabilizovat hologram tím, že tlumí pohyb. Pokud bychom to neudělali, uživatel by viděl hologram jitter z důvodu toho, co se obvykle považuje za nečitelné pohyby hlavy uživatele.

From StaticyQuadRenderer::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);
   }

Poznámka:

V případě panelu ladění se můžete rozhodnout, že hologram přeložíte na stranu trochu tak, aby nepřekáželo vašemu zobrazení. Tady je příklad toho, jak to můžete udělat.

Pro StaticyQuadRenderer::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));
       */

Otočení hologramu proti fotoaparátu

K umístění hologramu nestačí, což je v tomto případě čtyřúhelník; Objekt musíme také otočit tak, aby se na uživatele vystřídal. K této obměně dochází ve světě, protože tento typ plakátování umožňuje hologramu zůstat součástí prostředí uživatele. Plakáty s prostorem zobrazení nejsou tak pohodlné, protože hologram se uzamkne na orientaci zobrazení; v takovém případě byste také museli interpolovat mezi maticemi levého a pravého pohledu, abyste získali transformaci prostorového plakátu, která nenarušuje stereo vykreslování. V této části otočíme osy X a Z tak, aby se uživateli střídaly.

Ze StaticyQuadRenderer::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));

Vykreslení připojeného hologramu

V tomto příkladu se také rozhodneme vykreslit hologram v souřadnicovém systému SpatialLocatorAttachedReferenceFrame, kde jsme umístili hologram. (Pokud bychom se rozhodli vykreslit pomocí jiného souřadnicového systému, museli bychom získat transformaci ze souřadnicového systému připojeného k zařízení na tento souřadnicový systém.)

Z 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)
       );

A je to! Hologram teď "chase" pozici, která je 2 metry před směrem pohledů uživatele.

Poznámka:

Tento příklad také načte další obsah – viz StationaryQuadRenderer.cpp.

Zpracování ztráty sledování

Když zařízení nemůže najít na světě, aplikace zaznamená "ztrátu sledování". Aplikace windows Mixed Reality by měly být schopny takové narušení systému sledování polohy zvládnout. Tyto přerušení je možné pozorovat a odpovědi vytvořené pomocí události LocatabilityChanged ve výchozím spatiallocatoru.

Z 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)
               );

Když vaše aplikace obdrží událost LocatabilityChanged, může podle potřeby změnit chování. Například ve stavu PositionalTrackingInhibited může vaše aplikace pozastavit normální provoz a vykreslit značku na hologramu , který zobrazí zprávu s upozorněním.

Šablona aplikace pro Windows Holographic obsahuje již vytvořenou obslužnou rutinu LocatabilityChanged. Ve výchozím nastavení se v konzole ladění zobrazí upozornění, když je sledování pozice nedostupné. Do této obslužné rutiny můžete přidat kód, který poskytne odpověď podle potřeby z vaší aplikace.

Z 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;
       }
   }

Prostorové mapování

Rozhraní API prostorového mapování využívají souřadnicové systémy k získání transformací modelu pro povrchové sítě.

Viz také