Cabeça-olhar e olho-olhar entrada no DirectX

Observação

Este artigo está relacionado às APIs nativas do WinRT herdadas. Para novos projetos de aplicativos nativos, é recomendável usar a API OpenXR.

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

Head-olhar representa a direção na qual a cabeça do usuário é apontada. Imagine olhar como a posição e direção de encaminhamento do próprio dispositivo, com a posição como o ponto central entre as duas exibições. O Head-olhar está disponível em todos os dispositivos de realidade misturada.

Olho-olhar representa a direção que os olhos do usuário estão procurando. A origem está localizada entre os olhos do usuário. Ele está disponível em dispositivos com realidade misturada que incluem um sistema de controle de olho.

Os raios de cabeça e olho-olhar podem ser acessados por meio da API do SpatialPointerPose . Chame SpatialPointerPose:: TryGetAtTimestamp para receber um novo objeto SpatialPointerPose no sistemade carimbo de data e hora especificado. Este SpatialPointerPose contém uma origem e direção olhar. Ele também contém uma origem e direção olhar, se o acompanhamento dos olhos estiver disponível.

Suporte a dispositivos

Recurso HoloLens (1ª geração) HoloLens 2 Headsets imersivos
Foco com a cabeça ✔️ ✔️ ✔️
Olho-olhar ✔️

Usando Head-olhar

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

  • Um SpatialCoordinateSystem que representa o sistema de coordenadas que você deseja para o olhar. Isso é representado pela variável coordinateSystem no código a seguir. Para obter mais informações, visite nosso guia do desenvolvedor de sistemas de coordenadas .
  • Um carimbo de data/ hora que representa o tempo exato necessário para a pose de cabeçalho. Normalmente, você usará um carimbo de data/hora que corresponde ao horário 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 HolographicFrameatual. Esse objeto HolographicFramePrediction é representado pela variável de previsão no código a seguir.

Quando você tiver um SpatialPointerPose válido, a posição de cabeçalho e a direção de encaminhamento serã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 os olhos-olhar

Para que os usuários usem a entrada olhar, cada usuário precisa passar por uma calibragem de usuário com acompanhamento de olho na primeira vez que usar o dispositivo. A API de olho-olhar é semelhante ao olhar. Ele usa a mesma API SpatialPointerPose , que fornece uma Ray Origin e uma direção que você pode Raycast em sua cena. A única diferença é que você precisa habilitar explicitamente o controle de olho antes de usá-lo:

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

Solicitando acesso à entrada de olhar de olho

Quando seu aplicativo estiver sendo inicializado, chame EyesPose:: RequestAccessAsync para solicitar acesso ao controle ocular. O sistema solicitará o usuário se necessário e retornará GazeInputAccessStatus:: allowed depois que o acesso tiver sido concedido. Essa é uma chamada assíncrona, portanto, requer um pouco de gerenciamento extra. O exemplo a seguir gira 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 co_await com suporte do C++/WinRT. Aqui está outro exemplo para solicitar permissão de usuário:

  • EyesPose:: IsSupported () permite que o aplicativo dispare a caixa de diálogo de permissão somente se houver um rastreador ocular.
  • GazeInputAccessStatus m_gazeInputAccessStatus; Isso é para evitar o pop-up 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 o recurso de entrada olhar

Clique duas vezes no arquivo appxmanifest em Gerenciador de soluções. Em seguida, navegue até a seção de recursos e verifique o recurso de entrada olhar .

Funcionalidade de entrada do olhar

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

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

Obtendo o olhar Ray

Depois de ter recebido o acesso ao ET, você fica livre para pegar o olhar Ray a cada quadro. Como com o Head-olhar, obtenha o SpatialPointerPose chamando SpatialPointerPose:: TryGetAtTimestamp com um carimbo de data/hora desejado e sistema de coordenadas. O SpatialPointerPose contém um objeto EyesPose por meio da propriedade Eyes . Isso será não nulo somente se o controle de olho estiver habilitado. A partir daí, você pode verificar se o usuário no dispositivo tem uma calibragem de acompanhamento de olho chamando EyesPose:: IsCalibrationValid. Em seguida, use a propriedade olhar para obter o SpatialRay que contém a posição e a direção do olhar de olho. Às vezes, a propriedade olhar pode ser nula, portanto certifique-se de verificar isso. Isso pode acontecer se um usuário calibrado fechar temporariamente seus olhos.

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

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 controle de olho não está disponível

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

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

  • Um usuário não está sendo calibrado
  • Um usuário negou o acesso do aplicativo aos seus dados de controle de olho
  • as interferências temporárias, como manchas no HoloLens visor ou cabelo, occludingm os olhos do usuário.

Embora algumas das APIs já tenham sido mencionadas neste documento, a seguir, fornecemos um resumo de como detectar que o acompanhamento de olho está disponível como uma referência rápida:

Você também pode querer verificar se os dados de controle de olho não estão obsoletos adicionando um tempo limite entre atualizações de dados de acompanhamento de olho recebido e, de outra forma, fallback para o Head-olhar, conforme discutido abaixo.
Visite nossas considerações de design de fallback para obter mais informações.


Correlacionando 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 toque de ar, seu aplicativo poderá querer saber o que ele estava observando. Para essa finalidade, simplesmente usar SpatialPointerPose:: TryGetAtTimestamp com o tempo de quadro previsto não seria preciso devido à latência entre o processamento de entrada do sistema e o tempo de exibição. Além disso, se estiver usando olhar de olho para direcionamento, nossos olhos tendem a se mover até mesmo antes de concluir uma ação de confirmação. Isso é menos um problema para um toque simples de ar, mas se torna mais crítico ao combinar comandos de voz longos com movimentos de olho rápido. 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 entrada que roteiam o 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 a suposição. Para obter mais informações sobre como trabalhar com SpatialInteractionSourceStates, confira a documentação mãos e controladores de 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 exibição mais confortável e de maior qualidade para o usuário e para garantir o acompanhamento ocular preciso ao mesmo tempo. Os desenvolvedores não precisam fazer nada em suas extremidades 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 fornecer suporte adequado para usuários em que os dados de acompanhamento ocular podem não estar disponíveis. Saiba mais sobre as considerações para soluções de fallback em Acompanhamento ocular no HoloLens 2.


Confira também