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

Nota

Este artigo está relacionado com as APIs nativas do WinRT legadas. Para novos projetos de aplicações nativas, recomendamos a utilização da API OpenXR.

No Windows Mixed Reality, a entrada de olhar e de olhar para a cabeça é utilizada para determinar o que o utilizador está a ver. Pode utilizar os dados para impulsionar modelos de entrada principais, como olhar de cabeça e consolidação, e fornecer contexto para diferentes tipos de interação. Existem dois tipos de vetores de olhares disponíveis através 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. Em seguida, as aplicações podem fazer raycast nas suas cenas ou no mundo real e determinar o que o utilizador está a filtrar.

O olhar para a cabeça representa a direção em que a cabeça do utilizador é apontada. Pense no olhar da cabeça como a posição e a direção para a frente do próprio dispositivo, com a posição como o ponto central entre os dois ecrãs. O olhar atento está disponível em todos os dispositivos Mixed Reality.

Olhar para o olhar representa a direção para a qual os olhos do utilizador estão a olhar. A origem está localizada entre os olhos do utilizador. Está disponível em dispositivos Mixed Reality que incluem um sistema de controlo ocular.

Os raios de cabeça e olhar para os olhos são acessíveis através da API SpatialPointerPose . Chame SpatialPointerPose::TryGetAtTimestamp para receber um novo objeto SpatialPointerPose no carimbo de data/hora especificado e coordene o sistema. Este SpatialPointerPose contém uma origem e direção de olhar para a cabeça. Também contém uma origem e direção de olhar para os olhos se o controlo ocular estiver disponível.

Suporte de dispositivos

Funcionalidade HoloLens (1.ª geração) HoloLens 2 Headsets envolventes
Olhar para a cabeça ✔️ ✔️ ✔️
Olhar para os olhos ✔️

Utilizar o olhar para a cabeça

Para aceder ao olhar atento, comece por chamar SpatialPointerPose::TryGetAtTimestamp para receber um novo objeto SpatialPointerPose. Transmita os seguintes parâmetros.

  • Um SpatialCoordinateSystem que representa o sistema de coordenadas que pretende para o olhar atento. Isto é representado pela variável coordinateSystem no seguinte código. Para obter mais informações, visite o nosso guia para programadores de sistemas de coordenadas .
  • Um carimbo de data/hora que representa a hora exata da pose da cabeça pedida. Normalmente, irá utilizar um carimbo de data/hora que corresponde à hora em que o fotograma atual será apresentado. Pode obter este carimbo de data/hora de visualização previsto a partir de um objeto HolographicFramePrediction , que é acessível através do HolographicFrame atual. Este objeto HolographicFramePrediction é representado pela variável de predição no seguinte código.

Assim que tiver um SpatialPointerPose válido, a posição da cabeça e a direção para a frente são acessíveis como propriedades. O código seguinte mostra como aceder aos mesmos.

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
}

Utilizar o olhar atento

Para que os seus utilizadores utilizem a entrada de olhar para o olhar, cada utilizador tem de passar por uma calibragem de utilizador de controlo ocular na primeira vez que utilizar o dispositivo. A API de olhar para os olhos é semelhante ao olhar para a cabeça. Utiliza a mesma API SpatialPointerPose , que fornece uma origem de raios e direção que pode fazer raycast contra a sua cena. A única diferença é que tem de ativar explicitamente o controlo ocular antes de o utilizar:

  1. Peça permissão ao utilizador para utilizar o controlo ocular na sua aplicação.
  2. Ative a capacidade "Gaze Input" no manifesto do pacote.

Pedir acesso à entrada de olhar atento

Quando a aplicação estiver a ser iniciada, chame EyesPose::RequestAccessAsync para pedir acesso ao controlo ocular. O sistema pedirá ao utilizador, se necessário, e devolverá GazeInputAccessStatus::Allowed assim que o acesso for concedido. Esta é uma chamada assíncrona, pelo que requer um pouco de gestão extra. O exemplo seguinte cria um std::thread desanexado para aguardar pelo resultado, que armazena numa variável de membro 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 um thread desanexado é apenas uma opção para processar chamadas assíncronas. Também pode utilizar a nova funcionalidade de co_await suportada por C++/WinRT. Eis outro exemplo para pedir permissão de utilizador:

  • EyesPose::IsSupported() permite que a aplicação acione a caixa de diálogo de permissão apenas se existir um controlador ocular.
  • GazeInputAccessStatus m_gazeInputAccessStatus; Isto é para evitar que seja apresentado o pedido 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;	
		}
	});
}

Declarar a capacidade Gaze Input

Faça duplo clique no ficheiro appxmanifest no Explorador de Soluções. Em seguida, navegue para a secção Capacidades e verifique a capacidade De Entrada de Olhar .

Capacidade de entrada de olhar para o olhar

Esta ação adiciona as seguintes linhas à secção Pacote no ficheiro appxmanifest:

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

Obter o raio de olhar para os olhos

Depois de ter recebido acesso ao ET, pode agarrar o raio de olhar para cada moldura. Tal como acontece com o olhar atento, obtenha o SpatialPointerPose ao chamar SpatialPointerPose::TryGetAtTimestamp com um carimbo de data/hora pretendido e coordene o sistema. O SpatialPointerPose contém um objeto EyesPose através da propriedade Eyes . Isto não é nulo apenas se o controlo ocular estiver ativado. A partir daí, pode verificar se o utilizador no dispositivo tem uma calibragem de controlo ocular ao chamar EyesPose::IsCalibrationValid. Em seguida, utilize a propriedade Gaze para obter o SpatialRay que contém a posição e a direção do olhar. Por vezes, a propriedade Gaze pode ser nula, por isso certifique-se de que verifica esta situação. Isto pode acontecer se um utilizador calibrado fechar temporariamente os olhos.

O código seguinte mostra como aceder ao raio de olhar para os olhos.

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

Contingência quando o controlo ocular não está disponível

Conforme mencionado nos nossos documentos de design de controlo ocular, tanto os designers como os programadores devem estar cientes das instâncias em que os dados de controlo ocular podem não estar disponíveis.

Existem várias razões para os dados estarem indisponíveis:

  • Um utilizador não está a ser calibrado
  • Um utilizador negou o acesso da aplicação aos dados de controlo ocular
  • Interferências temporárias, como manchas no visor do HoloLens ou cabelos que ocluem os olhos do utilizador.

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

Também poderá querer verificar se os dados de controlo ocular não estão obsoletos ao adicionar um tempo limite entre as atualizações de dados de controlo ocular recebidas e, de outra forma, a contingência para o olhar atento, conforme abordado abaixo. Veja as nossas considerações de design de contingência para obter mais informações.


Correlacionar o olhar com outras entradas

Por vezes, poderá descobrir que precisa de um SpatialPointerPose que corresponda a um evento no passado. Por exemplo, se o utilizador fizer um Air Tap, a sua aplicação poderá querer saber o que estava a ver. Para esta finalidade, utilizar simplesmente SpatialPointerPose::TryGetAtTimestamp com o período previsto seria impreciso devido à latência entre o processamento de entrada do sistema e o tempo de apresentação. Além disso, se utilizarmos o olhar atento para a segmentação, os nossos olhos tendem a seguir em frente mesmo antes de terminar uma ação de consolidação. Isto é menos um problema para um Simples Air Tap, mas torna-se mais crítico ao combinar comandos de voz longa com movimentos oculares rápidos. Uma forma de lidar com este cenário é efetuar uma chamada adicional para SpatialPointerPose::TryGetAtTimestamp, utilizando um carimbo de data/hora histórico que corresponde ao evento de entrada.

No entanto, para entradas que encaminham através do SpatialInteractionManager, existe um método mais fácil. O SpatialInteractionSourceState tem a sua própria função TryGetAtTimestamp . Chamadas que fornecerão um SpatialPointerPose perfeitamente correlacionado sem a estimativa. Para obter mais informações sobre como trabalhar com SpatialInteractionSourceStates, veja a documentação Mãos e Controladores de Movimento no DirectX .


Calibragem

Para que o controlo ocular funcione com precisão, cada utilizador tem de passar por uma calibragem de utilizador de controlo ocular. Isto permite que o dispositivo ajuste o sistema para uma experiência de visualização mais confortável e de maior qualidade para o utilizador e para garantir um controlo ocular preciso ao mesmo tempo. Os programadores não precisam de fazer nada do seu lado para gerir a calibragem de utilizadores. O sistema irá garantir que o utilizador é solicitado a calibrar o dispositivo nas seguintes circunstâncias:

  • O utilizador está a utilizar o dispositivo pela primeira vez
  • O utilizador optou anteriormente por não participar no processo de calibragem
  • O processo de calibragem não foi bem-sucedido da última vez que o utilizador utilizou o dispositivo

Os programadores devem certificar-se de que fornecem suporte adequado aos utilizadores onde os dados de controlo ocular podem não estar disponíveis. Saiba mais sobre as considerações para soluções de contingência no Controlo de olhos no HoloLens 2.


Ver também