Souřadnicové systémy v DirectX

Poznámka

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

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

Dnešní usazená zařízení VR nebo jednopokojová virtuální realita vytvářejí jeden primární souřadnicový systém pro svůj sledovaný prostor. Mixed Reality zařízení, jako je HoloLens, jsou navržená pro velká nedefinovaná prostředí, přičemž zařízení zjišťuje a učí se o svém okolí, jak uživatel prochází. Zařízení se přizpůsobuje neustálému zlepšování znalostí o místnostech uživatele, ale výsledkem je souřadnicové systémy, které mění jejich vztah k sobě navzájem v průběhu životnosti aplikací. Windows Mixed Reality podporuje široké spektrum zařízení od sedících imerzivních náhlavních souprav až po referenční rámečky připojené ke světu.

Poznámka

Fragmenty kódu v tomto článku v současné době ukazují použití C++/CX místo C++17 kompatibilních C++/WinRT, jak se používá v šabloně holografického projektu C++. Koncepty jsou ekvivalentem pro projekt C++/WinRT, ale kód budete muset přeložit.

Prostorové souřadnicové systémy ve Windows

Základním typem, který se používá k odůvodnění reálný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 o systému souřadnic, 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í najít některé souřadnicové systémy, jiné ne. U většiny souřadnicových systémů musí být aplikace připravená na zpracování časových období, během kterých je nelze 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ý se mapuje na koncept popsaný na stránce Souřadnicové systémy :

Všechny souřadnicové systémy vrácené těmito objekty jsou pravé ruky, s +y nahoru, +x doprava a +z dozadu. Můžete si vzpomenout, který směr kladná osa z boduje, když ukážete prsty levé nebo pravé ruky do kladného směru x a zakroužkujete je do kladného směru y. Směr, kterým palec ukazuje směrem k vám nebo směrem od vás, je směr, kterým boduje kladná osa Z pro daný souřadnicový systém. Následující obrázek znázorňuje tyto dva souřadnicové systémy.

Levostranné a pravé souřadnicové systémy
Levostranné a pravé souřadnicové systémy

Použití SpatialLocator třídy vytvořit buď připojený nebo statický rámec odkazu bootstrap do SpatialCoordinateSystem založené na pozici HoloLens. Další informace o tomto procesu najdete v další části.

Umístění hologramů do světa pomocí prostorové fáze

Souřadnicový systém pro neprůhledné Windows Mixed Reality imerzivní náhlavní soupravy je přístupný pomocí statické vlastnosti SpatialStageFrameOfReference::Current. Toto rozhraní API poskytuje:

  • Souřadnicový systém
  • Informace o tom, zda je hráč usazen nebo mobilní
  • Hranice bezpečné oblasti pro procházky po okolí, pokud je hráč pohyblivý
  • Údaj o tom, 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í:

Kód pro inicializaci prostorové fáze

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 zkontrolovat prostorovou fázi a aktualizovat prostředí přehrávače. V tomto příkladu poskytujeme vizualizaci hranice fáze a počáteční pozice zadané uživatelem a oblasti zobrazení a rozsahu vlastností pohybu fáze. Také se vrátíme do našeho vlastního statického souřadnicového systému, když není možné poskytnout fázi.

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 v pořadí po směru hodinových ručiček. Windows Mixed Reality shell nakresluje plot na hranici, když se k němu uživatel přiblíží, ale možná budete chtít trojúhelníkovat pochůznou oblast pro vlastní účely. K triangularizaci fáze lze použít následující algoritmus.

Kód pro triangularizaci 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ění hologramů do světa pomocí nehybného referenčního rámce

SpatialStationaryFrameOfReference Třída představuje referenční rámec, který zůstává nehybný vzhledem k okolí uživatele při pohybu uživatele. Tento referenční rámec určuje prioritu zachování stabilních souřadnic v blízkosti zařízení. Jedním z klíčových použití SpatialStationaryFrameOfReference je, že při vykreslování hologramů funguje jako základní souřadnicový systém světa v rámci vykreslovacího modulu.

Chcete-li získat SpatialStationaryFrameOfReference, použijte Třídu SpatialLocator 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 nejlepší polohu vzhledem k celkovému prostoru. Jednotlivé pozice v rámci daného referenčního rámce se můžou 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 se k ukotvení jednotlivých hologramů na pozici v reálném světě použít spatialAnchor – například bod, který uživatel označuje jako zajímavý. Pozice ukotvení se neodklánějí, ale mohou být opraveny; kotva použije opravenou pozici počínaje dalším snímkem po provedení opravy.

Umístění hologramů do světa pomocí prostorových ukotvení

Prostorové kotvy představují skvělý způsob, jak umístit hologramy na konkrétní místo v reálném světě, přičemž systém zajišťuje, aby kotva zůstala na místě v průběhu času. Toto téma vysvětluje, jak vytvořit a používat kotvu a jak pracovat s daty ukotvení.

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

Po definování se souřadnicový systém spatialAnchor neustále upravuje, aby byla zachována přesná poloha a orientace jeho počátečního umístění. Potom můžete použít tento Prostorovýanchor k vykreslení hologramů, které se zobrazí pevně v okolí uživatele v tomto přesném umístění.

Účinky úprav, které udržují kotvu na místě, se zvětšují s rostoucí vzdáleností od kotvy. Měli byste se vyhnout vykreslování obsahu vzhledem k ukotvení, které je více než 3 metry od jeho původu.

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

Pomocí rawCoordinateSystem vlastnost a odpovídající RawCoordinateSystemAdjusted událost ke správě těchto úprav sami.

Zachování a sdílení prostorových ukotvení

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

Pomocí Azure Spatial Anchors můžete vytvořit odolnou cloudovou kotvu z místního SpatialAnchoru, kterou pak vaše aplikace může najít na více zařízeních HoloLens, iOS a Android. Sdílením společné prostorové kotvy na více zařízeních může každý uživatel zobrazit obsah vykreslený vzhledem k dané ukotvení ve stejném fyzickém umístění v reálném čase.

Azure Spatial Anchors můžete také použít k asynchronnímu hologramu na zařízeních HoloLens, iOS a Android. Sdílením odolné cloudové prostorové kotvy může několik zařízení sledovat stejný trvalý hologram v průběhu času, i když se tato zařízení nenacházejí současně.

Pokud chcete začít vytvářet sdílená prostředí v aplikaci HoloLens, vyzkoušejte 5minutový rychlý start azure Spatial Anchors HoloLens.

Jakmile začnete používat Azure Spatial Anchors, můžete vytvářet a vyhledávat kotvy na HoloLensu. Návody jsou k dispozici také pro Android a iOS , které umožňují sdílet stejné ukotvení na všech zařízeních.

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

Pro tuto ukázku kódu jsme upravili šablonu aplikace Windows Holographic tak, aby se při zjištění gesta stisknutí 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 datových krychlí, kolik chceme použít tuto ukázku kódu.

Poznámka

ID ukotvení jsou něco, co v aplikaci ovlá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 ukládá do mezipaměti spatialAnchorStore

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

  • Ukládání kolekce ukotvení v paměti indexovaných klíčem Platform::String.
  • Načítají se kotvy ze systémového úložiště SpatialAnchorStore, které se uchovává odděleně od místní kolekce v paměti.
  • Uložení místní kolekce ukotvení v paměti do SpatialAnchorStore, když se to aplikace rozhodne.

Tady je postup, jak uložit objekty SpatialAnchor vSpatialAnchorStore.

Při spuštění třídy požadujeme SpatialAnchorStore asynchronně. To zahrnuje vstupně-výstupní operace systému, protože rozhraní API načítá úložiště ukotvení, a toto rozhraní API je 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žuje klíčové hodnoty, které jsou Strings, s datovými hodnotami, které jsou SpatialAnchors. V našem ukázkovém kódu ho 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 aplikaci pozastaví, musíte prostorové kotvy uložit do úložiště ukotvení. Můžete se také rozhodnout ukládat kotvy do úložiště ukotvení v jiných časech, jak zjistíte, že je to nezbytné pro implementaci vaší aplikace.

Až budete připraveni zkusit uložit ukotvení v paměti do spatialAnchorStore, můžete procházet kolekci a pokusit se uložit každou z nich.

   // 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 v AnchorStore můžete obnovit tak, že je při obnovení aplikace nebo kdykoli přenesete z objektu IMapView v úložišti ukotvení do vlastní databáze SpatialAnchors v paměti.

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 SpatialAnchors v paměti. V našem ukázkovém kódu se rozhodneme k uložení ukotvení použít Windows::Foundation::Collections::IMap, 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

Obnovená kotva nemusí být okamžitě vyhledatelná. Může to být například kotva v samostatné místnosti nebo úplně v jiné budově. Kotvy načtené z anchorstore by měly být před použitím testovány z hlediska lokalizovatelnosti.


Poznámka

V tomto ukázkovém kódu načteme všechny kotvy z AnchorStore. To není požadavek; Vaše aplikace může stejně dobře vybrat a vybrat určitou podmnožinu ukotvení pomocí hodnot řetězcového klíče, 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 je potřeba vymazat stav aplikace a napsat nová data. Tady je postup, jak to uděláte s prostorovýmanchorstorem.

Pomocí naší pomocné třídy je téměř zbytečné zabalit funkci Clear. V naší ukázkové implementaci se tak rozhodneme, protože naše pomocná třída nese odpově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: Spojování kotevních souřadnicových systémů se souřadnicovými systémy nehybné referenční soustavy

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

   // 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 pro vás užitečný dvěma způsoby:

  1. To vám řekne, zda lze dva referenční rámce pochopit vzhledem k sobě navzájem, a;
  2. Pokud ano, poskytuje transformaci, která přejde přímo z jednoho souřadnicového systému do druhého.

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

Při vykreslování můžete často dosáhnout lepších výsledků 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ýkresu. Matice zobrazení jsou přesnější pro objekty s transformacemi modelu, které jsou původně vytvořeny pomocí stejného souřadnicového systému.

Vytváření hologramů pomocí referenčního rámce připojeného zařízením

Někdy chcete vykreslit hologram, který zůstane připojený k poloze zařízení, například panel s informacemi o ladění nebo informační zprávu, kdy je zařízení schopné určit pouze orientaci, a ne jeho polohu v prostoru. K tomu použijeme připojený referenční rámec.

Třída SpatialLocatorAttachedFrameOfReference definuje souřadnicové systémy, které jsou relativní k zařízení, nikoli k reálnému světu. Tento rámec má pevný nadpis vzhledem k okolí uživatele, který ukazuje ve směru, kterým byl uživatel při vytváření referenčního rámce. Od té doby jsou všechny orientace v tomto referenčním rámci relativní vzhledem k tomuto pevnému nadpisu, i když uživatel otáčí zařízení.

U HoloLensu je původ souřadnicového systému tohoto rámce umístěn ve středu otáčení hlavy uživatele, takže jeho pozice není ovlivněna otáčením hlavy. Aplikace může určit posun vzhledem k tomuto bodu a umístit hologramy před uživatelem.

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

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

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

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

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

Z Souboru HolographicTagAlongSampleMain.h:

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

Z Souboru 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ý shell 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);

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

Pro pohodlí uživatele používáme lineární interpolaci ("lerp") k vyhlazení změny polohy v určitém časovém období. To je pro uživatele pohodlnější než uzamčení hologramu před jeho pohledem. Umístění hologramu značky nám také umožňuje stabilizovat hologram tlumením pohybu. Pokud bychom toto tlumení neudělali, uživatel by viděl chvění hologramu kvůli tomu, co se obvykle považuje za nepostřehnutelné pohyby hlavy uživatele.

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

Poznámka

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

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

Otočení hologramu tak, aby se otočil ke kameře

Nestačí umístit hologram, což je v tomto případě čtyřúhelník; musíme také otočit objekt tak, aby byl tváří v tvář uživateli. K této rotaci dochází ve světovém prostoru, protože tento typ reklamních plakátů umožňuje, aby hologram zůstal součástí prostředí uživatele. Zobrazení-prostor pro plakáty není tak pohodlné, protože hologram se uzamkne na orientaci displeje; V takovém případě byste také museli interpolovat mezi levou a pravou maticí zobrazení, abyste získali transformaci reklamního prostoru, která nenaruší stereo vykreslování. Tady se otočíme na osách X a Z tak, abychom se dostali k uživateli.

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

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 referenčního rámce připojeného zařízením 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 to je vše! Hologram teď bude "pronásledovat" pozici, která je 2 metry před směrem pohledu uživatele.

Poznámka

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

Zpracování ztráty sledování

Když se zařízení nemůže najít na světě, aplikace zaznamená "ztrátu sledování". Windows Mixed Reality aplikace by měly být schopny takové narušení systému sledování polohy zvládnout. Tato přerušení lze pozorovat a vytvářet odpovědi 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 hologram s popisky , který zobrazí varovnou zprávu.

Šablona aplikace Pro Windows Holographic se dodává s již vytvořenou obslužnou rutinou LocatabilityChanged. Ve výchozím nastavení se v konzole ladění zobrazí upozornění na nedostupné sledování pozice. 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é