Input dello sguardo fisso e dello sguardo fisso con la testa in DirectX

Nota

Questo articolo è correlato alle API native WinRT legacy. Per i nuovi progetti di app native, è consigliabile usare l'API OpenXR.

In Windows Mixed Reality, l'input dello sguardo fisso e della testa viene usato per determinare cosa sta guardando l'utente. È possibile usare i dati per guidare i modelli di input principali, ad esempio lo sguardo fisso e il commit, e fornire il contesto per diversi tipi di interazione. Esistono due tipi di vettori di sguardo fisso disponibili tramite l'API: sguardo fisso e sguardo fisso. Entrambi vengono forniti come raggio tridimensionale con un'origine e una direzione. Le applicazioni possono quindi eseguire il raycast nelle loro scene o nel mondo reale e determinare quale destinazione ha l'utente.

Lo sguardo fisso rappresenta la direzione in cui punta la testa dell'utente. Si pensi allo sguardo fisso della testa come alla posizione e alla direzione in avanti del dispositivo stesso, con la posizione come punto centrale tra i due schermi. Lo sguardo fisso è disponibile in tutti i dispositivi Realtà mista.

Lo sguardo fisso rappresenta la direzione verso cui si guardano gli occhi dell'utente. L'origine si trova tra gli occhi dell'utente. È disponibile nei dispositivi Realtà mista che includono un sistema di tracciamento oculare.

I raggi head e sguardo fisso sono accessibili tramite l'API SpatialPointerPose . Chiamare SpatialPointerPose::TryGetAtTimestamp per ricevere un nuovo oggetto SpatialPointerPose nel timestamp e nel sistema di coordinate specificati. Questo oggetto SpatialPointerPose contiene un'origine e una direzione dello sguardo fisso. Contiene anche un'origine dello sguardo fisso e una direzione se il tracciamento oculare è disponibile.

Supporto di dispositivi

Funzionalità HoloLens (prima generazione) HoloLens 2 Visori VR immersive
Puntamento con la testa ✔️
Sguardo fisso ✔️

Uso dello sguardo fisso

Per accedere allo sguardo fisso, iniziare chiamando SpatialPointerPose::TryGetAtTimestamp per ricevere un nuovo oggetto SpatialPointerPose. Passare i parametri seguenti.

  • SpatialCoordinateSystem che rappresenta il sistema di coordinate desiderato per lo sguardo fisso. Questo valore è rappresentato dalla variabile coordinateSystem nel codice seguente. Per altre informazioni, visitare la guida per gli sviluppatori dei sistemi di coordinate .
  • Timestamp che rappresenta l'ora esatta della posizione della testa richiesta. In genere, si userà un timestamp che corrisponde all'ora in cui verrà visualizzato il frame corrente. È possibile ottenere questo timestamp di visualizzazione stimato da un oggetto HolographicFramePrediction , accessibile tramite l'holographicFrame corrente. Questo oggetto HolographicFramePrediction è rappresentato dalla variabile di stima nel codice seguente.

Dopo aver ottenuto un oggetto SpatialPointerPose valido, la posizione della testa e la direzione in avanti sono accessibili come proprietà. Il codice seguente illustra come accedervi.

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
}

Uso dello sguardo fisso

Affinché gli utenti usino l'input dello sguardo fisso, ogni utente deve eseguire una calibrazione utente di tracciamento oculare la prima volta che usano il dispositivo. L'API sguardo fisso è simile allo sguardo fisso. Usa la stessa API SpatialPointerPose , che fornisce un'origine raggio e una direzione che è possibile eseguire il raycast sulla scena. L'unica differenza è che è necessario abilitare esplicitamente il tracciamento oculare prima di usarlo:

  1. Richiedere all'utente l'autorizzazione per usare il tracciamento oculare nell'app.
  2. Abilitare la funzionalità "Gaze Input" nel manifesto del pacchetto.

Richiesta di accesso all'input sguardo fisso

Quando l'app viene avviata, chiama EyesPose::RequestAccessAsync per richiedere l'accesso al tracciamento oculare. Il sistema richiederà all'utente, se necessario, e restituirà GazeInputAccessStatus::Allowed una volta concesso l'accesso. Si tratta di una chiamata asincrona, quindi richiede una gestione aggiuntiva. Nell'esempio seguente viene attivato un std::thread scollegato per attendere il risultato archiviato in una variabile membro denominata 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();

L'avvio di un thread scollegato è solo un'opzione per la gestione delle chiamate asincrone. È anche possibile usare la nuova funzionalità di co_await supportata da C++/WinRT. Ecco un altro esempio per richiedere l'autorizzazione utente:

  • EyesPose::IsSupported() consente all'applicazione di attivare la finestra di dialogo di autorizzazione solo se è presente un tracciamento oculare.
  • GazeInputAccessStatus m_gazeInputAccessStatus; Ciò impedisce di visualizzare nuovamente la richiesta di autorizzazione.
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;	
		}
	});
}

Dichiarazione della funzionalità di input sguardo fisso

Fare doppio clic sul file appxmanifest in Esplora soluzioni. Passare quindi alla sezione Funzionalità e controllare la funzionalità Di input sguardo fisso .

Funzionalità di input sguardo fisso

In questo modo vengono aggiunte le righe seguenti alla sezione Package nel file appxmanifest:

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

Ottenere il raggio dello sguardo fisso

Dopo aver ricevuto l'accesso a ET, è possibile afferrare il raggio sguardo fisso ogni fotogramma. Come per lo sguardo fisso, ottenere SpatialPointerPose chiamando SpatialPointerPose::TryGetAtTimestamp con un timestamp e un sistema di coordinate desiderati. SpatialPointerPose contiene un oggetto EyesPose tramite la proprietà Eyes . Si tratta di un valore diverso da Null solo se il tracciamento oculare è abilitato. Da qui è possibile verificare se l'utente nel dispositivo ha una calibrazione del tracciamento oculare chiamando EyesPose::IsCalibrationValid. Usare quindi la proprietà Gaze per ottenere spatialRay contenente la posizione e la direzione dello sguardo fisso. A volte la proprietà Gaze può essere Null, quindi assicurarsi di verificarne la presenza. Ciò può verificarsi se un utente calibrato chiude temporaneamente gli occhi.

Il codice seguente illustra come accedere al raggio dello sguardo fisso.

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 quando il tracciamento oculare non è disponibile

Come accennato nella documentazione sulla progettazione del tracciamento oculare, sia i progettisti che gli sviluppatori devono essere consapevoli delle istanze in cui i dati di tracciamento oculare potrebbero non essere disponibili.

Esistono diversi motivi per cui i dati non sono disponibili:

  • Un utente non è calibrato
  • Un utente ha negato l'accesso dell'app ai dati di tracciamento oculare
  • Interferenze temporanee, ad esempio smuding sul visore HoloLens o capelli che occlusione degli occhi dell'utente.

Anche se alcune delle API sono già state menzionate in questo documento, di seguito viene fornito un riepilogo di come rilevare che il tracciamento oculare è disponibile come riferimento rapido:

È anche possibile verificare che i dati di tracciamento oculare non siano obsoleti aggiungendo un timeout tra gli aggiornamenti dei dati di tracciamento oculare ricevuti e in caso contrario il fallback allo sguardo fisso, come illustrato di seguito. Per altre informazioni, vedere le considerazioni sulla progettazione del fallback .


Correlazione dello sguardo fisso con altri input

In alcuni casi potrebbe essere necessario un oggetto SpatialPointerPose che corrisponde a un evento in passato. Ad esempio, se l'utente esegue un tocco Air, l'app potrebbe voler sapere cosa stava guardando. A questo scopo, l'uso di SpatialPointerPose::TryGetAtTimestamp con il tempo di intervallo stimato non sarebbe accurato a causa della latenza tra l'elaborazione dell'input di sistema e l'ora di visualizzazione. Inoltre, se si usa lo sguardo fisso per la destinazione, gli occhi tendono ad andare avanti anche prima di completare un'azione di commit. Questo è meno un problema per un semplice tocco Air, ma diventa più critico quando si combinano comandi vocali lunghi con movimenti oculari veloci. Un modo per gestire questo scenario consiste nell'effettuare una chiamata aggiuntiva a SpatialPointerPose::TryGetAtTimestamp, usando un timestamp cronologico che corrisponde all'evento di input.

Tuttavia, per l'input che instrada attraverso SpatialInteractionManager, esiste un metodo più semplice. SpatialInteractionSourceState ha la propria funzione TryGetAtTimestamp. Chiamando che fornirà un oggetto SpatialPointerPose perfettamente correlato senza indovinate. Per altre informazioni sull'uso di SpatialInteractionSourceStates, vedere la documentazione mani e controller di movimento nella documentazione di DirectX .


Calibrazione

Per il corretto funzionamento del tracciamento oculare, ogni utente deve eseguire la calibrazione dell'utente con tracciamento oculare. Questo consente al dispositivo di regolare il sistema per un'esperienza di visualizzazione più confortevole e di qualità superiore per l'utente e per garantire un tracciamento oculare accurato contemporaneamente. Gli sviluppatori non devono eseguire alcuna operazione per gestire la calibrazione degli utenti. Il sistema garantisce che all'utente venga richiesto di calibrare il dispositivo nelle circostanze seguenti:

  • L'utente usa il dispositivo per la prima volta
  • L'utente ha precedentemente rifiutato esplicitamente il processo di calibrazione
  • Il processo di calibrazione non ha avuto esito positivo l'ultima volta che l'utente ha usato il dispositivo

Gli sviluppatori devono assicurarsi di fornire supporto adeguato per gli utenti in cui i dati di tracciamento oculare potrebbero non essere disponibili. Altre informazioni sulle considerazioni per le soluzioni di fallback sono disponibili in Tracciamento oculare su HoloLens 2.


Vedi anche