Entrada de olhar para a cabeça e olhar no DirectX

Observação

Este artigo refere-se às APIs nativas herdadas do WinRT. Para novos projetos de aplicativo nativo, recomendamos usar a API OpenXR.

Em Windows Mixed Reality, a entrada do olhar e do olhar para a cabeça é usada para determinar o que o usuário está olhando. Você pode usar os dados para conduzir modelos de entrada primários, como foco de cabeçalho e confirmação, e fornecer contexto para diferentes tipos de interação. Há dois tipos de vetores de olhar disponíveis por meio da API: olhar para a cabeça e olhar para os olhos. Ambos são fornecidos como um raio tridimensional com uma origem e direção. Os aplicativos podem, em seguida, raycast em suas cenas, ou no mundo real, e determinar o que o usuário está direcionando.

O olhar de cabeçalho representa a direção na qual a cabeça do usuário está apontada. Pense no olhar de cabeçalho como a posição e a direção para a frente do próprio dispositivo, com a posição como o ponto central entre as duas exibições. O foco de cabeçalho está disponível em todos os dispositivos Realidade Misturada.

O olhar representa a direção que os olhos do usuário estão olhando. A origem está localizada entre os olhos do usuário. Ele está disponível em dispositivos Realidade Misturada que incluem um sistema de acompanhamento ocular.

Os raios de cabeça e de olhar podem ser acessados por meio da API SpatialPointerPose . Chame SpatialPointerPose::TryGetAtTimestamp para receber um novo objeto SpatialPointerPose no carimbo de data/hora especificado e no sistema de coordenadas. Este SpatialPointerPose contém uma origem e direção de foco de cabeça. Ele também contém uma origem e direção do olhar se o rastreamento ocular estiver disponível.

Suporte a dispositivos

Recurso HoloLens (1ª geração) HoloLens 2 Headsets imersivos
Foco com a cabeça ✔️ ✔️ ✔️
Olhar para os olhos ✔️

Usando o olhar para a cabeça

Para acessar o foco principal, comece chamando SpatialPointerPose::TryGetAtTimestamp para receber um novo objeto SpatialPointerPose. Passe os parâmetros a seguir.

  • Um SpatialCoordinateSystem que representa o sistema de coordenadas desejado para o foco de cabeçalho. Isso é representado pela variável coordinateSystem no código a seguir. Para obter mais informações, visite nosso guia de desenvolvedor de sistemas de coordenadas .
  • Um carimbo de data/ hora que representa a hora exata da pose de cabeça solicitada. Normalmente, você usará um carimbo de data/hora que corresponde à hora em que o quadro atual será exibido. Você pode obter esse carimbo de data/hora de exibição previsto de um objeto HolographicFramePrediction , que é acessível por meio do HolographicFrame atual. Esse objeto HolographicFramePrediction é representado pela variável de previsão no código a seguir.

Depois que você tiver um SpatialPointerPose válido, a posição da cabeça e a direção para a frente estarão acessíveis como propriedades. O código a seguir mostra como acessá-los.

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
}

Usando o olhar

Para que os usuários usem a entrada de olhar, cada usuário precisa passar por uma calibragem de usuário de acompanhamento ocular na primeira vez que usar o dispositivo. A API de olhar para os olhos é semelhante ao olhar para a cabeça. Ele usa a mesma API SpatialPointerPose , que fornece uma origem e direção de raios que você pode fazer raycast em sua cena. A única diferença é que você precisa habilitar explicitamente o rastreamento ocular antes de usá-lo:

  1. Solicite permissão do usuário para usar o acompanhamento ocular em seu aplicativo.
  2. Habilite a funcionalidade "Entrada de Foco" no manifesto do pacote.

Solicitando acesso à entrada de olhar

Quando seu aplicativo estiver iniciando, chame EyesPose::RequestAccessAsync para solicitar acesso ao rastreamento ocular. O sistema solicitará ao usuário, se necessário, e retornará GazeInputAccessStatus::Permitido depois que o acesso for concedido. Essa é uma chamada assíncrona, portanto, requer um pouco de gerenciamento extra. O exemplo a seguir gera um std::thread desanexado para aguardar o resultado, que ele armazena em uma variável de membro chamada 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 um thread desanexado é apenas uma opção para lidar com chamadas assíncronas. Você também pode usar a nova funcionalidade de co_await com suporte do C++/WinRT. Aqui está outro exemplo para solicitar a permissão do usuário:

  • EyesPose::IsSupported() permite que o aplicativo dispare a caixa de diálogo de permissão somente se houver um rastreador ocular.
  • M_gazeInputAccessStatus GazeInputAccessStatus; Isso é para evitar a exibição do prompt de permissão repetidamente.
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;	
		}
	});
}

Declarando a funcionalidade de Entrada de Foco

Clique duas vezes no arquivo appxmanifest em Gerenciador de Soluções. Em seguida, navegue até a seção Funcionalidades e verifique a funcionalidade de Entrada do Olhar .

Recurso de entrada de foco

Isso adiciona as seguintes linhas à seção Pacote no arquivo appxmanifest:

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

Obtendo o raio do olhar

Depois de receber acesso ao ET, você estará livre para pegar o raio do olhar a cada quadro. Assim como acontece com o foco na cabeça, obtenha o SpatialPointerPose chamando SpatialPointerPose::TryGetAtTimestamp com um carimbo de data/hora desejado e um sistema de coordenadas. O SpatialPointerPose contém um objeto EyesPose por meio da propriedade Eyes . Isso só será nulo se o acompanhamento ocular estiver habilitado. A partir daí, você pode verificar se o usuário no dispositivo tem uma calibragem de acompanhamento ocular chamando EyesPose::IsCalibrationValid. Em seguida, use a propriedade Gaze para obter o SpatialRay que contém a posição e a direção do olhar. Às vezes, a propriedade Gaze pode ser nula, portanto verifique isso. Isso pode acontecer se um usuário calibrado fechar temporariamente os olhos.

O código a seguir mostra como acessar o raio do olhar.

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 o acompanhamento ocular não está disponível

Conforme mencionado em nossos documentos de design de acompanhamento ocular, designers e desenvolvedores devem estar cientes de instâncias em que os dados de rastreamento ocular podem não estar disponíveis.

Há vários motivos para os dados estarem indisponíveis:

  • Um usuário que não está sendo calibrado
  • Um usuário negou ao aplicativo acesso aos dados de acompanhamento ocular
  • Interferências temporárias, como manchas no visor do HoloLens ou cabelo ocluindo os olhos do usuário.

Embora algumas das APIs já tenham sido mencionadas neste documento, no seguinte, fornecemos um resumo de como detectar que o rastreamento ocular está disponível como uma referência rápida:

Você também pode querer verificar se seus dados de acompanhamento ocular não estão obsoletos adicionando um tempo limite entre as atualizações de dados de acompanhamento ocular recebidas e, caso contrário, fallback para o foco principal, conforme discutido abaixo. Visite nossas considerações de design de fallback para obter mais informações.


Correlacionando o olhar com outras entradas

Às vezes, você pode achar que precisa de um SpatialPointerPose que corresponda a um evento no passado. Por exemplo, se o usuário fizer um Air Tap, talvez seu aplicativo queira saber o que ele estava olhando. Para essa finalidade, simplesmente usar SpatialPointerPose::TryGetAtTimestamp com o tempo de quadro previsto seria impreciso devido à latência entre o processamento de entrada do sistema e o tempo de exibição. Além disso, se estiver usando o olhar para direcionamento, nossos olhos tendem a seguir em frente mesmo antes de terminar uma ação de confirmação. Isso é menos um problema para um toque de ar simples, mas se torna mais crítico ao combinar comandos de voz longa com movimentos rápidos dos olhos. Uma maneira de lidar com esse cenário é fazer uma chamada adicional para SpatialPointerPose::TryGetAtTimestamp, usando um carimbo de data/hora histórico que corresponde ao evento de entrada.

No entanto, para a entrada que roteia pelo SpatialInteractionManager, há um método mais fácil. O SpatialInteractionSourceState tem sua própria função TryGetAtTimestamp . Chamar isso fornecerá um SpatialPointerPose perfeitamente correlacionado sem o guesswork. Para obter mais informações sobre como trabalhar com SpatialInteractionSourceStates, dê uma olhada nos Controladores de Mãos e Movimento na documentação do DirectX .


Calibragem

Para que o acompanhamento ocular funcione com precisão, cada usuário precisa passar por uma calibragem de usuário de acompanhamento ocular. Isso permite que o dispositivo ajuste o sistema para uma experiência de visualização mais confortável e de maior qualidade para o usuário e para garantir um acompanhamento visual preciso ao mesmo tempo. Os desenvolvedores não precisam fazer nada no final para gerenciar a calibragem do usuário. O sistema garantirá que o usuário seja solicitado a calibrar o dispositivo nas seguintes circunstâncias:

  • O usuário está usando o dispositivo pela primeira vez
  • O usuário rejeitou o processo de calibragem anteriormente
  • O processo de calibragem não teve sucesso na última vez que o usuário usou o dispositivo

Os desenvolvedores devem garantir que forneçam suporte adequado aos usuários em que os dados de acompanhamento ocular podem não estar disponíveis. Saiba mais sobre considerações sobre soluções de fallback no acompanhamento de olho no HoloLens 2.


Confira também