Spojrzenie na głowę i wejście wzroku w directx

Uwaga

Ten artykuł dotyczy starszych natywnych interfejsów API winRT. W przypadku nowych projektów aplikacji natywnych zalecamy użycie interfejsu API OpenXR.

W Windows Mixed Reality dane wejściowe wzroku i głowy są używane do określania, na czym patrzy użytkownik. Możesz użyć tych danych, aby kierować podstawowymi modelami wejściowymi, takimi jak head-gaze i commit, i zapewnić kontekst dla różnych typów interakcji. Istnieją dwa typy wektorów wzroku dostępnych za pośrednictwem interfejsu API: spojrzenie na głowę i spojrzenie wzrokowe. Oba są dostarczane jako trójwymiarowy ray ze źródłem i kierunkiem. Aplikacje mogą następnie raycast w swoich scenach lub w świecie rzeczywistym i określić, co jest celem użytkownika.

Spojrzenie na głowę reprezentuje kierunek , w jaki znajduje się głowa użytkownika. Spójrz na głowę jako położenie i kierunek do przodu samego urządzenia, z położeniem jako punktem środkowym między dwoma wyświetlaczami. Spojrzenie na głowę jest dostępne na wszystkich urządzeniach Mixed Reality.

Spojrzenie wzrokowe reprezentuje kierunek , w jaki oczy użytkownika patrzą w kierunku. Źródło znajduje się między oczami użytkownika. Jest ona dostępna na urządzeniach Mixed Reality, które obejmują system śledzenia oczu.

Zarówno promienie głowy, jak i wzroku są dostępne za pośrednictwem interfejsu API SpatialPointerPose . Wywołaj element SpatialPointerPose::TryGetAtTimestamp , aby odebrać nowy obiekt SpatialPointerPose w określonym systemie sygnatury czasowej i współrzędnych. Ten element SpatialPointerPose zawiera źródło i kierunek spojrzenia na głowę. Zawiera również źródło wzroku i kierunek, jeśli śledzenie oczu jest dostępne.

Obsługa urządzeń

Funkcja HoloLens (1. generacja) HoloLens 2 Immersyjne zestawy słuchawkowe
Spojrzenie głowy ✔️ ✔️ ✔️
Spojrzenie wzrokowe ✔️

Korzystanie z spojrzenia głowy

Aby uzyskać dostęp do spojrzenia głównego, zacznij od wywołania elementu SpatialPointerPose::TryGetAtTimestamp w celu odebrania nowego obiektu SpatialPointerPose. Przekaż następujące parametry.

  • System SpatialCoordinate, który reprezentuje odpowiedni układ współrzędnych dla spojrzenia głównego. Jest to reprezentowane przez zmienną coordinateSystem w poniższym kodzie. Aby uzyskać więcej informacji, odwiedź nasz przewodnik dewelopera ds. systemów współrzędnych .
  • Sygnatura czasowa reprezentująca dokładny czas żądanej pozycji głowy. Zazwyczaj użyjesz znacznika czasu odpowiadającego czasowi wyświetlania bieżącej ramki. Ten przewidywany znacznik czasu wyświetlania można uzyskać z obiektu HolographicFramePrediction , który jest dostępny za pośrednictwem bieżącego elementu HolographicFrame. Ten obiekt HolographicFramePrediction jest reprezentowany przez zmienną przewidywania w poniższym kodzie.

Gdy masz prawidłowy element SpatialPointerPose, położenie głowy i kierunek przesyłania dalej są dostępne jako właściwości. Poniższy kod pokazuje, jak uzyskać do nich dostęp.

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

SpatialPointerPose pointerPose = SpatialPointerPose::TryGetAtTimestamp(coordinateSystem, prediction.Timestamp());
if (pointerPose)
{
	float3 headPosition = pointerPose.Head().Position();
	float3 headForwardDirection = pointerPose.Head().ForwardDirection();

	// Do something with the head-gaze
}

Korzystanie z wzroku

Aby użytkownicy mogli używać danych wejściowych wzroku, każdy użytkownik musi przejść przez proces śledzenia oczu kalibracji użytkownika przy pierwszym użyciu urządzenia. Interfejs API wzroku jest podobny do spojrzenia na głowę. Używa on tego samego interfejsu API SpatialPointerPose , który zapewnia źródło promienia i kierunek, który można raycast przeciwko scenie. Jedyną różnicą jest to, że przed użyciem należy jawnie włączyć śledzenie oczu:

  1. Zażądaj uprawnień użytkownika do korzystania ze śledzenia oczu w aplikacji.
  2. Włącz funkcję "Dane wejściowe" w manifeście pakietu.

Żądanie dostępu do danych wejściowych wzroku

Po uruchomieniu aplikacji wywołaj aplikację EyesPose::RequestAccessAsync , aby zażądać dostępu do śledzenia oczu. System wyświetli monit użytkownika w razie potrzeby i zwróci błąd GazeInputAccessStatus::Allowed po udzieleniu dostępu. Jest to wywołanie asynchroniczne, więc wymaga nieco dodatkowego zarządzania. Poniższy przykład uruchamia odłączony element std::thread w celu oczekiwania na wynik, który jest przechowywany w zmiennej składowej o nazwie m_isEyeTrackingEnabled.

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

std::thread requestAccessThread([this]()
{
	auto status = EyesPose::RequestAccessAsync().get();

	if (status == GazeInputAccessStatus::Allowed)
		m_isEyeTrackingEnabled = true;
	else
		m_isEyeTrackingEnabled = false;
});

requestAccessThread.detach();

Uruchamianie odłączonego wątku jest tylko jedną z opcji obsługi wywołań asynchronicznych. Możesz również użyć nowej funkcji co_await obsługiwanej przez język C++/WinRT. Oto kolejny przykład monitowania o uprawnienie użytkownika:

  • EyesPose::IsSupported() umożliwia aplikacji wyzwalanie okna dialogowego uprawnień tylko wtedy, gdy istnieje monitor oczu.
  • GazeInputAccessStatus m_gazeInputAccessStatus; Zapobiega to pojawianiu się monitu o uprawnienia.
GazeInputAccessStatus m_gazeInputAccessStatus; // This is to prevent popping up the permission prompt over and over again.

// This will trigger to show the permission prompt to the user.
// Ask for access if there is a corresponding device and registry flag did not disable it.
if (Windows::Perception::People::EyesPose::IsSupported() &&
   (m_gazeInputAccessStatus == GazeInputAccessStatus::Unspecified))
{ 
	Concurrency::create_task(Windows::Perception::People::EyesPose::RequestAccessAsync()).then(
	[this](GazeInputAccessStatus status)
	{
  		// GazeInputAccessStatus::{Allowed, DeniedBySystem, DeniedByUser, Unspecified}
    		m_gazeInputAccessStatus = status;
		
		// Let's be sure to not ask again.
		if(status == GazeInputAccessStatus::Unspecified)
		{
      			m_gazeInputAccessStatus = GazeInputAccessStatus::DeniedBySystem;	
		}
	});
}

Deklarowanie możliwości wprowadzania spojrzenia

Kliknij dwukrotnie plik appxmanifest w Eksplorator rozwiązań. Następnie przejdź do sekcji Możliwości i sprawdź możliwości wprowadzania opinii .

Możliwość wprowadzania spojrzenia

Spowoduje to dodanie następujących wierszy do sekcji Pakiet w pliku appxmanifest:

  <Capabilities>
    <DeviceCapability Name="gazeInput" />
  </Capabilities>

Uzyskiwanie promienia wzroku

Po otrzymaniu dostępu do ET możesz chwycić wzrok wzroku każdej ramki. Podobnie jak w przypadku spojrzenia głównego, pobierz element SpatialPointerPose, wywołując element SpatialPointerPose::TryGetAtTimestamp z żądanym znacznikiem czasu i układem współrzędnych. Obiekt SpatialPointerPose zawiera obiekt EyesPose za pośrednictwem właściwości Eyes . Jest to wartość niepusta tylko wtedy, gdy śledzenie oczu jest włączone. W tym miejscu możesz sprawdzić, czy użytkownik na urządzeniu ma kalibrację śledzenia oczu, wywołując eyesPose::IsCalibrationValid. Następnie użyj właściwości Gaze , aby uzyskać pozycję SpatialRay zawierającą położenie i kierunek wzroku. Właściwość Gaze może czasami mieć wartość null, dlatego upewnij się, że jest to sprawdzana. Może się to zdarzyć, jeśli skalibrowany użytkownik tymczasowo zamyka oczy.

Poniższy kod pokazuje, jak uzyskać dostęp do promienia wzroku.

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

SpatialPointerPose pointerPose = SpatialPointerPose::TryGetAtTimestamp(coordinateSystem, prediction.Timestamp());
if (pointerPose)
{
	if (pointerPose.Eyes() && pointerPose.Eyes().IsCalibrationValid())
	{
		if (pointerPose.Eyes().Gaze())
		{
			auto spatialRay = pointerPose.Eyes().Gaze().Value();
			float3 eyeGazeOrigin = spatialRay.Origin;
			float3 eyeGazeDirection = spatialRay.Direction;
			
			// Do something with the eye-gaze
		}
	}
}

Powrót, gdy śledzenie oczu nie jest dostępne

Jak wspomniano w dokumentacji projektowej śledzenia oczu, zarówno projektanci, jak i deweloperzy powinni mieć świadomość wystąpień, w których dane śledzenia oczu mogą nie być dostępne.

Istnieją różne przyczyny niedostępności danych:

  • Użytkownik, który nie jest kalibrowany
  • Użytkownik zaprzeczył dostępowi aplikacji do danych śledzenia oczu
  • Tymczasowe zakłócenia, takie jak rozmazy na wizjerce HoloLens lub włosy okludniające oczy użytkownika.

Chociaż niektóre interfejsy API zostały już wymienione w tym dokumencie, w poniższym artykule przedstawiono podsumowanie sposobu wykrywania, że śledzenie oczu jest dostępne jako krótkie informacje:

Możesz również sprawdzić, czy dane śledzenia oczu nie są nieaktualne, dodając limit czasu między odebranym aktualizacjami danych śledzenia oczu a w przeciwnym razie powrót do spojrzenia głównego, jak opisano poniżej. Aby uzyskać więcej informacji, odwiedź nasze zagadnienia dotyczące projektowania rezerwowego .


Korelowanie spojrzenia z innymi danymi wejściowymi

Czasami może się okazać, że potrzebujesz elementu SpatialPointerPose odpowiadającego zdarzeniu w przeszłości. Jeśli na przykład użytkownik korzysta z funkcji Air Tap, aplikacja może chcieć wiedzieć, co patrzy. W tym celu po prostu użycie funkcji SpatialPointerPose::TryGetAtTimestamp z przewidywanym czasem ramki byłoby niedokładne z powodu opóźnienia między przetwarzaniem danych wejściowych systemu i czasem wyświetlania. Ponadto, jeśli używasz wzroku do określania celu, nasze oczy mają tendencję do poruszania się jeszcze przed zakończeniem akcji zatwierdzenia. Jest to mniej problemu dla prostego naciśnięcia Air Tap, ale staje się bardziej krytyczne podczas łączenia długich poleceń głosowych z szybkimi ruchami oczu. Jednym ze sposobów obsługi tego scenariusza jest wykonanie dodatkowego wywołania elementu SpatialPointerPose::TryGetAtTimestamp przy użyciu historycznego znacznika czasu odpowiadającego zdarzeniu wejściowemu.

Jednak w przypadku danych wejściowych, które są trasy za pośrednictwem narzędzia SpatialInteractionManager, jest łatwiejsza metoda. Element SpatialInteractionSourceState ma własną funkcję TryGetAtTimestamp . Wywołanie, które zapewni idealnie skorelowany element SpatialPointerPose bez zgadywania. Aby uzyskać więcej informacji na temat pracy z usługą SpatialInteractionSourceStates, zapoznaj się z dokumentacją Kontrolery rąk i ruchu w programie DirectX .


Kalibracja

Aby śledzenie oczu działało dokładnie, każdy użytkownik musi przejść przez kalibrację użytkownika śledzenia oczu. Dzięki temu urządzenie może dostosować system w celu zapewnienia wygodniejszego i wyższej jakości wyświetlania dla użytkownika oraz zapewnienia dokładnego śledzenia oczu w tym samym czasie. Deweloperzy nie muszą nic robić na końcu, aby zarządzać kalibracją użytkownika. System zapewni, że użytkownik otrzyma monit o kalibrowanie urządzenia w następujących okolicznościach:

  • Użytkownik używa urządzenia po raz pierwszy
  • Użytkownik wcześniej zrezygnował z procesu kalibracji
  • Proces kalibracji nie zakończył się pomyślnie, gdy użytkownik użył urządzenia

Deweloperzy powinni zapewnić odpowiednią obsługę dla użytkowników, w których dane śledzenia oczu mogą nie być dostępne. Dowiedz się więcej o zagadnieniach dotyczących rozwiązań rezerwowych na stronie Śledzenie oczu na HoloLens 2.


Zobacz też