Entrada de mirada con cabeza y mirada en DirectX

Nota

Este artículo se relaciona con las API nativas heredadas de WinRT. En el caso de los nuevos proyectos de aplicaciones nativas, se recomienda usar la API de OpenXR.

En Windows Mixed Reality, la entrada de mirada con los ojos y la cabeza se usa para determinar lo que el usuario está mirando. Puede usar los datos para controlar los modelos de entrada principales, como la mirada con la cabeza y la confirmación, y proporcionar contexto para diferentes tipos de interacción. Hay dos tipos de vectores de mirada disponibles a través de la API: la mirada con la cabeza y la mirada con los ojos. Ambos se proporcionan como un rayo tridimensional con un origen y una dirección. A continuación, las aplicaciones pueden raycast en sus escenas, o en el mundo real, y determinar cuál es el destino del usuario.

La mirada con la cabeza representa la dirección en la que apunta la cabeza del usuario. Piense en la mirada con la cabeza como la posición y la dirección hacia delante del propio dispositivo, con la posición como punto central entre las dos pantallas. La mirada con la cabeza está disponible en todos los dispositivos Mixed Reality.

La mirada ocular representa la dirección hacia la que están mirando los ojos del usuario. El origen se encuentra entre los ojos del usuario. Está disponible en Mixed Reality dispositivos que incluyen un sistema de seguimiento ocular.

Tanto los rayos de cabeza como los de mirada ocular son accesibles a través de spatialPointerPose API. Llame a SpatialPointerPose::TryGetAtTimestamp para recibir un nuevo objeto SpatialPointerPose en la marca de tiempo y el sistema de coordenadas especificados. Este SpatialPointerPose contiene un origen y una dirección de mirada con la cabeza. También contiene un origen de mirada ocular y una dirección si el seguimiento ocular está disponible.

Compatibilidad con dispositivos

Característica HoloLens (1.ª generación) HoloLens 2 Cascos envolventes
Mirada con la cabeza ✔️ ✔️ ✔️
Mirada con los ojos ✔️

Uso de la mirada con la cabeza

Para acceder a la mirada con la cabeza, comience llamando a SpatialPointerPose::TryGetAtTimestamp para recibir un nuevo objeto SpatialPointerPose. Pase los parámetros siguientes.

  • SpatialCoordinateSystem que representa el sistema de coordenadas que desea para la mirada con la cabeza. Esto se representa mediante la variable coordinateSystem en el código siguiente. Para obtener más información, visite nuestra guía para desarrolladores de sistemas de coordenadas .
  • Marca de tiempo que representa la hora exacta de la posición de cabeza solicitada. Normalmente, usará una marca de tiempo que corresponde a la hora en que se mostrará el fotograma actual. Puede obtener esta marca de tiempo de visualización prevista desde un objeto HolographicFramePrediction , al que se puede acceder a través del holographicFrame actual. Este objeto HolographicFramePrediction se representa mediante la variable de predicción en el código siguiente.

Una vez que tenga un SpatialPointerPose válido, la posición de la cabeza y la dirección hacia delante son accesibles como propiedades. En el código siguiente se muestra cómo acceder a ellos.

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 de la mirada ocular

Para que los usuarios usen la entrada de mirada ocular, cada usuario tiene que pasar por una calibración del usuario de seguimiento ocular la primera vez que use el dispositivo. La API de mirada ocular es similar a la mirada con la cabeza. Usa la misma API SpatialPointerPose , que proporciona un origen de rayos y una dirección que puede raycast en la escena. La única diferencia es que debe habilitar explícitamente el seguimiento ocular antes de usarlo:

  1. Solicitar permiso de usuario para usar el seguimiento ocular en la aplicación.
  2. Habilite la funcionalidad "Entrada de mirada" en el manifiesto del paquete.

Solicitud de acceso a la entrada de mirada ocular

Cuando se inicie la aplicación, llame a EyesPose::RequestAccessAsync para solicitar acceso al seguimiento ocular. El sistema solicitará al usuario si es necesario y devolverá GazeInputAccessStatus::Allowed una vez concedido el acceso. Se trata de una llamada asincrónica, por lo que requiere un poco de administración adicional. En el ejemplo siguiente se pone en marcha un std::thread desasociado para esperar el resultado, que almacena en una variable miembro denominada 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();

Iniciar un subproceso desasociado es solo una opción para controlar las llamadas asincrónicas. También puede usar la nueva funcionalidad de co_await compatible con C++/WinRT. Este es otro ejemplo para solicitar permiso de usuario:

  • EyesPose::IsSupported() permite que la aplicación desencadene el cuadro de diálogo de permisos solo si hay un seguimiento ocular.
  • GazeInputAccessStatus m_gazeInputAccessStatus; Esto es para evitar que aparezca la solicitud de permisos de nuevo y otra vez.
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;	
		}
	});
}

Declaración de la funcionalidad De entrada de mirada

Haga doble clic en el archivo appxmanifest en Explorador de soluciones. A continuación, vaya a la sección Funcionalidades y compruebe la funcionalidad Entrada de mirada .

Funcionalidad de entrada de mirada

Esto agrega las siguientes líneas a la sección Paquete del archivo appxmanifest:

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

Obtención del rayo de mirada

Una vez que haya recibido acceso a ET, es libre de agarrar el rayo de mirada con los ojos cada fotograma. Al igual que con la mirada en la cabeza, obtenga SpatialPointerPose llamando a SpatialPointerPose::TryGetAtTimestamp con una marca de tiempo y un sistema de coordenadas deseados. SpatialPointerPose contiene un objeto EyesPose a través de la propiedad Eyes . Solo es null si el seguimiento ocular está habilitado. Desde allí, puede comprobar si el usuario del dispositivo tiene una calibración de seguimiento ocular llamando a EyesPose::IsCalibrationValid. A continuación, use la propiedad Gaze para obtener el SpatialRay que contiene la posición y la dirección de la mirada ocular. La propiedad Gaze a veces puede ser null, por lo que asegúrese de comprobarlo. Esto puede ocurrir si un usuario calibrado cierra temporalmente sus ojos.

En el código siguiente se muestra cómo acceder al rayo de mirada ocular.

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

Reserva cuando el seguimiento ocular no está disponible

Como se mencionó en nuestros documentos de diseño de seguimiento ocular, tanto los diseñadores como los desarrolladores deben tener en cuenta las instancias en las que es posible que los datos de seguimiento ocular no estén disponibles.

Hay varias razones por las que los datos no están disponibles:

  • Un usuario que no está calibrado
  • Un usuario ha denegado el acceso de la aplicación a sus datos de seguimiento ocular
  • Interferencias temporales, como manchas en el visor de HoloLens o el cabello, que excluyen los ojos del usuario.

Aunque algunas de las API ya se han mencionado en este documento, en lo siguiente se proporciona un resumen de cómo detectar que el seguimiento ocular está disponible como referencia rápida:

También puede comprobar que los datos de seguimiento ocular no están obsoletos agregando un tiempo de espera entre las actualizaciones de datos de seguimiento ocular recibidos y, de lo contrario, revertir a la mirada con la cabeza, como se describe a continuación. Visite nuestras consideraciones de diseño de reserva para obtener más información.


Correlación de la mirada con otras entradas

A veces, es posible que encuentre que necesita un SpatialPointerPose que se corresponda con un evento en el pasado. Por ejemplo, si el usuario realiza una pulsación de aire, es posible que la aplicación quiera saber lo que estaba mirando. Para este propósito, simplemente usar SpatialPointerPose::TryGetAtTimestamp con el tiempo de fotograma previsto sería inexacto debido a la latencia entre el procesamiento de entrada del sistema y el tiempo de visualización. Además, si usa la mirada ocular para el objetivo, nuestros ojos tienden a pasar incluso antes de finalizar una acción de confirmación. Esto es menos un problema para una simple pulsación de aire, pero se vuelve más crítico al combinar comandos de voz largas con movimientos oculares rápidos. Una manera de controlar este escenario es realizar una llamada adicional a SpatialPointerPose::TryGetAtTimestamp mediante una marca de tiempo histórica que corresponde al evento de entrada.

Sin embargo, para la entrada que enruta a través de SpatialInteractionManager, hay un método más sencillo. SpatialInteractionSourceState tiene su propia función TryGetAtTimestamp. Llamar a que proporcionará un SpatialPointerPose perfectamente correlacionado sin las conjeturas. Para obtener más información sobre cómo trabajar con SpatialInteractionSourceStates, eche un vistazo a la documentación de manos y controladores de movimiento en DirectX .


Calibración

Para que el seguimiento ocular funcione con precisión, cada usuario debe pasar por una calibración de usuario de seguimiento ocular. Esto permite al dispositivo ajustar el sistema para una experiencia de visualización más cómoda y de mayor calidad para el usuario y garantizar un seguimiento ocular preciso al mismo tiempo. Los desarrolladores no necesitan hacer nada en su extremo para administrar la calibración del usuario. El sistema garantizará que se pida al usuario que calibra el dispositivo en las siguientes circunstancias:

  • El usuario está usando el dispositivo por primera vez.
  • El usuario ha optado previamente por no completar el proceso de calibración.
  • El proceso de calibración no se realizó correctamente la última vez que el usuario usó el dispositivo.

Los desarrolladores deben asegurarse de proporcionar soporte adecuado a los usuarios en los que es posible que los datos de seguimiento ocular no estén disponibles. Obtenga más información sobre las consideraciones para las soluciones de reserva en Seguimiento ocular en HoloLens 2.


Consulte también