DirectX의 헤드 응시 및 시선 응시 입력

참고

이 문서는 레거시 WinRT 네이티브 API와 관련이 있습니다. 새 네이티브 앱 프로젝트의 경우 OpenXR API를 사용하는 것이 좋습니다.

Windows Mixed Reality 눈과 머리 응시 입력을 사용하여 사용자가 보고 있는 내용을 확인합니다. 데이터를 사용하여 헤드 응시 및 커밋과 같은 기본 입력 모델을 구동하고 다양한 상호 작용 형식에 대한 컨텍스트를 제공할 수 있습니다. API를 통해 사용할 수 있는 응시 벡터에는 헤드 응시 및 시선 응시의 두 가지 유형이 있습니다. 둘 다 원점과 방향을 가진 3차원 광선으로 제공됩니다. 그런 다음 애플리케이션은 장면 또는 실제 세계로 레이캐스트하고 사용자가 대상으로 하는 대상을 결정할 수 있습니다.

헤드 응시 는 사용자의 머리가 가리키는 방향을 나타냅니다. 헤드 응시를 디바이스 자체의 위치 및 앞으로 방향으로 생각하고 위치는 두 디스플레이 사이의 중심점으로 간주합니다. 헤드 응시는 모든 Mixed Reality 디바이스에서 사용할 수 있습니다.

시선 응시 는 사용자의 눈이 바라보는 방향을 나타냅니다. 원본은 사용자의 눈 사이에 있습니다. 시선 추적 시스템을 포함하는 Mixed Reality 장치에서 사용할 수 있습니다.

헤드 및 시선 응시 광선은 SpatialPointerPose API를 통해 모두 액세스할 수 있습니다. SpatialPointerPose::TryGetAtTimestamp를 호출하여 지정된 타임스탬프 및 좌표계에서 새 SpatialPointerPose 개체를 받습니다. 이 SpatialPointerPose에는 헤드 응시 원점과 방향이 포함되어 있습니다. 또한 시선 추적을 사용할 수 있는 경우 시선 응시 원점과 방향을 포함합니다.

디바이스 지원

기능 HoloLens(1세대) HoloLens 2 몰입형 헤드셋
헤드 게이즈(head-gaze) ✔️ ✔️ ✔️
시선 응시 ✔️

헤드 응시 사용

헤드 응시에 액세스하려면 먼저 SpatialPointerPose::TryGetAtTimestamp 를 호출하여 새 SpatialPointerPose 개체를 받습니다. 다음 매개 변수를 전달합니다.

  • 헤드 응시에 원하는 좌표계를 나타내는 SpatialCoordinateSystem 입니다. 다음 코드의 coordinateSystem 변수로 표시됩니다. 자세한 내용은 좌표계 개발자 가이드를 참조하세요.
  • 요청된 머리 포즈의 정확한 시간을 나타내는 타임스탬프 입니다. 일반적으로 현재 프레임이 표시되는 시간에 해당하는 타임스탬프를 사용합니다. 현재 HolographicFrame을 통해 액세스할 수 있는 HolographicFramePrediction 개체에서 이 예측 표시 타임스탬프를 가져올 수 있습니다. 이 HolographicFramePrediction 개체는 다음 코드의 예측 변수로 표시됩니다.

유효한 SpatialPointerPose가 있으면 머리 위치 및 앞으로 방향에 속성으로 액세스할 수 있습니다. 다음 코드는 액세스하는 방법을 보여 주는 코드입니다.

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
}

시선 응시 사용

사용자가 시선 응시 입력을 사용하려면 각 사용자가 장치를 처음 사용할 때 시선 추적 사용자 보정 을 거쳐야 합니다. 시선 응시 API는 헤드 응시와 비슷합니다. 장면에 대해 레이캐스트할 수 있는 광선 원점과 방향을 제공하는 동일한 SpatialPointerPose API를 사용합니다. 유일한 차이점은 아이 트래킹을 사용하기 전에 명시적으로 사용하도록 설정해야 한다는 것입니다.

  1. 앱에서 시선 추적을 사용할 수 있는 사용자 권한을 요청합니다.
  2. 패키지 매니페스트에서 "응시 입력" 기능을 사용하도록 설정합니다.

시선 응시 입력에 대한 액세스 요청

앱이 시작되면 EyesPose::RequestAccessAsync 를 호출하여 시선 추적에 대한 액세스를 요청합니다. 필요한 경우 시스템에서 사용자에게 메시지를 표시하고 액세스 권한이 부여되면 GazeInputAccessStatus::Allowed 를 반환합니다. 비동기 호출이므로 약간의 추가 관리가 필요합니다. 다음 예제에서는 분리된 std::thread를 스핀업하여 결과를 기다리며 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();

분리된 스레드를 시작하는 것은 비동기 호출을 처리하는 한 가지 옵션일 뿐입니다. C++/WinRT에서 지원하는 새로운 co_await 기능을 사용할 수도 있습니다. 사용자 권한을 요청하는 또 다른 예는 다음과 같습니다.

  • EyesPose::IsSupported()를 사용하면 아이 트래커가 있는 경우에만 애플리케이션에서 권한 대화 상자를 트리거할 수 있습니다.
  • GazeInputAccessStatus m_gazeInputAccessStatus; 이는 사용 권한 프롬프트가 반복해서 표시되지 않도록 하기 위한 것입니다.
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;	
		}
	});
}

응시 입력 기능 선언

솔루션 탐색기 appxmanifest 파일을 두 번 클릭합니다. 그런 다음, 기능 섹션으로 이동하여 응시 입력 기능을 검사.

응시 입력 기능

그러면 appxmanifest 파일의 패키지 섹션에 다음 줄이 추가됩니다.

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

시선 응시 광선 가져오기

ET에 대한 액세스 권한을 받으면 모든 프레임마다 시선 응시 광선을 자유롭게 잡을 수 있습니다. 헤드 응시와 마찬가지로 원하는 타임스탬프 및 좌표계를 사용하여 SpatialPointerPose::TryGetAtTimestamp를 호출하여 SpatialPointerPose를 가져옵니다. SpatialPointerPose에는 Eyes 속성을 통한 EyesPose 개체가 포함되어 있습니다. 이는 시선 추적을 사용하도록 설정한 경우에만 null이 아닌 것입니다. 여기에서 EyesPose::IsCalibrationValid를 호출하여 디바이스의 사용자에게 시선 추적 보정이 있는지 검사 수 있습니다. 다음으로 Gaze 속성을 사용하여 시선 응시 위치와 방향이 포함된 SpatialRay 를 가져옵니다. Gaze 속성은 때때로 null일 수 있으므로 이를 위해 검사 합니다. 보정된 사용자가 일시적으로 눈을 감으면 이 문제가 발생할 수 있습니다.

다음 코드는 시선 응시 광선에 액세스하는 방법을 보여줍니다.

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

시선 추적을 사용할 수 없는 경우 대체

시선 추적 디자인 문서에서 언급했듯이 디자이너와 개발자는 모두 시선 추적 데이터를 사용할 수 없는 경우를 알고 있어야 합니다.

데이터를 사용할 수 없는 데는 여러 가지 이유가 있습니다.

  • 보정되지 않는 사용자
  • 사용자가 자신의 시선 추적 데이터에 대한 앱 액세스를 거부했습니다.
  • HoloLens 바이저의 얼룩이나 사용자의 눈을 가두는 머리카락과 같은 일시적인 간섭.

이 문서에서는 일부 API가 이미 언급되었지만 다음에서는 빠른 참조로 시선 추적을 검색하는 방법에 대한 요약을 제공합니다.

또한 수신된 시선 추적 데이터 업데이트 사이에 시간 제한을 추가하고 아래에 설명된 대로 응시에 대체하여 시선 추적 데이터가 부실하지 않도록 검사 수 있습니다. 자세한 내용은 대체 디자인 고려 사항을 참조하세요.


응시를 다른 입력과 상호 연결

경우에 따라 과거의 이벤트에 해당하는 SpatialPointerPose 가 필요할 수 있습니다. 예를 들어 사용자가 에어 탭을 수행하는 경우 앱에서 보고 있는 내용을 알고 싶을 수 있습니다. 이를 위해 시스템 입력 처리와 표시 시간 사이의 대기 시간으로 인해 예측된 프레임 시간과 함께 SpatialPointerPose::TryGetAtTimestamp 를 사용하기만 하면 정확하지 않습니다. 또한 대상 지정을 위해 시선 응시를 사용하는 경우 커밋 작업을 완료하기 전에도 눈이 움직이는 경향이 있습니다. 이것은 간단한 에어 탭에 대한 문제의 적은, 하지만 빠른 눈 움직임과 긴 음성 명령을 결합 할 때 더 중요해진다. 이 시나리오를 처리하는 한 가지 방법은 입력 이벤트에 해당하는 기록 타임스탬프를 사용하여 SpatialPointerPose::TryGetAtTimestamp를 추가로 호출하는 것입니다.

그러나 SpatialInteractionManager를 통해 라우팅되는 입력의 경우 더 쉬운 메서드가 있습니다. SpatialInteractionSourceState에는 자체 TryGetAtTimestamp 함수가 있습니다. 추측 없이 완벽하게 상호 관련된 SpatialPointerPose 를 제공하는 호출입니다. SpatialInteractionSourceStates 작업에 대한 자세한 내용은 DirectX 설명서의 손 및 동작 컨트롤러를 참조하세요.


보정

시선 추적이 정확하게 작동하려면 각 사용자가 시선 추적 사용자 보정을 거쳐야 합니다. 이를 통해 디바이스는 사용자에게 더 편안하고 더 높은 품질의 보기 환경을 위해 시스템을 조정하고 동시에 정확한 시선 추적을 보장할 수 있습니다. 개발자는 사용자 보정을 관리하기 위해 아무 작업도 수행할 필요가 없습니다. 시스템은 다음과 같은 상황에서 사용자에게 디바이스를 보정하라는 메시지가 표시되도록 합니다.

  • 사용자가 처음으로 장치를 사용하는 중
  • 사용자가 이전에 보정 프로세스에서 옵트아웃함
  • 사용자가 마지막으로 장치를 사용했을 때 보정 프로세스에 실패함

개발자는 시선 추적 데이터를 사용할 수 없는 사용자에게 적절한 지원을 제공해야 합니다. HoloLens 2 시선 추적에서 대체 솔루션에 대한 고려 사항에 대해 자세히 알아보세요.


추가 정보