Kéz- és mozgásvezérlők 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 Windows Mixed Reality a kéz- és a mozgásvezérlő bemenetét a Windows.UI.Input.Spatial névtérben található térbeli bemeneti API-k kezelik. Ez lehetővé teszi, hogy könnyen kezelje az olyan gyakori műveleteket, mint a Select billentyű lenyomása ugyanúgy a kéz és a mozgásvezérlő között.

Első lépések

A térbeli bemenetek Windows Mixed Reality való eléréséhez kezdje a SpatialInteractionManager felülettel. Ezt a felületet a SpatialInteractionManager::GetForCurrentView hívásával érheti el, általában az alkalmazás indításakor.

using namespace winrt::Windows::UI::Input::Spatial;

SpatialInteractionManager interactionManager = SpatialInteractionManager::GetForCurrentView();

A SpatialInteractionManager feladata, hogy hozzáférést biztosítson a SpatialInteractionSourceshoz, amely a bemeneti forrást jelöli. A rendszerben háromféle SpatialInteractionSource érhető el.

  • A kéz a felhasználó észlelt kezét jelöli. A kézforrások az eszköztől függően különböző funkciókat kínálnak, a HoloLens alapszintű kézmozdulataitól a HoloLens 2 teljes körűen tagolt kézkövetéséig.
  • A vezérlő párosított mozgásvezérlőt jelöl. A mozgásvezérlők különböző képességeket kínálnak, például triggerek kiválasztása, menügombok, fogógombok, érintőpárnák és pendrive-ok.
  • A hang a felhasználó hangalapú, rendszer által észlelt kulcsszavainak felel meg. Ez a forrás például beszúr egy Select billentyűt és kiadást, amikor a felhasználó a "Select" (Kiválasztás) szöveget mondja.

A forrás keretenkénti adatait a SpatialInteractionSourceState felület képviseli. Az adatok elérésének két különböző módja van attól függően, hogy eseményvezérelt vagy lekérdezésalapú modellt szeretne-e használni az alkalmazásban.

Eseményvezérelt bemenet

A SpatialInteractionManager számos olyan eseményt biztosít, amelyeket az alkalmazás figyelhet. Néhány példa: SourcePressed, [SourceReleased és SourceUpdated.

Az alábbi kód például csatlakoztat egy MyApp::OnSourcePressed nevű eseménykezelőt a SourcePressed eseményhez. Ez lehetővé teszi, hogy az alkalmazás bármilyen típusú interakciós forrás lenyomását észlelje.

using namespace winrt::Windows::UI::Input::Spatial;

auto interactionManager = SpatialInteractionManager::GetForCurrentView();
interactionManager.SourcePressed({ this, &MyApp::OnSourcePressed });

Ezt a megnyomott eseményt aszinkron módon küldi el az alkalmazásnak, a megfelelő SpatialInteractionSourceState-tal együtt, amikor a lenyomás történt. Előfordulhat, hogy az alkalmazás vagy a játékmotor azonnal megkezdi a feldolgozást, vagy várólistára szeretné helyezni az eseményadatokat a bemeneti feldolgozási rutinban. Íme egy eseménykezelő függvény a SourcePressed eseményhez, amely ellenőrzi, hogy a választógomb megnyomva lett-e.

using namespace winrt::Windows::UI::Input::Spatial;

void MyApp::OnSourcePressed(SpatialInteractionManager const& sender, SpatialInteractionSourceEventArgs const& args)
{
	if (args.PressKind() == SpatialInteractionPressKind::Select)
	{
		// Select button was pressed, update app state
	}
}

A fenti kód csak a "Select" billentyűt ellenőrzi, amely megfelel az eszköz elsődleges műveletének. Ilyen például egy AirTap művelet a HoloLensen, vagy az eseményindító lekérése egy mozgásvezérlőn. A "Select" billentyű lenyomásával a felhasználó aktiválni szeretné a megcélzott hologramot. A SourcePressed esemény számos különböző gombot és kézmozdulatot aktivál, és a SpatialInteractionSource más tulajdonságait is megvizsgálhatja az ilyen esetek teszteléséhez.

Lekérdezésalapú bemenet

A SpatialInteractionManager használatával is lekérdezheti a bemeneti állapotot minden keretben. Ehhez hívja meg a GetDetectedSourcesAtTimestamp parancsot minden keretben. Ez a függvény minden aktív SpatialInteractionSource esetében egy SpatialInteractionSourceState tömböt ad vissza. Ez azt jelenti, hogy minden aktív mozgásvezérlőhöz egyet, egy-egy nyomon követett kézhez, egyet pedig a beszédhez, ha nemrég kimondott egy "select" parancsot. Ezután megvizsgálhatja a tulajdonságokat az egyes SpatialInteractionSourceState-on, hogy be tudja vezetni a bemenetet az alkalmazásba.

Íme egy példa arra, hogyan ellenőrizheti a "select" műveletet a lekérdezési módszerrel. Az előrejelzési változó egy HolographicFramePrediction objektumot jelöl, amely a HolographicFrame-ből szerezhető be.

using namespace winrt::Windows::UI::Input::Spatial;

auto interactionManager = SpatialInteractionManager::GetForCurrentView();
auto sourceStates = m_spatialInteractionManager.GetDetectedSourcesAtTimestamp(prediction.Timestamp());

for (auto& sourceState : sourceStates)
{
	if (sourceState.IsSelectPressed())
	{
		// Select button is down, update app state
	}
}

Minden SpatialInteractionSource rendelkezik egy azonosítóval, amellyel új forrásokat azonosíthat, és a meglévő forrásokat keretről keretre korrelálhatja. A kezek minden távozáskor új azonosítót kapnak, és belépnek az FOV-be, de a vezérlőazonosítók statikusak maradnak a munkamenet időtartama alatt. A SpatialInteractionManager eseményei, például a SourceDetected és a SourceLost segítségével reagálhat, amikor a kezek belépnek vagy elhagyják az eszköz nézetét, illetve amikor a mozgásvezérlők be vannak kapcsolva/ki vannak kapcsolva, illetve párosítva/nem párosítva vannak.

Előrejelzett és történelmi pózok

A GetDetectedSourcesAtTimestamp egy időbélyegző paraméterrel rendelkezik. Ez lehetővé teszi, hogy állapotot kérjen, és előrejelzett vagy előzményadatokat adjon meg, így korrelálhat más bemeneti forrásokkal való térbeli interakciókkal. Ha például a kéz pozícióját az aktuális keretben jeleníti meg, a HolographicFrame által megadott előrejelzett időbélyeget is átadhatja. Ez lehetővé teszi, hogy a rendszer előre jelezhesse a kéz pozícióját, hogy szorosan igazodjon a renderelt keret kimenetéhez, minimalizálva az észlelt késést.

Az ilyen előrejelzett pózok azonban nem hoznak létre ideális mutató sugarat az interakciós forrással való célzáshoz. Ha például megnyom egy mozgásvezérlő gombot, az akár 20 ms-ot is igénybe vehet ahhoz, hogy az esemény felboruljon a Bluetooth-kapcsolaton keresztül az operációs rendszerhez. Hasonlóképpen, miután egy felhasználó kézmozdulatot végez, némi idő telhet el, mielőtt a rendszer észleli a kézmozdulatot, és az alkalmazás lekérdezi azt. Mire az alkalmazás lekérdezi az állapotváltozást, a fej és a kéz pózol, hogy megcélozhassa az adott interakciót a múltban. Ha az aktuális HolographicFrame időbélyegét a GetDetectedSourcesAtTimestamp értékre továbbítja, a pózt a rendszer a keret megjelenésekor előrejelzi a célzósugárnak, ami a jövőben több mint 20 ms lehet. Ez a jövőbeli póz jó az interakció forrásának megjelenítéséhez , de az időproblémát az interakció megcélzásához okozza, mivel a felhasználó célzása a múltban történt.

Szerencsére a SourcePressed, [SourceReleased és SourceUpdated események biztosítják az egyes bemeneti eseményekhez társított előzményállapotokat. Ez közvetlenül tartalmazza a TryGetPointerPose-on keresztül elérhető előzményfej- és kézpózokat, valamint egy előzményidőbélyeget, amelyet átadhat más API-knak, hogy korreláljon ezzel az eseménysel.

Ez a következő ajánlott eljárásokhoz vezet, amikor az egyes kereteket kézzel és vezérlőkkel jeleníti meg és célozza meg:

  • Az egyes képkockák kézi/vezérlős megjelenítéséhez az alkalmazásnak le kellkérdeznie az egyes interakciós források előrejelzett pózát az aktuális keret fotonidejénél. Az összes interakciós forrást lekérdezheti úgy, hogy meghívja a GetDetectedSourcesAtTimestamp függvényt minden egyes képkockához, és megadja a HolographicFrame::CurrentPrediction által megadott előrejelzett időbélyeget.
  • A sajtóra vagy kiadásra irányuló kézi/kontrolleres célzáshoz az alkalmazásnak kezelnie kell a lenyomásos/kiadott eseményeket, a raycastingot az adott esemény előzményfeje vagy kézi póza alapján. Ezt a célzási sugarat a SourcePressed vagy a SourceReleased esemény kezelésével, az Állapot tulajdonság az eseményargumentumokból való lekérésével, majd a TryGetPointerPose metódus meghívásával kapja meg.

Eszközközi beviteli tulajdonságok

A SpatialInteractionSource API számos funkcióval támogatja a vezérlőket és a kézkövető rendszereket. Számos ilyen képesség gyakori az eszköztípusok között. A kézkövetés és a mozgásvezérlők például "kiválasztási" műveletet és 3D pozíciót is biztosítanak. Ahol csak lehetséges, az API ezeket a közös képességeket a SpatialInteractionSource ugyanazon tulajdonságaira képezi le. Ez lehetővé teszi, hogy az alkalmazások könnyebben támogatják a bemeneti típusok széles körét. Az alábbi táblázat a támogatott tulajdonságokat és a bemeneti típusok összehasonlításának módját ismerteti.

Tulajdonság Leírás HoloLens(1. generációs) kézmozdulatok Mozgásvezérlők Csuklós kezek
SpatialInteractionSource::Handedness Jobb vagy bal kéz / vezérlő. Nem támogatott Támogatott Támogatott
SpatialInteractionSourceState::IsSelectPressed Az elsődleges gomb aktuális állapota. Levegő koppintás Eseményindító Ellazult levegőcsapás (függőleges csippentés)
SpatialInteractionSourceState::IsGrasped A megragadás gomb aktuális állapota. Nem támogatott Megragadás gomb Csippentés vagy zárt kéz
SpatialInteractionSourceState::IsMenuPressed A menügomb aktuális állapota. Nem támogatott Menü gomb Nem támogatott
SpatialInteractionSourceLocation::Position A kéz vagy a fogó pozíció XYZ helye a vezérlőn. Tenyér helye Markolat pozíciója Tenyér helye
SpatialInteractionSourceLocation::Orientation Quaternion, amely a kéz tájolását vagy a markolatot jelöli a vezérlőn. Nem támogatott Fogó póz tájolása Tenyér tájolása
SpatialPointerInteractionSourcePose::Position A mutató sugár eredete. Nem támogatott Támogatott Támogatott
SpatialPointerInteractionSourcePose::ForwardDirection A mutató sugár iránya. Nem támogatott Támogatott Támogatott

A fenti tulajdonságok némelyike nem érhető el minden eszközön, és az API lehetővé teszi ennek tesztelését. Megvizsgálhatja például a SpatialInteractionSource::IsGraspSupported tulajdonságot annak megállapításához, hogy a forrás rendelkezik-e fogóművelettel.

Markolat-póz és mutató póz

Windows Mixed Reality különböző formában támogatja a mozgásvezérlőket. Támogatja a csuklós kézkövető rendszereket is. Ezen rendszerek mindegyike eltérő kapcsolatot mutat a kéz helyzete és a természetes "előre" irány között, amelyet az alkalmazásoknak az objektumok a felhasználó kezében való mutatásához vagy megjelenítéséhez kell használniuk. Mindezek támogatása érdekében kétféle 3D-s póz érhető el a kézkövetéshez és a mozgásvezérlőkhöz. Az első a markolati póz, amely a felhasználó kézpozícióját jelöli. A második a pontozási póz, amely a felhasználó kezéből vagy vezérlőből származó mutató sugarat jelöl. Ha tehát a felhasználó kezét vagy a felhasználó kezében tartott objektumot , például kardot vagy fegyvert szeretne megjeleníteni, használja a fogó pózt. Ha a vezérlőből vagy kézből szeretne sugárképet küldeni, például ha a felhasználó **a felhasználói felületre mutat, használja a mutató pózt.

A fogó pózt a SpatialInteractionSourceState::P roperties::TryGetLocation(...) segítségével érheti el. Ez a következőképpen van definiálva:

  • A markolat helyzete: A tenyér centroid, ha a vezérlőt természetes módon tartja, balra vagy jobbra igazítva a markolaton belüli pozíció középre állításához.
  • A fogó tájolás jobb tengelye: Amikor teljesen felnyitja a kezét, hogy lapos ötujjas pózt alakítson ki, a tenyérre normál sugarat (a bal tenyérről előre, a jobb tenyérről visszafelé)
  • A fogó tájolás Előre tengelye: Ha részlegesen bezárja a kezét (mintha a vezérlőt tartana), a nem hüvelykujja által létrehozott csőben "előre" mutató sugár.
  • A fogó tájolás felfelé tengelye: A Jobbra és Előre definíciók által sugallt Fel tengely.

A mutató pózát a SpatialInteractionSourceState::P roperties::TryGetLocation(...)::SourcePointerPose vagy SpatialInteractionSourceState::TryGetPointerPose(...)::TryGetInteractionSourcePose webhelyen érheti el.

Vezérlőspecifikus bemeneti tulajdonságok

A vezérlők esetében a SpatialInteractionSource további képességekkel rendelkező Controller tulajdonsággal rendelkezik.

  • HasThumbstick: Ha igaz, a vezérlőnek van egy hüvelykujja. Vizsgálja meg a SpatialInteractionSourceState ControllerProperties tulajdonságát, és szerezze be a thumbstick x és y értékeit (ThumbstickX és ThumbstickY), valamint a lenyomott állapotát (IsThumbstickPressed).
  • HasTouchpad: Ha igaz, a vezérlő érintőpárnával rendelkezik. Vizsgálja meg a SpatialInteractionSourceState ControllerProperties tulajdonságát az érintőpárna x és y értékeinek (TouchpadX és TouchpadY) beszerzéséhez, valamint annak megállapításához, hogy a felhasználó megérinti-e a padot (IsTouchpadTouched), és hogy lenyomja-e az érintőpárnát (IsTouchpadPressed).
  • SimpleHapticsController: A vezérlőHöz készült SimpleHapticsController API lehetővé teszi a vezérlő haptics képességeinek vizsgálatát, és lehetővé teszi a haptikus visszajelzések vezérlését is.

Az érintőpárnák és a hüvelykujjak tartománya -1 és 1 között van mindkét tengelyen (alulról felfelé és balról jobbra). Az analóg eseményindító tartománya, amely a SpatialInteractionSourceState::SelectPressedValue tulajdonság használatával érhető el, 0 és 1 közötti tartománysal rendelkezik. Az 1 érték korrelál azzal, hogy az IsSelectPressed értéke igaz; bármely más érték korrelál azzal, hogy az IsSelectPressed értéke hamis.

Csuklós kézkövetés

A Windows Mixed Reality API teljes körű támogatást nyújt a csuklós kézkövetéshez, például HoloLens 2. A csuklós kézkövetés közvetlen manipulációs és pont- és véglegesítési bemeneti modellek implementálhatók az alkalmazásokban. Teljesen egyéni interakciók készítésére is használható.

Kézváz

A csuklós kézkövetés 25 közös csontvázat biztosít, amely számos különböző típusú interakciót tesz lehetővé. A csontváz öt ízületet biztosít az indexhez/középső/gyűrűhöz/kisujjhoz, négy ízületet a hüvelykujjhoz és egy csuklóízületet. A csuklókötés szolgál a hierarchia alapjaként. Az alábbi képen a csontváz elrendezése látható.

Kézváz

A legtöbb esetben minden ízület neve a csont alapján van elnevezve. Mivel minden ízületnél két csont van, minden ízület elnevezésére vonatkozó konvenciót használunk az adott helyen található gyermekcsont alapján. A gyermekcsont a csuklótól távolabbi csontként van definiálva. Például az "Index Proximal" ízület tartalmazza az index proximális csontjának kezdő pozícióját és a csont tájolását. Nem tartalmazza a csont záró pozícióját. Ha erre szüksége van, a hierarchiában található következő ízületből, az "Index köztes" ízületből szerezheti be.

A 25 hierarchikus illesztés mellett a rendszer tenyérkötést is biztosít. A tenyér általában nem a csontváz szerkezetének része. Ez csak egy kényelmes módja annak, hogy a kéz általános helyzetét és tájolását.

Az egyes illesztéseket a következő információk biztosítják:

Név Leírás
Pozíció A kötés 3D pozíciója, bármely kért koordinátarendszerben elérhető.
Tájolás A csont térhatású tájolása, bármely kért koordinátarendszerben elérhető.
Radius A bőr felületének távolsága az ízületi pozícióban. Hasznos az ujjszélességre támaszkodó közvetlen interakciók vagy vizualizációk finomhangolásához.
Pontosság Ad egy tippet, hogy mennyire magabiztos a rendszer úgy érzi, ez a közös információkat.

A kéz csontvázadatait a SpatialInteractionSourceState függvényen keresztül érheti el. A függvény neve TryGetHandPose, és egy HandPose nevű objektumot ad vissza. Ha a forrás nem támogatja a csuklós kezeket, akkor ez a függvény null értéket ad vissza. Ha már rendelkezik HandPose-tal, a TryGetJoint meghívásával lekérheti az aktuális közös adatokat a kívánt ízület nevével. A rendszer jointPose-struktúraként adja vissza az adatokat. Az alábbi kód lekéri az indexujjhegy pozícióját. A currentState változó a SpatialInteractionSourceState egy példányát jelöli.

using namespace winrt::Windows::Perception::People;
using namespace winrt::Windows::Foundation::Numerics;

auto handPose = currentState.TryGetHandPose();
if (handPose)
{
	JointPose joint;
	if (handPose.TryGetJoint(desiredCoordinateSystem, HandJointKind::IndexTip, joint))
	{
		float3 indexTipPosition = joint.Position;

		// Do something with the index tip position
	}
}

Kézi háló

A csuklós kézkövető API teljes mértékben deformálható háromszög kézhálót tesz lehetővé. Ez a háló valós időben deformálható a kézvázzal együtt, és hasznos a vizualizációkhoz és a fejlett fizikai technikákhoz. A kézi háló eléréséhez először létre kell hoznia egy HandMeshObserver objektumot a TryCreateHandMeshObserverAsync meghívásával a SpatialInteractionSource-on. Ezt forrásonként csak egyszer kell elvégezni, általában az első alkalommal. Ez azt jelenti, hogy ezt a függvényt úgy fogja meghívni, hogy hozzon létre egy HandMeshObserver objektumot, amikor egy kéz belép az FOV-ba. Ez egy aszinkron függvény, ezért itt egy kis egyidejűséggel kell foglalkoznia. Ha elérhető, a GetTriangleIndices meghívásával kérheti a HandMeshObserver objektumtól a háromszög indexpufferét. Az indexek nem módosítják a keret keretét, így ezeket egyszer lekérheti, és gyorsítótárazhatja őket a forrás élettartama alatt. Az indexek az óramutató járásával megegyező sorrendben vannak megadva.

A következő kód létrehoz egy leválasztott std::-szálat a hálófigyelő létrehozásához, és kinyeri az indexpuffert, amint a hálófigyelő elérhető. Egy currentState nevű változóból indul ki, amely a SpatialInteractionSourceState példánya, amely egy nyomon követett kezet jelöl.

using namespace Windows::Perception::People;

std::thread createObserverThread([this, currentState]()
{
    HandMeshObserver newHandMeshObserver = currentState.Source().TryCreateHandMeshObserverAsync().get();
    if (newHandMeshObserver)
    {
		unsigned indexCount = newHandMeshObserver.TriangleIndexCount();
		vector<unsigned short> indices(indexCount);
		newHandMeshObserver.GetTriangleIndices(indices);

        // Save the indices and handMeshObserver for later use - and use a mutex to synchronize access if needed!
     }
});
createObserverThread.detach();

A leválasztott szálak indítása csak egy lehetőség az aszinkron hívások kezelésére. Másik lehetőségként használhatja a C++/WinRT által támogatott új co_await funkciót.

Ha már rendelkezik HandMeshObserver objektummal, a megfelelő SpatialInteractionSource aktív állapotban tartandó. Ezután minden egyes keretben lekérheti a legújabb csúcspontpuffert, amely a kezet jelképezi a GetVertexStateForPose meghívásával, és átad egy HandPose-példányt , amely azt a pózt jelöli, amelyhez csúcspontokat szeretne adni. A puffer minden csúcspontja egy pozícióval és egy normál értékkel rendelkezik. Íme egy példa arra, hogyan szerezheti be a kézháló csúcsainak aktuális készletét. Mint korábban, a currentState változó a SpatialInteractionSourceState egy példányát jelöli.

using namespace winrt::Windows::Perception::People;

auto handPose = currentState.TryGetHandPose();
if (handPose)
{
    std::vector<HandMeshVertex> vertices(handMeshObserver.VertexCount());
    auto vertexState = handMeshObserver.GetVertexStateForPose(handPose);
    vertexState.GetVertices(vertices);

    auto meshTransform = vertexState.CoordinateSystem().TryGetTransformTo(desiredCoordinateSystem);
    if (meshTransform != nullptr)
    {
    	// Do something with the vertices and mesh transform, along with the indices that you saved earlier
    }
}

A csontváz-illesztésekkel ellentétben a hand mesh API nem teszi lehetővé a csúcspontok koordinátarendszerének megadását. Ehelyett a HandMeshVertexState határozza meg azt a koordinátarendszert, amelyben a csúcsok meg vannak adva. Ezután a TryGetTransformTo meghívásával és a kívánt koordinátarendszer megadásával hálóátalakítást kaphat. Ezt a hálóátalakítást akkor kell használnia, amikor a csúcspontokkal dolgozik. Ez a megközelítés csökkenti a processzorterhelést, különösen akkor, ha csak renderelési célokra használja a hálót.

Tekintet és véglegesítés – összetett kézmozdulatok

A tekintet- és véglegesítési bemeneti modellt használó alkalmazások, különösen a HoloLens (első generációs) esetében a Spatial Input API opcionális SpatialGestureRecognizert biztosít, amely a "select" eseményre épülő összetett kézmozdulatok engedélyezésére használható. A SpatialInteractionManager és a hologram SpatialGestureRecognizer közötti interakciók útválasztásával az alkalmazások egységesen észlelhetik a tap, a hold, a manipuláció és a navigáció eseményeit a kéz, a hang és a térbeli beviteli eszközök között anélkül, hogy manuálisan kellene kezelni a nyomatokat és a kiadásokat.

A SpatialGestureRecognizer csak a kért kézmozdulatok minimális egyértelműsítését teszi lehetővé. Ha például csak a Koppintást kéri, előfordulhat, hogy a felhasználó lenyomva tartja az ujját, amíg szeretné, és a koppintás továbbra is megtörténik. Ha a Koppintás és a Nyomva tartás elemet is kéri, körülbelül egy másodperc elteltével az ujját lenyomva tartva a kézmozdulat visszatartásra lép, és a koppintás már nem fog bekövetkezni.

A SpatialGestureRecognizer használatához kezelje a SpatialInteractionManager InteractionDetected eseményét, és ragadja meg az ott közzétett SpatialPointerPose-t. Használja a felhasználó ettől a póztól érkező fejkinézeti sugarát, hogy a felhasználó környezetében lévő hologramokkal és felületi hálókkal metssződjön annak meghatározásához, hogy a felhasználó mit kíván használni. Ezután az eseményargumentumok SpatialInteraction elemét a cél hologram SpatialGestureRecognizer eleméhez irányíthatja a CaptureInteraction metódussal. Ez elkezdi értelmezni ezt az interakciót a felismerőn a létrehozáskor beállított SpatialGestureSettings vagy a TrySetGestureSettings alapján.

A HoloLensen (első generációs) az interakcióknak és a kézmozdulatoknak a felhasználó fejnézetéből kell származniuk ahelyett, hogy a kéz helyén renderelnek vagy kommunikálnak. Az interakció megkezdése után a kézmozdulat szabályozására a kézmozdulat relatív mozgásai használhatók, mint a Manipuláció vagy a Navigációs kézmozdulat esetében.

Lásd még