Unity의 확장된 시선 추적

확장된 시선 추적 샘플에 대한 GitHub 리포지토리에 액세스하려면 다음을 수행합니다.

확장된 시선 추적은 HoloLens 2 새로운 기능입니다. 이는 결합된 시선 응시 데이터만 제공하는 표준 시선 추적의 상위 집합입니다. 또한 확장된 시선 추적은 개별 시선 응시 데이터를 제공하며 애플리케이션이 응시 데이터에 대해 30, 60 및 90fps와 같은 다양한 프레임 속도를 설정할 수 있도록 합니다. 눈 뜨기 및 눈 버전과 같은 다른 기능은 현재 HoloLens 2 지원되지 않습니다.

확장된 시선 추적 SDK를 사용하면 애플리케이션이 확장된 시선 추적의 데이터 및 기능에 액세스할 수 있습니다. OpenXR API 또는 레거시 WinRT API와 함께 사용할 수 있습니다.

이 문서에서는 Mixed Reality OpenXR 플러그 인과 함께 Unity에서 확장된 시선 추적 SDK를 사용하는 방법을 설명합니다.

프로젝트 설정

  1. HoloLens 개발을 위한 Unity 프로젝트를 설정합니다.
    • 응시 입력 기능 선택
  2. MRTK 기능 도구에서 Mixed Reality OpenXR 플러그 인을 가져옵니다.
  3. 시선 추적 SDK NuGet 패키지를 Unity 프로젝트로 가져옵니다.
    1. NuGetForUnity 패키지를 다운로드하여 설치합니다.
    2. Unity 편집기에서 ->Manage NuGet PackagesNuGet이동한 다음, 를 검색합니다.Microsoft.MixedReality.EyeTracking
    3. 설치 단추를 클릭하여 최신 버전의 NuGet 패키지를 가져옵니다.
      시선 추적 SDK Nuget 패키지의 스크린샷
  4. Unity 도우미 스크립트를 추가합니다.
    1. 여기에서 스크립트를 ExtendedEyeGazeDataProvider.cs Unity 프로젝트에 추가합니다.
    2. 장면을 만든 다음, 스크립트를 ExtendedEyeGazeDataProvider.cs GameObject에 연결합니다.
  5. 의 함수를 ExtendedEyeGazeDataProvider.cs 사용하고 논리를 구현합니다.
  6. HoloLens를 빌드하고 배포합니다.

ExtendedEyeGazeDataProvider의 함수 사용

참고

스크립트는 ExtendedEyeGazeDataProvider 응시 데이터의 좌표를 변환하기 위해 Mixed Reality OpenXR 플러그 인의 일부 API에 따라 달라집니다. Unity 프로젝트에서 더 이상 사용되지 않는 Windows XR 플러그 인 또는 이전 Unity 버전의 레거시 기본 제공 XR을 사용하는 경우에는 작동하지 않습니다. 확장된 시선 추적이 이러한 시나리오에서도 작동하도록 하려면 다음을 수행합니다.

  • 프레임 속도 설정에 액세스하기만 하면 Mixed Reality OpenXR 플러그 인이 필요하지 않으며 프레임 속도 관련 논리만 유지하도록 을 수정 ExtendedEyeGazeDataProvider 할 수 있습니다.
  • 여전히 개별 시선 응시 데이터에 액세스해야 하는 경우 Unity에서 WinRT API를 사용해야 합니다. WinRT API와 함께 확장된 시선 추적 SDK를 사용하는 방법을 보려면 "참고" 섹션을 참조하세요.

클래스는 ExtendedEyeGazeDataProvider 확장된 시선 추적 SDK API를 래핑합니다. Unity 월드 공간에서 또는 기본 카메라를 기준으로 응시 읽기를 가져오는 기능을 제공합니다.

응시 데이터를 가져오는 데 사용할 ExtendedEyeGazeDataProvider 코드 샘플은 다음과 같습니다.

ExtendedEyeGazeDataProvider extendedEyeGazeDataProvider;
void Update() {
    timestamp = DateTime.Now;

    var leftGazeReadingInWorldSpace = extendedEyeGazeDataProvider.GetWorldSpaceGazeReading(extendedEyeGazeDataProvider.GazeType.Left, timestamp);
    var rightGazeReadingInWorldSpace = extendedEyeGazeDataProvider.GetWorldSpaceGazeReading(extendedEyeGazeDataProvider.GazeType.Right, timestamp);
    var combinedGazeReadingInWorldSpace = extendedEyeGazeDataProvider.GetWorldSpaceGazeReading(extendedEyeGazeDataProvider.GazeType.Combined, timestamp);

    var combinedGazeReadingInCameraSpace = extendedEyeGazeDataProvider.GetCameraSpaceGazeReading(extendedEyeGazeDataProvider.GazeType.Combined, timestamp);
}

스크립트가 ExtendedEyeGazeDataProvider 실행되면 응시 데이터 프레임 속도를 현재 90fps인 가장 높은 옵션으로 설정합니다.

확장된 시선 추적 SDK의 API 참조

스크립트를 ExtendedEyeGazeDataProvider 사용하는 것 외에도 직접 SDK API를 사용하는 사용자 고유의 스크립트를 만들 수도 있습니다.

namespace Microsoft.MixedReality.EyeTracking
{
    /// <summary>
    /// Allow discovery of Eye Gaze Trackers connected to the system
    /// This is the only class from the Extended Eye Tracking SDK that the application will instantiate, 
    /// other classes' instances will be returned by method calls or properties.
    /// </summary>
    public class EyeGazeTrackerWatcher
    {
        /// <summary>
        /// Constructs an instance of the watcher
        /// </summary>
        public EyeGazeTrackerWatcher();

        /// <summary>
        /// Starts trackers enumeration.
        /// </summary>
        /// <returns>Task representing async action; completes when the initial enumeration is completed</returns>
        public System.Threading.Tasks.Task StartAsync();

        /// <summary>
        /// Stop listening to trackers additions and removal
        /// </summary>
        public void Stop();

        /// <summary>
        /// Raised when an Eye Gaze tracker is connected
        /// </summary>
        public event System.EventHandler<EyeGazeTracker> EyeGazeTrackerAdded;

        /// <summary>
        /// Raised when an Eye Gaze tracker is disconnected
        /// </summary>
        public event System.EventHandler<EyeGazeTracker> EyeGazeTrackerRemoved;        
    }

    /// <summary>
    /// Represents an Eye Tracker device
    /// </summary>
    public class EyeGazeTracker
    {
        /// <summary>
        /// True if Restricted mode is supported, which means the driver supports providing individual 
        /// eye gaze vector and frame rate 
        /// </summary>
        public bool IsRestrictedModeSupported;

        /// <summary>
        /// True if Vergence Distance is supported by tracker
        /// </summary>
        public bool IsVergenceDistanceSupported;

        /// <summary>
        /// True if Eye Openness is supported by the driver
        /// </summary>
        public bool IsEyeOpennessSupported;

        /// <summary>
        /// True if individual gazes are supported
        /// </summary>
        public bool AreLeftAndRightGazesSupported;

        /// <summary>
        /// Get the supported target frame rates of the tracker
        /// </summary>
        public System.Collections.Generic.IReadOnlyList<EyeGazeTrackerFrameRate> SupportedTargetFrameRates;

        /// <summary>
        /// NodeId of the tracker, used to retrieve a SpatialLocator or SpatialGraphNode to locate the tracker in the scene
        /// for the Perception API, use SpatialGraphInteropPreview.CreateLocatorForNode
        /// for the Mixed Reality OpenXR API, use SpatialGraphNode.FromDynamicNodeId
        /// </summary>
        public Guid TrackerSpaceLocatorNodeId;

        /// <summary>
        /// Opens the tracker
        /// </summary>
        /// <param name="restrictedMode">True if restricted mode active</param>
        /// <returns>Task representing async action; completes when the initial enumeration is completed</returns>
        public System.Threading.Tasks.Task OpenAsync(bool restrictedMode);

        /// <summary>
        /// Closes the tracker
        /// </summary>
        public void Close();

        /// <summary>
        /// Changes the target frame rate of the tracker
        /// </summary>
        /// <param name="newFrameRate">Target frame rate</param>
        public void SetTargetFrameRate(EyeGazeTrackerFrameRate newFrameRate);

        /// <summary>
        /// Try to get tracker state at a given timestamp
        /// </summary>
        /// <param name="timestamp">timestamp</param>
        /// <returns>State if available, null otherwise</returns>
        public EyeGazeTrackerReading TryGetReadingAtTimestamp(DateTime timestamp);

        /// <summary>
        /// Try to get tracker state at a system relative time
        /// </summary>
        /// <param name="time">time</param>
        /// <returns>State if available, null otherwise</returns>
        public EyeGazeTrackerReading TryGetReadingAtSystemRelativeTime(TimeSpan time);

        /// <summary>
        /// Try to get first first tracker state after a given timestamp
        /// </summary>
        /// <param name="timestamp">timestamp</param>
        /// <returns>State if available, null otherwise</returns>
        public EyeGazeTrackerReading TryGetReadingAfterTimestamp(DateTime timestamp);

        /// <summary>
        /// Try to get the first tracker state after a system relative time
        /// </summary>
        /// <param name="time">time</param>
        /// <returns>State if available, null otherwise</returns>
        public EyeGazeTrackerReading TryGetReadingAfterSystemRelativeTime(TimeSpan time);
    }

    /// <summary>
    /// Represents a frame rate supported by an Eye Tracker
    /// </summary>
    public class EyeGazeTrackerFrameRate
    {
        /// <summary>
        /// Frames per second of the frame rate
        /// </summary>
        public UInt32 FramesPerSecond;
    }

    /// <summary>
    /// Snapshot of Gaze Tracker state
    /// </summary>
    public class EyeGazeTrackerReading
    {
        /// <summary>
        /// Timestamp of state
        /// </summary>
        public DateTime Timestamp;

        /// <summary>
        /// Timestamp of state as system relative time
        /// Its SystemRelativeTime.Ticks could provide the QPC time to locate tracker pose 
        /// </summary>
        public TimeSpan SystemRelativeTime;

        /// <summary>
        /// Indicates of user calibration is valid
        /// </summary>
        public bool IsCalibrationValid;

        /// <summary>
        /// Tries to get a vector representing the combined gaze related to the tracker's node
        /// </summary>
        /// <param name="origin">Origin of the gaze vector</param>
        /// <param name="direction">Direction of the gaze vector</param>
        /// <returns></returns>
        public bool TryGetCombinedEyeGazeInTrackerSpace(out System.Numerics.Vector3 origin, out System.Numerics.Vector3 direction);

        /// <summary>
        /// Tries to get a vector representing the left eye gaze related to the tracker's node
        /// </summary>
        /// <param name="origin">Origin of the gaze vector</param>
        /// <param name="direction">Direction of the gaze vector</param>
        /// <returns></returns>
        public bool TryGetLeftEyeGazeInTrackerSpace(out System.Numerics.Vector3 origin, out System.Numerics.Vector3 direction);

        /// <summary>
        /// Tries to get a vector representing the right eye gaze related to the tracker's node position
        /// </summary>
        /// <param name="origin">Origin of the gaze vector</param>
        /// <param name="direction">Direction of the gaze vector</param>
        /// <returns></returns>
        public bool TryGetRightEyeGazeInTrackerSpace(out System.Numerics.Vector3 origin, out System.Numerics.Vector3 direction);

        /// <summary>
        /// Tries to read vergence distance
        /// </summary>
        /// <param name="value">Vergence distance if available</param>
        /// <returns>bool if value is valid</returns>
        public bool TryGetVergenceDistance(out float value);

        /// <summary>
        /// Tries to get left Eye openness information
        /// </summary>
        /// <param name="value">Eye Openness if valid</param>
        /// <returns>bool if value is valid</returns>
        public bool TryGetLeftEyeOpenness(out float value);

        /// <summary>
        /// Tries to get right Eye openness information
        /// </summary>
        /// <param name="value">Eye openness if valid</param>
        /// <returns>bool if value is valid</returns>
        public bool TryGetRightEyeOpenness(out float value);
    }
}

추가 정보