Koordinátarendszerek a DirectX-ben

Megjegyzés

Ez a cikk az örökölt WinRT natív API-kkal kapcsolatos. Új natív alkalmazásprojektek esetén az OpenXR API használatát javasoljuk.

A koordinátarendszerek képezik az Windows Mixed Reality API-k által kínált térbeli megértés alapját.

A mai ülő VR- vagy egyszobás VR-eszközök egyetlen elsődleges koordinátarendszert hoznak létre a követett térhez. Mixed Reality olyan eszközök, mint a HoloLens, nagy, nem definiált környezetekhez vannak kialakítva, és az eszköz felfedezi és megismeri a környezetét, miközben a felhasználó jár. Az eszköz alkalmazkodik ahhoz, hogy folyamatosan bővítse a felhasználói szobákkal kapcsolatos ismereteket, de olyan koordinátarendszereket eredményez, amelyek megváltoztatják egymáshoz való viszonyukat az alkalmazások élettartama során. Windows Mixed Reality az eszközök széles skáláját támogatja, az ülő, modern mikrofonos fejhallgatóktól a világhoz csatlakoztatott referenciakeretekig.

Megjegyzés

A jelen cikkben szereplő kódrészletek jelenleg a C++/CX használatát mutatják be a C++17-kompatibilis C++/WinRT helyett, a C++ holografikus projektsablonban használt módon. A fogalmak egyenértékűek egy C++/WinRT-projekt esetében, bár a kódot le kell fordítania.

Térbeli koordinátarendszerek a Windowsban

A Windows valós koordinátarendszereivel kapcsolatos alapvető típus a SpatialCoordinateSystem. Az ilyen típusú példányok egy tetszőleges koordinátarendszert jelölnek, amely lehetővé teszi az átalakítási mátrix adatainak lekérését, amellyel két koordinátarendszer közötti átalakítást végezhet anélkül, hogy az egyes rendszerek részleteinek megértése nélkül használná az átalakítást.

A térbeli információkat visszavevő metódusok elfogadnak egy SpatialCoordinateSystem paramétert, amely lehetővé teszi, hogy eldöntse, melyik koordinátarendszerben a legpontosabban adja vissza ezeket a koordinátákat. A térbeli információ pontként, sugarként vagy kötetként jelenik meg a felhasználó környezetében, és ezeknek a koordinátáknak az egységei mindig méterben lesznek.

A SpatialCoordinateSystem dinamikus kapcsolatban áll más koordinátarendszerekkel, beleértve az eszköz helyzetét képviselő rendszereket is. Az eszköz bármikor megkereshet koordinátarendszereket, nem pedig másokat. A legtöbb koordinátarendszer esetében az alkalmazásnak készen kell állnia az olyan időszakok kezelésére, amelyek során nem találhatók.

Az alkalmazásnak nem szabad közvetlenül létrehoznia a SpatialCoordinateSystemst, hanem a Perception API-kkal kell használni őket. A Perception API-kban három elsődleges koordinátarendszer található, amelyek mindegyike megfelel a Koordinátarendszerek oldalon ismertetett fogalomnak:

Az objektumok által visszaadott koordinátarendszerek mindegyike jobbkezes, +y felfelé, +x jobbra és +z hátrafelé. A pozitív z tengely pontokat úgy jegyezheti meg, hogy a bal vagy a jobb kezének ujjait a pozitív x irányba mutogatja, és a pozitív y irányba curlingeli őket. Az az irány, amely a hüvelykujja felé vagy öntől távol van, az az irány, amelyet az adott koordinátarendszer pozitív z tengelypontjai mutatnak. Az alábbi ábrán ez a két koordinátarendszer látható.

Bal és jobb oldali koordinátarendszerek
Bal és jobb oldali koordinátarendszerek

A SpatialLocator osztály használatával hozzon létre egy csatolt vagy helyhez kötött referenciakeretet a bootstraphoz egy SpatialCoordinateSystem-be a HoloLens pozíció alapján. A folyamatról további információt a következő szakaszban olvashat.

Hologramok elhelyezése a világon térbeli szakasz használatával

Az átlátszatlan Windows Mixed Reality modern headsetek koordinátarendszere a static SpatialStageFrameOfReference::Current tulajdonság használatával érhető el. Ez az API a következőket biztosítja:

  • Koordinátarendszer
  • Információ arról, hogy a játékos ül-e vagy mobil
  • A biztonságos terület határa, ahol mozogni lehet, ha a játékos mobil
  • Annak jelzése, hogy a headset irányirányban van-e.
  • Eseménykezelő a térbeli szakasz frissítéséhez.

Először lekérjük a térbeli szakaszt, és feliratkozunk a frissítéseire:

A térbeli szakasz inicializálásának kódja

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

Az OnCurrentChanged metódusban az alkalmazásnak meg kell vizsgálnia a térbeli szakaszt, és frissítenie kell a játékos élményét. Ebben a példában megjelenítjük a szakaszhatárt és a felhasználó által megadott indítási pozíciót, valamint a szakasz nézettartományát és mozgási tulajdonságainak tartományát. A saját helyhez kötött koordinátarendszerünkbe is visszaesünk, amikor nem lehet szakaszt biztosítani.

A térbeli szakasz frissítésének kódja

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

A szakaszhatárt meghatározó csúcspontok halmaza óramutató járásával megegyező sorrendben van megadva. A Windows Mixed Reality felület egy kerítést rajzol a határhoz, amikor a felhasználó megközelíti, de érdemes lehet háromszögelni a járható területet a saját céljaira. Az alábbi algoritmus használható a fázis háromszögelésére.

A térbeli szakasz háromszögeléséhez használható kód

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

Hologramok elhelyezése a világon helyhez kötött referenciakeret használatával

A SpatialStationaryFrameOfReference osztály egy referenciakeretet jelöl, amely a felhasználó környezetéhez képest állandó marad , miközben a felhasználó mozog. Ez a referenciakeret prioritást ad a koordinátáknak az eszköz közelében való stabil tartásához. A SpatialStationaryFrameOfReference egyik fő felhasználási célja, hogy a hologramok renderelésekor a mögöttes világkoordináta-rendszerként működjön egy renderelő motoron belül.

A SpatialStationaryFrameOfReference beszerzéséhez használja a SpatialLocator osztályt, és hívja meg a CreateStationaryFrameOfReferenceAtCurrentLocation parancsot.

A Windows Holographic alkalmazás sablonkódjából:

           // 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();
  • A helyhez kötött referenciakeretek úgy vannak kialakítva, hogy a lehető legjobb pozíciót biztosíthassák a teljes területhez képest. A referenciakereten belüli egyes pozíciók kissé eltolódnak. Ez normális, mivel az eszköz többet tud meg a környezetről.
  • Ha az egyes hologramok pontos elhelyezésére van szükség, egy SpatialAnchort kell használni az egyes hologramok valós helyzethez való rögzítéséhez – például egy olyan ponthoz, amelyet a felhasználó különleges érdeklődésnek jelez. A horgonypozíciók nem sodródnak, de javíthatók; a horgony a javítás után a következő keretben kezdődő helyes pozíciót fogja használni.

Hologramok elhelyezése a világon térbeli horgonyok használatával

A térbeli horgonyok kiválóan alkalmasak arra, hogy a hologramokat a valós világban egy adott helyen helyezhesse el, és a rendszer biztosítja, hogy a horgony az idő múlásával a helyén maradjon. Ez a témakör bemutatja, hogyan hozhat létre és használhat horgonyadatokat, és hogyan használhatja a horgonyadatokat.

A SpatialAnchort tetszőleges pozícióban és tájolásban létrehozhatja az Ön által választott SpatialCoordinateSystem területen. Az eszköznek képesnek kell lennie a koordinátarendszer megkeresésére, és a rendszernek nem szabad elérnie a térbeli horgonyok korlátját.

A definiálást követően a SpatialAnchor koordinátarendszere folyamatosan igazodik a kezdeti hely pontos pozíciójának és tájolásának megtartásához. Ezt a SpatialAnchort használhatja a hologramok renderelésére, amelyek rögzítetten jelennek meg a felhasználó környezetében ezen a helyen.

A horgonyt a helyükön tartó korrekciók hatása a horgonytól való távolság növekedésével növekszik. Ne jelenítsen meg tartalmat olyan horgonyhoz viszonyítva, amely a horgony forrásától körülbelül 3 méterre van.

A CoordinateSystem tulajdonság egy koordinátarendszert kap, amely lehetővé teszi a tartalom elhelyezését a horgonyhoz viszonyítva, az eszköz pontos helyének beállításakor alkalmazott enyhítéssel.

Ezeket a módosításokat saját maga kezelheti a RawCoordinateSystem tulajdonság és a hozzá tartozó RawCoordinateSystemAdjusted esemény használatával.

Térbeli horgonyok megőrzése és megosztása

A SpatialAnchor-osztály használatával helyben is megőrizheti a SpatialAnchorStore osztályt, majd visszakérheti egy későbbi alkalmazás-munkamenetben ugyanazon a HoloLens-eszközön.

Az Azure Spatial Anchors használatával tartós felhőhorgonyt hozhat létre egy helyi SpatialAnchorból, amelyet az alkalmazás több HoloLens, iOS és Android-eszközön is megtalálhat. Ha közös térbeli horgonyt oszt meg több eszközön, minden felhasználó valós időben láthatja az adott horgonyhoz képest renderelt tartalmat.

Az Azure Spatial Anchors használatával aszinkron hologram-megőrzést is használhat HoloLens, iOS és Android rendszerű eszközökön. A tartós felhőbeli térbeli horgony megosztásával több eszköz is megfigyelheti ugyanazt az állandó hologramot, még akkor is, ha ezek az eszközök nem egyszerre vannak jelen.

Ha el szeretné kezdeni a megosztott élmények kiépítését a HoloLens alkalmazásban, próbálja ki az 5 perces Azure Spatial Anchors HoloLens rövid útmutatót.

Az Azure Spatial Anchors használata után létrehozhat és megkereshet horgonyokat a HoloLensben. Az útmutatók androidos és iOS rendszeren is elérhetők, így minden eszközön ugyanazokat a horgonyokat oszthatja meg.

SpatialAnchors létrehozása holografikus tartalomhoz

Ebben a kódmintában úgy módosítottuk a Windows Holographic alkalmazássablont, hogy horgonyokat hozzon létre a lenyomott kézmozdulat észlelésekor. A kocka ezután a leképezési átengedés során a horgonyra kerül.

Mivel a segédosztály több horgonyt is támogat, annyi kockát helyezhetünk el, amennyit használni szeretnénk ezt a kódmintát!

Megjegyzés

A horgonyok azonosítóit ön szabályozhatja az alkalmazásban. Ebben a példában létrehoztunk egy elnevezési sémát, amely szekvenciális, az alkalmazás horgonygyűjteményében jelenleg tárolt horgonyok száma alapján.

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

Aszinkron módon betölti és gyorsítótárazza a SpatialAnchorStore-t

Lássuk, hogyan írhatunk sampleSpatialAnchorHelper osztályt, amely segít kezelni ezt az adatmegőrzést, beleértve a következőket:

  • Memóriabeli horgonyok gyűjteményének tárolása platform::sztringkulcs által indexelt.
  • Horgonyok betöltése a rendszer SpatialAnchorStore-jából, amely a helyi memóriabeli gyűjteménytől elkülönítve marad.
  • Ha az alkalmazás ezt választja, mentse a horgonyok helyi memóriabeli gyűjteményét a SpatialAnchorStore-ba.

Így mentheti a SpatialAnchor objektumokat a SpatialAnchorStoreban.

Az osztály indításakor aszinkron módon kérjük a SpatialAnchorStore-t. Ez magában foglalja a rendszer I/O-jának használatát, mivel az API betölti a horgonytárolót, és ezt az API-t aszinkron módon készíti el, hogy az I/O ne legyen blokkolva.

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

Kap egy SpatialAnchorStore-t, amellyel mentheti a horgonyokat. Ez egy IMapView, amely sztringek kulcsértékeket társít a SpatialAnchors adatértékekkel. A mintakódban ezt egy privát osztálytag változóban tároljuk, amely a segédosztály nyilvános függvényén keresztül érhető el.

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

Megjegyzés

Ne felejtse el összekapcsolni a felfüggesztési/folytatási eseményeket a horgonytároló mentéséhez és betöltéséhez.

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

Tartalom mentése a horgonytárolóba

Amikor a rendszer felfüggeszti az alkalmazást, mentenie kell a térbeli horgonyokat a horgonytárolóba. Dönthet úgy is, hogy más időpontokban menti a horgonytárba a horgonytárat, mivel az alkalmazás implementációjához szükségesnek találja.

Ha készen áll arra, hogy megpróbálja menteni a memóriában lévő horgonyokat a SpatialAnchorStore-ba, végighaladhat a gyűjteményen, és megpróbálhatja menteni őket.

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

Tartalom betöltése a horgonytárolóból az alkalmazás folytatásakor

A mentett horgonyokat visszaállíthatja az AnchorStoreban úgy, hogy azokat a horgonytár IMapView nézetéből a SpatialAnchors saját memóriában lévő adatbázisába adhatja át, amikor az alkalmazás újraindul vagy bármikor.

A SpatialAnchorStore-ban lévő horgonyok visszaállításához állítsa vissza azokat, amelyek érdeklik a saját memóriabeli gyűjteményében.

A Sztringek és a létrehozott SpatialAnchors társításához saját, memórián belüli SpatialAnchors-adatbázisra van szükség. A mintakódban egy Windows::Foundation::Collections::IMap protokollt használunk a horgonyok tárolására, ami megkönnyíti ugyanazt a kulcsot és adatértéket a SpatialAnchorStore esetében.

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

Megjegyzés

Előfordulhat, hogy egy visszaállított horgony nem található meg azonnal. Ez lehet például egy horgony egy külön szobában vagy egy teljesen más épületben. Az AnchorStoreból lekért horgonyokat használat előtt tesztelni kell a helymeghatározásukhoz.


Megjegyzés

Ebben a példakódban lekérjük az összes horgonyt az AnchorStoreból. Ez nem követelmény; az alkalmazás egyszerűen kiválaszthatja és kiválaszthatja a horgonyok egy bizonyos részhalmazát az implementáció szempontjából jelentőséggel bíró Sztringkulcs-értékek használatával.

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

Szükség esetén törölje a horgonytárolót

Előfordulhat, hogy törölnie kell az alkalmazás állapotát, és új adatokat kell írnia. Ezt a SpatialAnchorStore használatával teheti meg.

A segédosztály használatával szinte szükségtelen a Clear függvény burkolása. Ezt a mintaimplementációban választjuk, mert a segédosztály a SpatialAnchorStore-példány tulajdonosának felel.

   // 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élda: Horgonykoordináta-rendszerek és helyhez kötött referenciakeret koordinátarendszerek

Tegyük fel, hogy van egy horgonya, és a horgony koordinátarendszerében lévő valamit a másik tartalomhoz már használt SpatialStationaryReferenceFrame-hez szeretne kapcsolni. A TryGetTransformTo használatával átalakíthatja a horgony koordinátarendszerét a helyhez kötött referenciakereté:

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

Ez a folyamat kétféleképpen hasznos:

  1. Azt jelzi, hogy a két referenciakeret értelmezhető-e egymáshoz képest, és;
  2. Ha igen, az átalakítással közvetlenül az egyik koordinátarendszerből a másikba léphet.

Ezekkel az információkkal megismerheti az objektumok közötti térbeli kapcsolatot a két referenciakeret között.

A rendereléshez gyakran jobb eredményeket érhet el, ha az objektumokat az eredeti referenciakeret vagy horgony alapján csoportosítja. Minden csoporthoz külön rajzi bérletet kell végrehajtania. A nézetmátricák pontosabbak az olyan modellátalakításokkal rendelkező objektumok esetében, amelyek eredetileg ugyanazzal a koordinátarendszerrel lettek létrehozva.

Hologramok létrehozása eszközhöz csatolt referenciakeret használatával

Vannak esetek, amikor olyan hologramot szeretne renderelni, amely az eszköz helyéhez marad csatolva , például egy hibakeresési információkat tartalmazó panelt vagy egy tájékoztató üzenetet, amikor az eszköz csak a tájolását tudja meghatározni, és nem a helyének helyét a térben. Ehhez egy csatolt referenciakeretet használunk.

A SpatialLocatorAttachedFrameOfReference osztály koordinátarendszereket határoz meg, amelyek nem a valós, hanem az eszközhöz vannak viszonyítva. Ez a keret rögzített címsorsal rendelkezik a felhasználó környezetéhez képest, amely arra az irányba mutat, amellyel a felhasználó a referenciakeret létrehozásakor szembesült. Ettől kezdve a hivatkozási keretben lévő összes tájolás a rögzített címsorhoz képest van, még akkor is, ha a felhasználó elforgatja az eszközt.

A HoloLens esetében ennek a keretnek a koordinátarendszere a felhasználó fejének forgatási középpontjában helyezkedik el, így a pozícióját nem befolyásolja a fej elforgatása. Az alkalmazás megadhat egy eltolást ehhez a ponthoz képest, hogy hologramokat helyezzen el a felhasználó előtt.

A SpatialLocatorAttachedFrameOfReference lekéréséhez használja a SpatialLocator osztályt, és hívja meg a CreateAttachedFrameOfReferenceAtCurrentHeading parancsot.

Ez a Windows Mixed Reality eszközök teljes tartományára vonatkozik.

Az eszközhöz csatolt referenciakeret használata

Ezek a szakaszok arról szólnak, hogy mit módosítottunk a Windows Holographic alkalmazássablonban, hogy ezzel az API-val engedélyezze az eszközhöz csatolt referenciakeretet. Ez a "csatolt" hologram a helyhez kötött vagy rögzített hologramok mellett működik, és akkor is használható, ha az eszköz átmenetileg nem találja a helyét a világon.

Először úgy módosítottuk a sablont, hogy a SpatialLocatorAttachedFrameOfReference tárolót tárolja a SpatialStationaryFrameOfReference helyett:

Forrás : HolographicTagAlongSampleMain.h:

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

Forrás : HolographicTagAlongSampleMain.cpp:

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

A frissítés során most lekérjük a koordinátarendszert a keret előrejelzésével kapott időbélyeggel.

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

Térbeli mutató pózának lekérése és a felhasználó tekintetének követése

Azt szeretnénk, hogy a példa hologram a felhasználó tekintetét kövesse, hasonlóan ahhoz, ahogyan a holografikus felület képes követni a felhasználó tekintetét. Ehhez le kell kérnünk a SpatialPointerPose-t ugyanabból az időbélyegből.

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

Ez a SpatialPointerPose a hologramnak a felhasználó aktuális címsora szerinti elhelyezéséhez szükséges információkkal rendelkezik.

A felhasználói kényelem érdekében lineáris interpolációt ("lerp") használunk a pozícióváltozások simítására egy adott időszakban. Ez kényelmesebb a felhasználó számára, mint a hologramot a tekintetükhöz rögzíteni. A címke mentén lévő hologram helyzetének tűrése lehetővé teszi a hologram stabilizálását a mozgás csillapításával. Ha nem végeznénk el ezt a csillapítást, a felhasználó a hologram jittert látja, mert a felhasználó feje általában észrevehetetlen mozgásnak minősül.

Forrás : 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);
   }

Megjegyzés

Hibakeresési panel esetén dönthet úgy, hogy a hologramot egy kicsit áthelyezi az oldalra, hogy az ne akadályozza a nézetet. Íme egy példa arra, hogyan teheti ezt meg.

A StationaryQuadRenderer::P ositionHologram esetében:

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

A hologram elforgatása a kamera előtt

Nem elég a hologram elhelyezése, ami ebben az esetben egy quad; az objektumot is el kell forgatnunk, hogy a felhasználóval szembe tudjunk nézni. Ez a rotáció a világűrben fordul elő, mivel az ilyen típusú hirdetőtáblák lehetővé teszik, hogy a hologram a felhasználó környezetének része maradjon. A térbeli hirdetőtáblázás nem olyan kényelmes, mert a hologram a megjelenítési tájoláshoz lesz zárolva; ebben az esetben a bal és a jobb oldali nézet mátrixai között is interpolálnia kell, hogy olyan térbeli billboard átalakítást szerezzen be, amely nem akadályozza meg a sztereó megjelenítést. Itt elforgatjuk az X és Z tengelyeket, hogy szembe nézhessenek a felhasználóval.

Forrás : 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));

A csatolt hologram renderelése

Ebben a példában a hologramot a SpatialLocatorAttachedReferenceFrame koordinátarendszerében is rendereljük, ahol a hologramot elhelyeztük. (Ha úgy döntünk, hogy egy másik koordinátarendszer használatával renderelünk, át kell szereznünk egy átalakítást az eszközhöz csatolt referenciakeret koordinátarendszeréről az adott koordinátarendszerre.)

Forrás : 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)
       );

Ennyi az egész! A hologram ekkor "üldözi" azt a pozíciót, amely 2 méterre van a felhasználó tekintete előtt.

Megjegyzés

Ez a példa további tartalmakat is betölt – lásd: StationaryQuadRenderer.cpp.

A nyomon követési veszteség kezelése

Ha az eszköz nem találja magát a világon, az alkalmazás a "veszteség nyomon követését" tapasztalja. Windows Mixed Reality alkalmazásoknak képesnek kell lenniük a pozíciókövetési rendszer ilyen zavarainak kezelésére. Ezek a fennakadások az alapértelmezett SpatialLocator LocatabilityChanged eseményével figyelhetők meg és hozhatók létre.

Az AppMain::SetHolographicSpace fájlból:

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

Amikor az alkalmazás locatabilityChanged eseményt kap, szükség szerint módosíthatja a viselkedést. PositionalTrackingInhibited állapotban például az alkalmazás szüneteltetheti a normál műveletet, és megjeleníthet egy címke mentén megjelenő hologramot , amely figyelmeztető üzenetet jelenít meg.

A Windows Holographic alkalmazássablonhoz tartozik egy Már létrehozott LocatabilityChanged kezelő. Alapértelmezés szerint figyelmeztetést jelenít meg a hibakeresési konzolon, ha a pozíciókövetés nem érhető el. Ehhez a kezelőhöz kódot adhat hozzá, hogy szükség szerint választ adjon az alkalmazásból.

Az AppMain.cpp fájlból:

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

Térbeli leképezés

A térbeli leképezési API-k koordinátarendszereket használnak a felületi hálók modellátalakításainak lekéréséhez.

Lásd még