Head-Gaze- und Eye-Gaze-Eingabe in DirectX

Hinweis

Dieser Artikel bezieht sich auf die nativen WinRT-Legacy-APIs. Für neue native App-Projekte wird die Verwendung der OpenXR-API empfohlen.

In Windows Mixed Reality wird mithilfe von Augen- und Kopfanzeigen ermittelt, was der Benutzer betrachtet. Sie können die Daten verwenden, um primäre Eingabemodelle wie Head-Gaze und Commit zu steuern und Kontext für verschiedene Interaktionstypen bereitzustellen. Es gibt zwei Arten von Blickvektoren, die über die API verfügbar sind: Head-Gaze und Eye-Gaze. Beide werden als dreidimensionaler Strahl mit Ursprung und Richtung bereitgestellt. Anwendungen können dann in ihre Szenen oder die reale Welt raycasten und bestimmen, auf welche Ziele der Benutzer abzielt.

Head-Gaze stellt die Richtung dar, in die der Kopf des Benutzers zeigt. Stellen Sie sich den Kopf-Blick als die Position und die Vorwärtsrichtung des Geräts selbst vor, wobei die Position als Mittelpunkt zwischen den beiden Displays steht. Head-Gaze ist auf allen Mixed Reality Geräten verfügbar.

Der Blick stellt die Richtung dar, in die die Augen des Benutzers blicken. Der Ursprung befindet sich zwischen den Augen des Benutzers. Sie ist auf Mixed Reality Geräten verfügbar, die ein Eyetracking-System enthalten.

Sowohl Kopf- als auch Blickstrahlen sind über die SpatialPointerPose-API zugänglich. Rufen Sie SpatialPointerPose::TryGetAtTimestamp auf, um ein neues SpatialPointerPose-Objekt zum angegebenen Zeitstempel und Koordinatensystem zu empfangen. Dieser SpatialPointerPose enthält einen Kopf-Blick-Ursprung und eine Richtung. Es enthält auch einen Blickursprung und eine Richtung, wenn Eyetracking verfügbar ist.

Geräteunterstützung

Feature HoloLens (1. Generation) HoloLens 2 Immersive Headsets
Anvisieren mit dem Kopf ✔️ ✔️ ✔️
Blick auf die Augen ✔️

Verwenden des Kopf-Anvisierens

Um auf den Head-Gaze zuzugreifen, rufen Sie zunächst SpatialPointerPose::TryGetAtTimestamp auf, um ein neues SpatialPointerPose-Objekt zu empfangen. Übergeben Sie die folgenden Parameter.

  • Ein SpatialCoordinateSystem , das das Koordinatensystem darstellt, das Sie für den Kopf-Blick benötigen. Dies wird durch die Variable coordinateSystem im folgenden Code dargestellt. Weitere Informationen finden Sie in unserem Entwicklerhandbuch für Koordinatensysteme .
  • Ein Zeitstempel , der die genaue Uhrzeit der angeforderten Kopfhaltung darstellt. In der Regel verwenden Sie einen Zeitstempel, der dem Zeitpunkt entspricht, zu dem der aktuelle Frame angezeigt wird. Sie können diesen vorhergesagten Anzeigezeitstempel von einem HolographicFramePrediction-Objekt abrufen, auf das über den aktuellen HolographicFrame zugegriffen werden kann. Dieses HolographicFramePrediction-Objekt wird durch die Vorhersagevariable im folgenden Code dargestellt.

Sobald Sie über einen gültigen SpatialPointerPose verfügen, sind die Kopfposition und die Vorwärtsrichtung als Eigenschaften zugänglich. Der folgende Code zeigt, wie Sie darauf zugreifen.

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
}

Verwenden des Blicks

Damit Ihre Benutzer die Blickeingabe verwenden können, muss jeder Benutzer bei der ersten Verwendung des Geräts eine Eyetracking-Benutzerkalibrierung durchlaufen. Die Blick-API ähnelt dem Kopf-Anvisieren. Es verwendet die gleiche SpatialPointerPose-API , die einen Strahlenursprung und eine Richtung bereitstellt, die Sie für Ihre Szene raycasten können. Der einzige Unterschied besteht darin, dass Sie das Eyetracking explizit aktivieren müssen, bevor Sie es verwenden:

  1. Fordern Sie die Benutzerberechtigung für die Verwendung von Eyetracking in Ihrer App an.
  2. Aktivieren Sie die Funktion "Eingabe anvisieren" in Ihrem Paketmanifest.

Anfordern des Zugriffs auf die Eingabe des Blicks

Wenn Ihre App gestartet wird, rufen Sie EyesPose::RequestAccessAsync auf, um den Zugriff auf eye tracking anzufordern. Das System fordert den Benutzer bei Bedarf auf und gibt GazeInputAccessStatus::Allowed zurück, sobald der Zugriff gewährt wurde. Dies ist ein asynchroner Aufruf, sodass eine zusätzliche Verwaltung erforderlich ist. Im folgenden Beispiel wird ein getrennter std::thread gestartet, um auf das Ergebnis zu warten, das in einer Membervariable namens m_isEyeTrackingEnabled gespeichert wird.

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

Das Starten eines getrennten Threads ist nur eine Option für die Behandlung asynchroner Aufrufe. Sie können auch die neue co_await-Funktionalität verwenden, die von C++/WinRT unterstützt wird. Hier sehen Sie ein weiteres Beispiel für die Aufforderung zur Benutzerberechtigung:

  • EyesPose::IsSupported() ermöglicht es der Anwendung, das Berechtigungsdialogfeld nur dann auszulösen, wenn ein Eyetracker vorhanden ist.
  • GazeInputAccessStatus m_gazeInputAccessStatus; Dadurch soll verhindert werden, dass die Berechtigungsaufforderung immer wieder angezeigt wird.
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;	
		}
	});
}

Deklarieren der Funktion "Gaze Input "

Doppelklicken Sie in Projektmappen-Explorer auf die Datei appxmanifest. Navigieren Sie dann zum Abschnitt Funktionen , und überprüfen Sie die Funktion "Eingabe anvisieren ".

Eingabefunktion

Dadurch werden dem Abschnitt Paket in der Datei appxmanifest die folgenden Zeilen hinzugefügt:

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

Abrufen des Blickstrahls

Sobald Sie Zugriff auf ET erhalten haben, können Sie bei jedem Frame den Blick auf die Augen blicken. Rufen Sie wie beim Head-Gaze den SpatialPointerPose ab, indem Sie SpatialPointerPose::TryGetAtTimestamp mit einem gewünschten Zeitstempel und Koordinatensystem aufrufen. SpatialPointerPose enthält ein EyesPose-Objekt über die Eyes-Eigenschaft . Dies ist nur nicht NULL, wenn eye tracking aktiviert ist. Von dort aus können Sie überprüfen, ob der Benutzer im Gerät über eine Eyetracking-Kalibrierung verfügt, indem Sie EyesPose::IsCalibrationValid aufrufen. Verwenden Sie als Nächstes die Gaze-Eigenschaft , um den SpatialRay abzurufen, der die Blickposition und -richtung enthält. Die Gaze-Eigenschaft kann manchmal NULL sein. Überprüfen Sie daher unbedingt darauf. Dies kann passieren, wenn ein kalibrierter Benutzer vorübergehend die Augen schließt.

Der folgende Code zeigt, wie Sie auf den Blickstrahl zugreifen.

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

Fallback, wenn eyetracking nicht verfügbar ist

Wie in unserer Eyetracking-Designdokumentation erwähnt, sollten sowohl Designer als auch Entwickler Fälle kennen, in denen Eyetracking-Daten möglicherweise nicht verfügbar sind.

Es gibt verschiedene Gründe für die Nichtverfügbarkeit von Daten:

  • Ein Benutzer, der nicht kalibriert wird
  • Ein Benutzer hat der App den Zugriff auf seine Eyetracking-Daten verweigert.
  • Vorübergehende Störungen, z. B. Fleckungen auf dem HoloLens-Visier oder Haare, die die Augen des Benutzers verdecken.

Auch wenn einige der APIs bereits in diesem Dokument erwähnt wurden, finden Sie im Folgenden eine Zusammenfassung, wie Sie erkennen können, dass Eyetracking als Kurzübersicht verfügbar ist:

Sie können auch überprüfen, ob Ihre Eyetracking-Daten nicht veraltet sind, indem Sie ein Timeout zwischen empfangenen Aktualisierungen der Eyetracking-Daten und andernfalls einem Fallback zum Blick auf den Kopf hinzufügen, wie unten erläutert. Weitere Informationen finden Sie in unseren Überlegungen zum Fallbackentwurf .


Korrelieren des Blicks mit anderen Eingaben

Manchmal stellen Sie möglicherweise fest, dass Sie ein SpatialPointerPose benötigen, das einem Ereignis in der Vergangenheit entspricht. Wenn der Benutzer beispielsweise ein Air Tap-Steuerelement ausführt, möchte Ihre App möglicherweise wissen, was er sich gerade angeschaut hat. Zu diesem Zweck wäre die einfache Verwendung von SpatialPointerPose::TryGetAtTimestamp mit der vorhergesagten Framezeit aufgrund der Latenz zwischen Systemeingabeverarbeitung und Anzeigezeit ungenau. Wenn Sie den Blick für das Targeting verwenden, neigen unsere Augen auch dazu, sich zu bewegen, bevor sie eine Commit-Aktion abgeschlossen haben. Dies ist weniger ein Problem für einen einfachen Air Tap, sondern wird wichtiger, wenn lange Sprachbefehle mit schnellen Augenbewegungen kombiniert werden. Eine Möglichkeit, dieses Szenario zu behandeln, besteht darin, einen zusätzlichen Aufruf von SpatialPointerPose::TryGetAtTimestamp mithilfe eines historischen Zeitstempels durchzuführen, der dem Eingabeereignis entspricht.

Für Eingaben, die über SpatialInteractionManager weitergeleitet werden, gibt es jedoch eine einfachere Methode. SpatialInteractionSourceState verfügt über eine eigene TryGetAtTimestamp-Funktion. Aufruf, der eine perfekt korrelierte SpatialPointerPose ohne das Rätselraten bereitstellt. Weitere Informationen zum Arbeiten mit SpatialInteractionSourceStates finden Sie in der Dokumentation hands and motion controller in DirectX .


Kalibrierung

Damit eye tracking genau funktioniert, muss jeder Benutzer eine Eye Tracking-Benutzerkalibrierung durchlaufen. Dadurch kann das Gerät das System für eine komfortablere und hochwertigere Anzeige für den Benutzer anpassen und gleichzeitig eine genaue Augenverfolgung gewährleisten. Entwickler müssen an ihrem Ende nichts tun, um die Benutzerkalibrierung zu verwalten. Das System stellt sicher, dass der Benutzer unter folgenden Umständen zur Kalibrierung des Geräts aufgefordert wird:

  • Der Benutzer verwendet das Gerät zum ersten Mal
  • Der Benutzer hat sich zuvor von dem Kalibrierungsvorgang abgemeldet
  • Der Kalibrierungsvorgang war bei der letzten Verwendung des Geräts durch den Benutzer nicht erfolgreich

Entwickler sollten sicherstellen, dass sie Benutzern, bei denen Eye Tracking-Daten möglicherweise nicht verfügbar sind, angemessene Unterstützung bieten. Weitere Informationen zu Überlegungen zu Fallbacklösungen finden Sie unter Eye tracking on HoloLens 2.


Weitere Informationen