Dane wejściowe w spojrzeniach głowy i spojrzeniach na oczy w directx

Uwaga

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

W Windows Mixed Reality dane wejściowe dla oka i głowy są używane do określenia, na co patrzy użytkownik. Za pomocą danych można kierować podstawowymi modelami wejściowymi, na przykład spojrzeniemgłowy i zatwierdzaniem, oraz zapewniać kontekst dla różnych typów interakcji. Za pośrednictwem interfejsu API są dostępne dwa typy wektorów spojrzenia: spojrzenie głowy i spojrzenie okiem. Oba są dostarczane jako trójwymiarowe promienie ze źródłem i kierunkiem. Aplikacje mogą następnie przejść do ich scen lub do świata rzeczywistego i określić, co jest przeznaczone dla użytkownika.

Spojrzenie głowy reprezentuje kierunek wskazywany przez użytkownika. Spojrzenie na głowy należy myśleć jak o położeniu i kierunku do przodu samego urządzenia, z pozycją jako środkowym punktem między dwoma ekranami. Spojrzenie na głowy jest dostępne na wszystkich Mixed Reality urządzeniach.

Spojrzenie na oko reprezentuje kierunek, w którym patrzy wzrok użytkownika. Źródło znajduje się między wzrokiem użytkownika. Jest ona dostępna na urządzeniach Mixed Reality, które zawierają system śledzenia wzroku.

Promienie głowy i oczu są dostępne za pośrednictwem interfejsu API SpatialPointerPose. Wywołaj obiekt SpatialPointerPose::TryGetAtTimestamp, aby otrzymać nowy obiekt SpatialPointerPose w określonym znaczniku czasu i systemie współrzędnych. To spatialPointerPose zawiera początek i kierunek spojrzenia głowy. Zawiera również kierunek i pochodzenie spojrzenia, jeśli jest dostępne śledzenie wzroku.

Obsługa urządzeń

Funkcja HoloLens (1. generacja) HoloLens 2 Immersywne zestawy nagłow
Spojrzenie na głowy ✔️ ✔️ ✔️
Spojrzenie na oko ✔️

Korzystanie z spojrzenia głowy

Aby uzyskać dostęp do spojrzenia głowy, rozpocznij od wywołania obiektu SpatialPointerPose::TryGetAtTimestamp w celu otrzymania nowego obiektu SpatialPointerPose. Przekaż następujące parametry.

  • System SpatialCoordinateSystem reprezentujący system współrzędnych, który ma być spojrzeniami głowy. Jest on reprezentowany przez zmienną coordinateSystem w poniższym kodzie. Aby uzyskać więcej informacji, odwiedź nasz przewodnik dla deweloperów systemów współrzędnych.
  • Znacznik czasu, który reprezentuje dokładny czas żądanej pozycji głowy. Zazwyczaj używa się znacznika czasu odpowiadającego godzinie 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żącej ramki HolographicFrame. Ten obiekt HolographicFramePrediction jest reprezentowany przez zmienną przewidywania w poniższym kodzie.

Po prawidłowym obiektu SpatialPointerPose pozycja głowy i kierunek do przodu 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 spojrzenia wzrokowego

Aby użytkownicy korzystali z danych wejściowych dla oka, każdy użytkownik musi przejść przez śledzenie wzroku, gdy pierwszy raz korzysta z urządzenia. Interfejs API spojrzenia oczu jest podobny do spojrzenia głowy. Używa tego samego interfejsu API SpatialPointerPose, który zapewnia kierunek i pochodzenie promienia, które można zmiestrować na scenie. Jedyna różnica polega na tym, że należy jawnie włączyć śledzenie wzroku przed jego użyciem:

  1. Zażądaj uprawnień użytkownika do korzystania ze śledzenia wzroku w aplikacji.
  2. Włącz możliwość "Danych wejściowych spojrzenia" w manifeście pakietu.

Żądanie dostępu do danych wejściowych dla oka

Po uruchomieniu aplikacji wywołaj aplikację EyesPose::RequestAccessAsync, aby zażądać dostępu do śledzenia wzroku. System wyświetli monit dla użytkownika w razie potrzeby i zwróci komunikat GazeInputAccessStatus::Allowed po przyznaeniu dostępu. Jest to wywołanie asynchroniczne, więc wymaga nieco dodatkowego zarządzania. W poniższym przykładzie jest wywoływana odłączona zmienna std::thread w celu oczekiwania na wynik, który przechowuje w zmiennej członkowskiej 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łączony wątek jest tylko jedną z opcji obsługi wywołań asynchronicznych. Można również użyć nowych funkcji co_await obsługiwanych przez C++/WinRT. Oto inny przykład z pytaniem o uprawnienia użytkownika:

  • Funkcja EyesPose::IsSupported() umożliwia aplikacji wyzwalanie okna dialogowego uprawnień tylko wtedy, gdy istnieje monitor oka.
  • Statystyki funkcji GazeInputAccessStatus m_gazeInputAccessStatus; Ma to na celu zapobieganie powtarzaniu 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 danych zaglądanych

Kliknij dwukrotnie plik appxmanifest w Eksplorator rozwiązań. Następnie przejdź do sekcji Capabilities (Możliwości) i sprawdź możliwość wprowadzania danych przez spojrzenie.

Możliwość wprowadzania danych zaglądaj

Powoduje to dodanie następujących wierszy do sekcji Package w pliku appxmanifest:

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

Uzyskiwanie promienia wzroku

Po otrzymaniu dostępu do et można chwycić promienia oka każdej ramki. Podobnie jak w przypadku spojrzenia głowy, pobierz dane SpatialPointerPose, wywołując znacznik SpatialPointerPose::TryGetAtTimestamp z żądanym sygnaturą czasową i układem współrzędnych. Obiekt SpatialPointerPose zawiera obiekt EyesPose za pośrednictwem właściwości Eyes. Ta wartość nie ma wartości null tylko wtedy, gdy śledzenie wzroku jest włączone. W tym miejscu możesz sprawdzić, czy użytkownik na urządzeniu ma śledzenie wzroku, wywołując element EyesPose::IsCalibrationValid. Następnie użyj właściwości Gaze, aby uzyskać obiekt SpatialRay zawierający położenie i kierunek spojrzenia. Właściwość Gaze może czasami mieć wartość null, dlatego należy to sprawdzić. Może się tak zdarzyć, jeśli użytkownik skalibrowany tymczasowo zamknie wzrok.

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

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

Rezerwowe, gdy śledzenie wzroku nie jest dostępne

Jak wspomniano w naszej dokumentów dotyczącychprojektowania śledzenia oczu, zarówno projektanci, jak i deweloperzy powinni wiedzieć o wystąpieniach, w których dane śledzenia oczu mogą być niedostępne.

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

  • Użytkownik nie jest skalibrowany
  • Użytkownik odrzucił dostęp aplikacji do swoich danych śledzenia wzroku
  • Tymczasowe zakłócenia, takie jak rozmycie HoloLens lub zarostu ochłodzające oczy użytkownika.

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

Możesz również sprawdzić, czy dane śledzenia oczu nie są nieaktualne, dodając limit czasu między odebranymi aktualizacjami danych śledzenia oka i w inny sposób powrotu do spojrzenia głowy, jak omówiono poniżej.
Odwiedź nasze rezerwowe zagadnienia dotyczące projektowania, aby uzyskać więcej informacji.


Korelowanie spojrzenia z innymi wejściami

Czasami może się okazać, że potrzebny jest element SpatialPointerPose, który odpowiada zdarzeniu w przeszłości. Jeśli na przykład użytkownik korzysta z naciśnięcia w powietrzu, aplikacja może chcieć wiedzieć, na co patrzył. W tym celu użycie funkcji SpatialPointerPose::TryGetAtTimestamp z przewidywanym czasem ramek byłoby niedokładne z powodu opóźnienia między przetwarzaniem danych wejściowych systemu a czasem wyświetlania. Ponadto, jeśli używasz spojrzenia wzrokowego do określania celu, nasze oczy zwykle sięgają dalej, nawet przed zakończeniem akcji zatwierdzenia. Nie stanowi to problemu w przypadku prostego naciśnięcia w powietrzu, ale staje się bardziej krytyczne w przypadku łączenia długich poleceń głosowych z szybkimi ruchami oczu. Jednym ze sposobów obsługi tego scenariusza jest wywołanie dodatkowego wywołania metody SpatialPointerPose::TryGetAtTimestampprzy użyciu historycznego znacznika czasu odpowiadającego zdarzeniu wejściowemu.

Jednak w przypadku danych wejściowych, które są trasami przez element SpatialInteractionManager, istnieje łatwiejsza metoda. Funkcja SpatialInteractionSourceState ma własną funkcję TryGetAtTimestamp. Wywołanie , które zapewni idealnie skorelowany spatialPointerPose bez zgadywanie. Aby uzyskać więcej informacji na temat pracy z spatialInteractionSourceStates, zobacz dokumentację Hands and Motion Controllers in DirectX (Kontrolery rąk i ruchu w directx).


Kalibracja

Aby śledzenie wzroku działało dokładnie, każdy użytkownik musi przejść przez śledzenie wzroku. Dzięki temu urządzenie może dostosować system w celu zapewnienia użytkownikom wygodniejszego i wyższego komfortu przeglądania oraz zapewnienia dokładnego śledzenia wzroku w tym samym czasie. Deweloperzy nie muszą nic robić po swojej stronie, aby zarządzać opiniami użytkowników. System zapewni, że użytkownik zostanie poproszony o skalibrowanie urządzenia w następujących okolicznościach:

  • Użytkownik korzysta z urządzenia po raz pierwszy
  • Użytkownik wcześniej zrezygnował z procesu odc.
  • Proces odtąd zakończył się powodzeniem, gdy użytkownik użył urządzenia

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


Zobacz też