Ruce a ovladače pohybu v rozhraní 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í API OpenXR.

V Windows Mixed Reality se vstupy rukou i ovladače pohybu zpracovávají prostřednictvím rozhraní API prostorového vstupu, která se nacházejí v oboru názvů Windows.UI.Input.Spatial. To vám umožní snadno zpracovávat běžné akce, jako je stisknutí tlačítka Select stejným způsobem napříč oběma rukama a ovladači pohybu.

Začínáme

Pokud chcete získat přístup k prostorovým vstupům v Windows Mixed Reality, začněte rozhraním SpatialInteractionManager. K tomuto rozhraní můžete přistupovat voláním SpatialInteractionManager::GetForCurrentView, obvykle někdy během spouštění aplikace.

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

SpatialInteractionManager interactionManager = SpatialInteractionManager::GetForCurrentView();

Úkolem SpatialInteractionManager je poskytnout přístup k SpatialInteractionSources, které představují zdroj vstupu. V systému jsou k dispozici tři druhy SpatialInteractionSources.

  • Hand představuje detekovanou ruku uživatele. Ruční zdroje nabízejí různé funkce v závislosti na zařízení, od základních gest na HoloLensu až po plně artikulované sledování rukou na HoloLens 2.
  • Ovladač představuje spárovaný ovladač pohybu. Ovladače pohybu můžou nabízet různé možnosti, například spouště pro výběr, tlačítka nabídek, tlačítka uchopení, touchpady a thumbsticky.
  • Hlas představuje klíčová slova systému rozpoznávání hlasu uživatele. Tento zdroj například vloží stisk a uvolnění funkce Select pokaždé, když uživatel řekne "Vybrat".

Data pro jednotlivé snímky pro zdroj jsou reprezentována SpatialInteractionSourceState rozhraní. Existují dva různé způsoby přístupu k datům v závislosti na tom, jestli chcete ve své aplikaci použít model řízený událostmi nebo model založený na dotazování.

Vstup řízený událostmi

SpatialInteractionManager poskytuje řadu událostí, kterým může vaše aplikace naslouchat. Mezi příklady patří SourcePressed, [SourceReleased a SourceUpdated.

Například následující kód připojí obslužnou rutinu události s názvem MyApp::OnSourcePressed k události SourcePressed. To vaší aplikaci umožní rozpoznat stisknutí na libovolném typu zdroje interakce.

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

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

Tato stisknutá událost se odešle do vaší aplikace asynchronně spolu s odpovídajícím parametrem SpatialInteractionSourceState v okamžiku stisknutí. Vaše aplikace nebo herní modul může chtít zahájit zpracování okamžitě nebo zařadit data události do fronty v rutině zpracování vstupu. Tady je funkce obslužné rutiny události pro událost SourcePressed, která kontroluje, jestli bylo tlačítko pro výběr stisknuto.

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

Výše uvedený kód kontroluje pouze stisknutí tlačítka Vybrat, které odpovídá primární akci na zařízení. Mezi příklady patří airTap na HoloLensu nebo zatáhnutí spouště na ovladači pohybu. Stisknutí tlačítka Vybrat představuje záměr uživatele aktivovat hologram, na který cílí. SourcePressed Událost se aktivuje pro řadu různých tlačítek a gest a můžete zkontrolovat další vlastnosti SpatialInteractionSource a otestovat pro tyto případy.

Vstup založený na dotazování

Můžete také použít SpatialInteractionManager k dotazování na aktuální stav vstupu každého snímku. Chcete-li to provést, volání GetDetectedSourcesAtTimestamp každý snímek. Tato funkce vrátí pole obsahující jeden SpatialInteractionSourceState pro každý aktivní SpatialInteractionSource. To znamená jeden pro každý aktivní ovladač pohybu, jeden pro každou sledovanou ruku a jeden pro řeč, pokud byl nedávno vysloven příkaz "vybrat". Pak můžete zkontrolovat vlastnosti jednotlivých SpatialInteractionSourceState a řídit vstup do vaší aplikace.

Tady je příklad, jak zkontrolovat akci "vybrat" pomocí metody dotazování. Prediktivní proměnná představuje Objekt HolographicFramePrediction, který lze získat z HolographicFrame.

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

Každý SpatialInteractionSource má ID, které můžete použít k identifikaci nových zdrojů a korelaci existujících zdrojů mezi snímkem. Ruce dostanou nové ID pokaždé, když odejdou a vstoupí do FOV, ale ID kontroleru zůstanou po dobu relace statická. Pomocí událostí ve službě SpatialInteractionManager, jako jsou SourceDetected a SourceLost, můžete reagovat, když ruce vstoupí do zobrazení zařízení nebo opustí jeho zobrazení, nebo když jsou ovladače pohybu zapnuté/vypnuté nebo jsou spárované/spárované.

Predikce vs. historické pozice

GetDetectedSourcesAtTimestamp má parametr časového razítka. To vám umožní vyžádat si stav a zobrazit data, která jsou predikovaná nebo historická, což vám umožní korelovat prostorové interakce s jinými zdroji vstupu. Například při vykreslování pozice ruky v aktuálním snímku můžete předat předpovězené časové razítko, které poskytuje HolographicFrame. Systém tak může dopředu předpovědět pozici ruky tak, aby byla přesně zarovnaná s výstupem vykresleného snímku, čímž se minimalizuje vnímaná latence.

Taková predikovaná pozice však nevytváří ideální bodovací paprsek pro cílení se zdrojem interakce. Když je například stisknuto tlačítko ovladače pohybu, může trvat až 20 ms, než událost probublává přes Bluetooth do operačního systému. Podobně může poté, co uživatel udělá gesto rukou, uplynout určitá doba, než systém gesto rozpozná a aplikace se na něj pak dotazuje. V době, kdy se vaše aplikace dotazuje na změnu stavu, se pozice hlavy a ruky používají k zacílení této interakce, ke které skutečně došlo v minulosti. Pokud cílíte předáním časového razítka aktuálního holographicFrame getDetectedSourcesAtTimestamp, bude pozice předpovězena paprsku cílení v okamžiku zobrazení rámce, což může být v budoucnu více než 20 ms. Tato pozice v budoucnosti je vhodná pro vykreslení zdroje interakce, ale protěžuje náš časový problém s cílením na interakci, protože k cílení uživatele došlo v minulosti.

Události SourcePressed, [SourceReleased a SourceUpdated naštěstí poskytují historický stav přidružený ke každé vstupní události. To přímo zahrnuje historické hlavní a hand pozice dostupné prostřednictvím TryGetPointerPose spolu s historickým časovým razítkem , které můžete předat jiným rozhraním API, aby bylo možné s touto událostí korelovat.

To vede k následujícím osvědčeným postupům při vykreslování a cílení pomocí rukou a ovladačů každého snímku:

  • Pokud chcete vykreslovat jednotlivé snímky rukou nebo ovladačem , měla by se vaše aplikace dotazovat na dopředu předpovězenou pozici jednotlivých zdrojů interakce v době fotonu aktuálního snímku. Dotazování na všechny zdroje interakce můžete provést voláním GetDetectedSourcesAtTimestamp každého snímku a předáním předpokládaného časového razítka, které poskytuje HolographicFrame::CurrentPrediction.
  • V případě cílení na ruku nebo ovladač při stisknutí nebo uvolnění by vaše aplikace měla zpracovávat události stisknutí/uvolnění, raycasting na základě historické pozice hlavy nebo ruky pro danou událost. Tento cílový ray získáte zpracováním SourcePressed nebo SourceReleased událostí, získáním state vlastnost z argumentů události a voláním metody TryGetPointerPose .

Vstupní vlastnosti napříč zařízeními

Rozhraní SPATIALInteractionSource API podporuje kontrolery a systémy pro sledování rukou se širokou škálou funkcí. Řada těchto funkcí je u různých typů zařízení společná. Například sledování rukou a ovladače pohybu poskytují akci výběru a 3D pozici. Kdykoli je to možné, rozhraní API mapuje tyto běžné funkce na stejné vlastnosti na SpatialInteractionSource. To umožňuje aplikacím snadněji podporovat širokou škálu typů vstupu. Následující tabulka popisuje podporované vlastnosti a způsob jejich porovnání mezi vstupními typy.

Vlastnost Popis Gesta HoloLens(1. generace) Ovladače pohybu Kloubové ruce
SpatialInteractionSource::Handedness Pravá nebo levá ruka / ovladač. Nepodporuje se Podporováno Podporováno
SpatialInteractionSourceState::IsSelectPressed Aktuální stav primárního tlačítka Klepnutí ve vzduchu Trigger Uvolněné klepnutí na vzduch (vertikální stažení)
SpatialInteractionSourceState::IsGrasped Aktuální stav tlačítka pro uchopení. Nepodporuje se Tlačítko Pro uchopení Stažení nebo zavření ruky
SpatialInteractionSourceState::IsMenuPressed Aktuální stav tlačítka nabídky Nepodporuje se Tlačítko Nabídky Nepodporuje se
SpatialInteractionSourceLocation::Position XYZ poloha ruky nebo rukojeti na ovladači. Umístění dlaně Pozice uchopení Umístění dlaně
SpatialInteractionSourceLocation::Orientation Kvaternion představující orientaci ruky nebo pozice rukojeti na ovladači. Nepodporuje se Orientace pozice uchopení Orientace dlaně
SpatialPointerInteractionSourcePose::Position Původ ukazováku. Nepodporuje se Podporováno Podporováno
SpatialPointerInteractionSourcePose::ForwardDirection Směr ukazujícího paprsku. Nepodporuje se Podporováno Podporováno

Některé z výše uvedených vlastností nejsou dostupné na všech zařízeních a rozhraní API poskytuje způsob, jak to otestovat. Můžete například zkontrolovat SpatialInteractionSource::IsGraspSupported vlastnost určit, zda zdroj poskytuje akce uchopení.

Pozice gripu vs. polohovací pozice

Windows Mixed Reality podporuje ovladače pohybu v různých formách. Podporuje také kloubové systémy pro sledování rukou. Všechny tyto systémy mají různé vztahy mezi polohou ruky a přirozeným směrem "vpřed", který by aplikace měly používat k ukazování nebo vykreslování objektů v ruce uživatele. Pro podporu toho všeho jsou k dispozici dva typy 3D pozic pro sledování rukou a ovladače pohybu. První je pozice rukojeti, která představuje pozici ruky uživatele. Druhým je polohovací pozice, která představuje bodovací paprsek pocházející z ruky uživatele nebo ovladače. Pokud tedy chcete vykreslit ruku uživatele nebo předmět, který drží v ruce uživatele, například meč nebo zbraň, použijte pozici sevření. Pokud chcete zobrazit paprsek z ovladače nebo ruky, například když uživatel **ukazuje na uživatelské rozhraní, použijte polohovací pozici.

K pozici úchytu se dostanete přes SpatialInteractionSourceState::P roperties::TryGetLocation(...). Definuje se takto:

  • Pozice rukojeti: Centroid dlaně při přirozeném podržení ovladače, upraven doleva nebo doprava na střed pozice v rukojeti.
  • Pravá osa orientace rukojeti: Když zcela otevřete ruku, abyste vytvořili plochou pozici s 5 prsty, paprsek, který je normální k vaší dlani (dopředu z levé dlaně, zpět od pravé)
  • Orientace rukojeti je dopředu osa: Když částečně zavřete ruku (jako by držel ovladač), paprsek, který ukazuje "dopředu" trubicí tvořenou vašimi prsty bez palce.
  • Osa nahoru orientace úchytu: Osa nahoru odvozená z definic Doprava a Vpřed.

Přístup k pozici ukazatele můžete získat prostřednictvím SpatialInteractionSourceState::P roperties::TryGetLocation(...)::SourcePointerPose nebo SpatialInteractionSourceState::TryGetPointerPose(...)::TryGetInteractionSourcePose.

Vstupní vlastnosti specifické pro kontroler

U kontrolerů má SpatialInteractionSource vlastnost Controller s dalšími funkcemi.

  • HasThumbstick: Pokud je hodnota true, má ovladač palec. Zkontrolujte vlastnost ControllerProperties objektu SpatialInteractionSourceState a získejte hodnoty kryptografického ovladače x a y (ThumbstickX a ThumbstickY) a jeho stisknutého stavu (IsThumbstickPressed).
  • HasTouchpad: Pokud ano, ovladač má touchpad. Zkontrolujte vlastnost ControllerProperties objektu SpatialInteractionSourceState, abyste získali hodnoty touchpadu x a y (TouchpadX a TouchpadY) a abyste věděli, jestli se uživatel dotýká podložky (IsTouchpadTouched) a jestli touchpad stiská dolů (IsTouchpadPressed).
  • SimpleHapticsController: Rozhraní API SimpleHapticsController pro kontroler umožňuje kontrolovat možnosti haptiky kontroleru a také ovládat haptickou zpětnou vazbu.

Rozsah touchpadu a palce je -1 až 1 pro obě osy (odspodu nahoru a zleva doprava). Rozsah analogového triggeru, ke kterému se přistupuje pomocí SpatialInteractionSourceState::SelectPressedValue vlastnost, má rozsah od 0 do 1. Hodnota 1 koreluje s IsSelectPressed je rovna true; Jakákoli jiná hodnota koreluje s hodnotou IsSelectPressed, která je rovna false.

Kloubové sledování rukou

Rozhraní API Windows Mixed Reality poskytuje plnou podporu pro artikulované sledování rukou, například na HoloLens 2. Artikulované sledování rukou můžete použít k implementaci přímou manipulaci a vstupních modelů point-and-commit ve vašich aplikacích. Dá se také použít k vytváření plně vlastních interakcí.

Kostra rukou

Kloubové sledování rukou poskytuje 25 kostru, která umožňuje mnoho různých typů interakcí. Kostra poskytuje pět kloubů pro index / střední / prsten / malé prsty, čtyři klouby pro palec a jeden zápěstní kloub. Zápěstní kloub slouží jako základ hierarchie. Následující obrázek znázorňuje rozložení kostry.

Kostra rukou

Ve většině případů je každý kloub pojmenován na základě kosti, kterou představuje. Vzhledem k tomu, že na každém kloubu jsou dvě kosti, používáme konvenci pojmenování každého kloubu na základě dětské kosti v daném místě. Dětská kost je definována jako kost dále od zápěstí. Například "Index Proximální" kloub obsahuje počáteční pozici indexu proximální kosti a orientaci této kosti. Neobsahuje koncovou pozici kosti. Pokud to potřebujete, získáte ho z dalšího spojení v hierarchii, z indexového zprostředkujícího spojení.

Kromě 25 hierarchických kloubů poskytuje systém dlaňový kloub. Dlaň se obvykle nepovažuje za součást kosterní struktury. Poskytuje se pouze jako pohodlný způsob, jak získat celkovou pozici a orientaci ruky.

Pro každou společnou akci jsou k dispozici následující informace:

Název Popis
Position 3D pozice spojnice, která je k dispozici v libovolném požadovaném souřadnicovém systému.
Orientace 3D orientace kosti, k dispozici v libovolném požadovaném souřadnicovém systému.
RADIUS Vzdálenost k povrchu pokožky v poloze kloubu. Užitečné pro ladění přímých interakcí nebo vizualizací, které závisí na šířce prstu.
Přesnost Poskytuje nápovědu k tomu, jak si systém myslí, že má informace o tomto jointu jistotu.

K datům kostry rukou můžete přistupovat prostřednictvím funkce na spatialInteractionSourceState. Funkce se nazývá TryGetHandPose a vrací objekt s názvem HandPose. Pokud zdroj nepodporuje kloubové ruce, vrátí tato funkce hodnotu null. Jakmile budete mít HandPose, můžete získat aktuální společná data voláním TryGetJoint s názvem společného spojení, který vás zajímá. Data se vrátí jako struktura JointPose . Následující kód získá pozici ukazováčku. Proměnná currentState představuje instanci SpatialInteractionSourceState.

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

Ruční síťovina

Rozhraní API pro sledování kloubových rukou umožňuje plně deformovatelnou trojúhelníkovou síť. Tato síť se může v reálném čase deformovat spolu s kostru rukou a je užitečná pro vizualizaci a pokročilé fyzikální techniky. Pokud chcete získat přístup k síti rukou, musíte nejprve vytvořit objekt HandMeshObserver voláním TryCreateHandMeshObserverAsync na SpatialInteractionSource. To je potřeba provést pouze jednou pro každý zdroj, obvykle při prvním zobrazení. To znamená, že tuto funkci zavoláte k vytvoření objektu HandMeshObserver vždy, když ruka vstoupí do FOV. Jedná se o asynchronní funkci, takže tady budete muset řešit určitou souběžnost. Jakmile je k dispozici, můžete požádat Objekt HandMeshObserver o vyrovnávací paměť indexu trojúhelníku voláním GetTriangleIndices. Indexy nemění rámec přes rámec, takže je můžete získat jednou a uložit je do mezipaměti po celou dobu životnosti zdroje. Indexy jsou k dispozici v pořadí vinutí po směru hodinových ručiček.

Následující kód roztáčí odpojené std::thread a vytvoří pozorovatele sítě a extrahuje vyrovnávací paměť indexu, jakmile je pozorovatel sítě k dispozici. Začíná z proměnné s názvem currentState, což je instance SpatialInteractionSourceState představující sledovanou ruku.

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

Spuštění odpojeného vlákna je jen jednou z možností pro zpracování asynchronních volání. Případně můžete použít novou funkci co_await , kterou podporuje C++/WinRT.

Jakmile máte objekt HandMeshObserver, měli byste ho držet po dobu, po kterou je jeho odpovídající SpatialInteractionSource aktivní. Potom můžete každý snímek požádat o nejnovější vyrovnávací paměť vrcholů, která představuje ruku voláním GetVertexStateForPose a předáním instance HandPose , která představuje pozici, pro kterou chcete vrcholy. Každý vrchol v pufru má polohu a normální. Tady je příklad, jak získat aktuální sadu vrcholů pro ruční síť. Stejně jako dříve proměnná currentState představuje instanci SpatialInteractionSourceState.

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

Na rozdíl od skeletových spojů rozhraní API pro ruční síť neumožňuje určit souřadnicový systém pro vrcholy. Místo toho HandMeshVertexState určuje souřadnicový systém, ve které jsou zadávány vrcholy. Pak můžete získat transformaci sítě voláním tryGetTransformTo a určením požadovaného souřadnicového systému. Tuto transformaci sítě budete muset použít při každé práci s vrcholy. Tento přístup snižuje režii procesoru, zejména pokud síť používáte jenom pro účely vykreslování.

Složená gesta pohledu a potvrzení

Pro aplikace používající vstupní model pohledu a potvrzení, zejména u HoloLensu (první generace), poskytuje rozhraní API prostorového vstupu volitelný spatialGestureRecognizer , který lze použít k povolení složených gest postavených na události select. Díky směrování interakcí z SpatialInteractionManageru do prostorového znaménkéru SpatialGestureRecognizer můžou aplikace rozpoznát události klepnutí, podržení, manipulace a navigace rovnoměrně napříč ručičkami, hlasem a prostorovými vstupními zařízeními, aniž by musely ručně zpracovávat stisknutí a uvolnění.

SpatialGestureRecognizer provede pouze minimální nejednoznačnost mezi sadou požadovaných gest. Pokud například požádáte o pouhé klepnutí, může uživatel držet prst stisknutou tak dlouho, dokud se mu bude líbit, a klepnutí bude i nadále probíhat. Pokud požádáte o klepnutí i podržení, přibližně po sekundě podržení prstu se gesto posune na přidržení a klepnutí se už nezobrazí.

Pokud chcete použít SpatialGestureRecognizer, zpracujte událost InteractionDetected SpatialInteractionManager a získejte spatialPointerPose vystavenou tam. Pomocí paprsku pohledu hlavy uživatele z této pozice se protínejte s hologramy a povrchovými oky v okolí uživatele a určete, s čím chce uživatel pracovat. Pak směrujte SpatialInteraction v argumentech události na SpatialGestureRecognizer cílového hologramu pomocí metody CaptureInteraction . Tím začnete interpretovat tuto interakci podle SpatialGestureSettings nastavených v tomto rozpoznávači při vytváření – nebo trySetGestureSettings.

Na HoloLensu (první generace) by interakce a gesta měly odvozovat cílení podle pohledu uživatele, a ne vykreslovat nebo interagovat na místě ruky. Po zahájení interakce lze k ovládání gesta použít relativní pohyby ruky, jako je tomu u gesta manipulace nebo navigace.

Viz také