Kopf- und Augenblickeingaben in DirectX

Hinweis

Dieser Artikel bezieht sich auf die älteren WinRT-nativen APIs. Für neue native App-Projekte empfehlen wir die Verwendung der OpenXR-API.

In Windows Mixed Reality wird die Augen- und Kopfblickeingabe verwendet, um zu bestimmen, was der Benutzer sieht. 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: Kopf- und Augenblick. Beide werden als dreidimensionale Strahl mit Ursprung und Richtung bereitgestellt. Anwendungen können dann in ihre Szenen oder die reale Welt übertragen und bestimmen, was der Benutzer angibt.

Kopfblick stellt die Richtung dar, in die der Kopf des Benutzers verweist. Denken Sie an die Kopfanzeige als Position und Vorwärtsrichtung des Geräts selbst, wobei die Position als Mittelpunkt zwischen den beiden Anzeigen angezeigt wird. Head-Gaze ist auf allen Mixed Reality Geräten verfügbar.

Augenblick stellt die Richtung dar, die die Augen des Benutzers anschaut. Der Ursprung befindet sich zwischen den Augen des Benutzers. Es ist auf Mixed Reality Geräten verfügbar, die ein Augenverfolgungssystem enthalten.

Sowohl Kopf- als auch Augenblickstrahlen sind über die SpatialPointerPose-API zugänglich. Rufen Sie SpatialPointerPose::TryGetAtTimestamp auf, um ein neues SpatialPointerPose-Objekt im angegebenen Zeitstempel- und Koordinatensystem zu erhalten. Dieses SpatialPointerPose enthält einen Kopf-Blick-Ursprung und eine Richtung. Es enthält auch einen Augenblick-Ursprung und eine Richtung, wenn die Augenverfolgung verfügbar ist.

Geräteunterstützung

Feature HoloLens (1. Generation) HoloLens 2 Immersive Headsets
Anvisieren mit dem Kopf ✔️ ✔️ ✔️
Augenblick ✔️

Verwenden von Kopfblicken

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

  • Ein SpatialCoordinateSystem , das das gewünschte Koordinatensystem für den Kopfblick darstellt. Dies wird durch die KoordinateSystem-Variable im folgenden Code dargestellt. Weitere Informationen finden Sie in unserem Entwicklerhandbuch für Koordinatensysteme .
  • Ein Zeitstempel , der die genaue Uhrzeit der angeforderten Kopfpose darstellt. Normalerweise verwenden Sie einen Zeitstempel, der dem Zeitpunkt entspricht, zu dem der aktuelle Frame angezeigt wird. Sie können diesen vorhergesagten Zeitstempel aus einem HolographicFramePrediction-Objekt abrufen, das über den aktuellen HolographicFrame zugänglich ist. Dieses HolographicFramePrediction-Objekt wird durch die Vorhersagevariable im folgenden Code dargestellt.

Sobald Sie über eine gültige SpatialPointerPose verfügen, sind die Kopfposition und die Vorwärtsrichtung als Eigenschaften zugänglich. Im folgenden Code wird gezeigt, wie sie darauf zugreifen können.

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 von Augenblick

Damit Ihre Benutzer Augenblickeingaben verwenden können, muss jeder Benutzer eine Augenverfolgungsbenutzerkalibrierung durchlaufen, wenn sie das Gerät zum ersten Mal verwenden. Die Augen-Blick-API ähnelt dem Kopfblick. Es verwendet die gleiche SpatialPointerPose-API , die einen Ray-Ursprung und eine Richtung bereitstellt, die Sie gegen Ihre Szene ausstrahlen können. Der einzige Unterschied besteht darin, dass Sie die Augenverfolgung explizit aktivieren müssen, bevor Sie es verwenden:

  1. Bitten Sie die Benutzerberechtigung, die Augenverfolgung in Ihrer App zu verwenden.
  2. Aktivieren Sie die Funktion "Gaze Input" im Paketmanifest.

Anfordern des Zugriffs auf Augenblickeingaben

Wenn Ihre App gestartet wird, rufen Sie EyesPose::RequestAccessAsync auf, um Zugriff auf Die Augenverfolgung anzufordern. Das System fordert den Benutzer nach Bedarf auf, und gibt GazeInputAccessStatus::Zulässig , sobald der Zugriff erteilt wurde. Dies ist ein asynchroner Aufruf, sodass es ein bisschen zusätzliche Verwaltung erfordert. Im folgenden Beispiel wird ein getrennter std::thread gedreht, um auf das Ergebnis zu warten, das sie an eine Membervariable mit dem Namen m_isEyeTrackingEnabled speichert.

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 von asynchronen Aufrufen. 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 auszulösen, wenn ein Eye Tracker vorhanden ist.
  • GazeInputAccessStatus m_gazeInputAccessStatus; Dies ist das Verhindern, dass die Berechtigungsaufforderung erneut 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-Eingabe "

Doppelklicken Sie in Projektmappen-Explorer auf die Appxmanifest-Datei. Navigieren Sie dann zum Abschnitt "Funktionen " und überprüfen Sie die Funktion "Blickeingabe ".

Gaze input capability

Dies fügt den Abschnitt "Paket " in der Datei "Appxmanifest" die folgenden Zeilen hinzu:

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

Abrufen des Blickstrahls

Sobald Sie Zugriff auf ET erhalten haben, können Sie den Blickstrahl jedes Frame erfassen. Wie bei Kopfsicht erhalten Sie das SpatialPointerPose durch Aufrufen von SpatialPointerPose::TryGetAtTimestamp mit einem gewünschten Zeitstempel und Koordinatensystem. Das SpatialPointerPose-Objekt enthält ein EyesPose-Objekt über die Eyes-Eigenschaft . Dies ist nicht null, wenn die Augenverfolgung aktiviert ist. Von dort aus können Sie überprüfen, ob der Benutzer auf dem Gerät über eine Augenverfolgungskalibrierung verfügt, indem Sie EyesPose::IsCalibrationValid aufrufen. Verwenden Sie als Nächstes die Gaze-Eigenschaft , um die SpatialRay-Eigenschaft abzurufen, die die Augenblickposition und -richtung enthält. Die Gaze-Eigenschaft kann manchmal null sein, daher stellen Sie sicher, dass Sie nach dieser Eigenschaft suchen. Dies kann passieren, wenn ein kalibrierter Benutzer vorübergehend seine Augen schließt.

Im folgenden Code wird gezeigt, wie sie auf den Blickstrahl zugreifen können.

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 die Augenverfolgung nicht verfügbar ist

Wie in unseren Augenverfolgungsdesigndokumenten erwähnt, sollten sowohl Designer als auch Entwickler auf Instanzen aufmerksam sein, in denen Augenverfolgungsdaten 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 den Zugriff auf die App auf seine Augenverfolgungsdaten verweigert.
  • Temporäre Störungen, wie z. B. Schmierungen auf dem HoloLens Visier oder Haar, das die Augen des Benutzers verdeckt.

Während einige der APIs bereits in diesem Dokument erwähnt wurden, stellen wir eine Zusammenfassung zur Erkennung dieser Augenverfolgung als Kurzübersicht bereit:

Möglicherweise möchten Sie auch überprüfen, ob Ihre Augenverfolgungsdaten nicht veraltet sind, indem Sie ein Timeout zwischen empfangenen Aktualisierungen von Augenverfolgungsdaten hinzufügen und andernfalls fallbacken, wie unten beschrieben. Besuchen Sie unsere Fallbackdesignaspekte , um weitere Informationen zu erhalten.


Korrelieren des Blicks mit anderen Eingaben

Manchmal finden Sie, dass Sie eine SpatialPointerPose benötigen, die einem Ereignis in der Vergangenheit entspricht. Wenn der Benutzer z. B. ein Air Tap ausführt, möchte Ihre App möglicherweise wissen, was sie sehen. Für diesen Zweck wäre die Verwendung von SpatialPointerPose::TryGetAtTimestamp mit der vorhergesagten Framezeit aufgrund der Latenz zwischen Systemeingabeverarbeitung und Anzeigezeit ungenau. Auch wenn wir Augenblicke zum Ziel verwenden, neigen unsere Augen dazu, selbst vor abschluss einer Commit-Aktion zu wechseln. Dies ist weniger ein Problem für ein einfaches Air Tap, wird aber bei der Kombination von langen Sprachbefehlen mit schnellen Augenbewegungen wichtiger. Eine Möglichkeit zum Behandeln dieses Szenarios besteht darin, einen zusätzlichen Aufruf an SpatialPointerPose::TryGetAtTimestamp mit einem historischen Zeitstempel zu erstellen, der dem Eingabeereignis entspricht.

Für Eingaben, die über den SpatialInteractionManager weitergeleitet werden, gibt es jedoch eine einfachere Methode. Die SpatialInteractionSourceState verfügt über eine eigene TryGetAtTimestamp-Funktion . Anrufe, die ein perfekt korreliertes SpatialPointerPose ohne Diekarbeit bieten. Weitere Informationen zur Arbeit mit SpatialInteractionSourceStates sehen Sie sich die Dokumentation "Hands and Motion Controller" in DirectX an .


Kalibrierung

Damit die Augenverfolgung genau funktioniert, muss jeder Benutzer eine Augenverfolgungsbenutzerkalibrierung durchlaufen. Dadurch kann das Gerät das System für eine komfortablere und höhere Qualität der Anzeigeumgebung für den Benutzer anpassen und gleichzeitig genaue Augenverfolgung gewährleisten. Entwickler müssen nichts am Ende tun, um die Benutzerkalibrierung zu verwalten. Das System stellt sicher, dass der Benutzer aufgefordert wird, das Gerät unter den folgenden Umständen zu kalibrieren:

  • 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 Benutzer angemessene Unterstützung für Benutzer bereitstellen, in denen Augenverfolgungsdaten möglicherweise nicht verfügbar sind. Erfahren Sie mehr über Überlegungen zu Fallbacklösungen bei Eye Tracking on HoloLens 2.


Siehe auch