Tracciamento oculare esteso in Unity

Per accedere al repository GitHub per l'esempio di tracciamento oculare esteso:

Il tracciamento oculare esteso è una nuova funzionalità in HoloLens 2. Si tratta di un superset del tracciamento oculare standard, che fornisce solo dati combinati dello sguardo fisso. Il tracciamento oculare esteso fornisce anche dati singoli dello sguardo fisso e consente alle applicazioni di impostare frequenze di fotogrammi diverse per i dati dello sguardo fisso, ad esempio 30, 60 e 90fps. Altre caratteristiche, ad esempio l'apertura degli occhi e la vergenza oculare, non sono supportate da HoloLens 2 in questo momento.

Extended Eye Tracking SDK consente alle applicazioni di accedere a dati e funzionalità di tracciamento oculare esteso. Può essere usato insieme alle API OpenXR o alle API WinRT legacy.

Questo articolo illustra i modi per usare l'SDK di tracciamento oculare esteso in Unity insieme al plug-in OpenXR Realtà mista.

Configurazione del progetto

  1. Configurare il progetto Unity per lo sviluppo di HoloLens.
    • Selezionare la funzionalità Di input sguardo fisso
  2. Importare il plug-in OpenXR Realtà mista dallo strumento di funzionalità MRTK.
  3. Importare il pacchetto NuGet Eye Tracking SDK nel progetto Unity.
    1. Scaricare e installare il pacchetto NuGetForUnity .
    2. Nell'editor di Unity passare a NuGete>Manage NuGet Packages quindi cercare Microsoft.MixedReality.EyeTracking
    3. Fare clic sul pulsante Installa per importare la versione più recente del pacchetto NuGet.
      Screenshot del pacchetto NuGet Eye Tracking SDK.
  4. Aggiungere gli script helper di Unity.
    1. Aggiungere lo ExtendedEyeGazeDataProvider.cs script da qui al progetto Unity.
    2. Creare una scena, quindi allegare lo ExtendedEyeGazeDataProvider.cs script a qualsiasi GameObject.
  5. Usare le funzioni di ExtendedEyeGazeDataProvider.cs e implementare le logiche.
  6. Compilare e distribuire in HoloLens.

Utilizzare le funzioni di ExtendedEyeGazeDataProvider

Nota

Lo ExtendedEyeGazeDataProvider script dipende da alcune API dal plug-in OpenXR Realtà mista per convertire le coordinate dei dati dello sguardo fisso. Non può funzionare se il progetto Unity usa il plug-in XR di Windows deprecato o la versione XR predefinita legacy nella versione precedente di Unity. Per fare in modo che il tracciamento oculare esteso funzioni anche in questi scenari:

  • Se è sufficiente accedere alle impostazioni di frequenza dei fotogrammi, il plug-in OpenXR Realtà mista non è necessario e si potrebbe modificare in ExtendedEyeGazeDataProvider modo da mantenere solo la logica correlata alla frequenza dei fotogrammi.
  • Se è ancora necessario accedere ai singoli dati dello sguardo fisso, è necessario usare le API WinRT in Unity. Per informazioni su come usare l'SDK di tracciamento oculare esteso con le API WinRT, vedere la sezione "Vedi anche".

La ExtendedEyeGazeDataProvider classe esegue il wrapping delle API sdk di tracciamento oculare esteso. Fornisce funzioni per ottenere la lettura dello sguardo fisso nello spazio globale unity o rispetto alla fotocamera principale.

Di seguito sono riportati esempi di codice da utilizzare ExtendedEyeGazeDataProvider per ottenere i dati dello sguardo fisso.

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

Quando lo ExtendedEyeGazeDataProvider script viene eseguito, imposta la frequenza dei frame di dati dello sguardo fisso sull'opzione più alta, che è attualmente di 90fps.

Informazioni di riferimento sulle API di Extended Eye Tracking SDK

Oltre a usare lo ExtendedEyeGazeDataProvider script, è anche possibile creare uno script personalizzato per usare direttamente le API SDK.

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

Vedi anche