DirectX でのヘッドと視線の入力Head-gaze and eye-gaze input in DirectX

Windows Mixed Reality では、ユーザーがどのようなことを確認しているかを判断するために、目と下の見つめ入力が使用されます。In Windows Mixed Reality, eye and head gaze input is used to determine what the user is looking at. これを使用すると、ヘッドを見つめたりコミットしたりする主な入力モデルを利用したり、相互作用の種類のコンテキストを提供したりすることもできます。This can be used to drive primary input models such as head-gaze and commit, and also to provide context for types of interactions. API を介して使用できる2種類の宝石ベクトルがあります。ヘッド宝石と視線There are two types of gaze vectors available through the API: head-gaze and eye-gaze. どちらも、原点と方向を持つ3次元射線として提供されます。Both are provided as a three dimensional ray with an origin and direction. その後、アプリケーションは、それらのシーンや現実の世界に raycast し、ユーザーの対象を特定できます。Applications can then raycast into their scenes, or the real world, and determine what the user is targeting.

頭を見つめているのは、ユーザーの頭がポイントされている方向を示しています。Head-gaze represents the direction that the user's head is pointed in. これは、デバイス自体の位置と方向であり、2つのディスプレイの間の中心点を表す位置であると考えてください。Think of this as the position and forward direction of the device itself, with the position representing the center point between the two displays. ヘッド宝石は、すべての Mixed Reality デバイスで使用できます。Head-gaze is available on all Mixed Reality devices.

視線は、ユーザーの目が見ている方向を表します。Eye-gaze represents the direction that the user's eyes are looking towards. 原点はユーザーの目の間にあります。The origin is located between the user's eyes. これは、視線追跡システムを含む Mixed Reality デバイスで使用できます。It is available on Mixed Reality devices that include an eye tracking system.

ヘッドと視線の両方には、 SpatialPointerPose API を使用してアクセスできます。Both head and eye-gaze rays are accessible through the SpatialPointerPose API. SpatialPointerPose:: TryGetAtTimestampを呼び出して、指定されたタイムスタンプと座標系で新しい SpatialPointerPose オブジェクトを受け取るだけです。Simply call SpatialPointerPose::TryGetAtTimestamp to receive a new SpatialPointerPose object at the specified timestamp and coordinate system. この SpatialPointerPose には、中心と方向があります。This SpatialPointerPose contains a head-gaze origin and direction. また、視線追跡が利用可能な場合は、視線の原点と方向も含まれています。It also contains an eye-gaze origin and direction if eye tracking is available.

デバイスのサポートDevice support

機能Feature HoloLens (第 1 世代)HoloLens (1st gen) HoloLens 2HoloLens 2 イマーシブ ヘッドセットImmersive headsets
頭の視線入力Head-gaze ✔️✔️ ✔️✔️ ✔️✔️
視線Eye-gaze ✔️✔️

ヘッドの使用Using head-gaze

ヘッド宝石にアクセスするには、まずSpatialPointerPose:: TryGetAtTimestampを呼び出して、新しい SpatialPointerPose オブジェクトを受け取ります。To access the head-gaze, start by calling SpatialPointerPose::TryGetAtTimestamp to receive a new SpatialPointerPose object. 次のパラメーターを渡す必要があります。You need to pass the following parameters.

  • ヘッド見つめの目的の座標系を表すSpatialCoordinateSystemA SpatialCoordinateSystem that represents the desired coordinate system for the head-gaze. これは、次のコードのcoordinateSystem変数によって表されます。This is represented by the coordinateSystem variable in the following code. 詳細については、「コーディネート系開発者ガイド」を参照してください。For more information, visit our coordinate systems developer guide.
  • 要求されたヘッドポーズの正確な時刻を表すタイムスタンプA Timestamp that represents the exact time of the head pose requested. 通常は、現在のフレームが表示される時刻に対応するタイムスタンプを使用します。Typically you will use a timestamp that corresponds to the time when the current frame will be displayed. この予測された表示タイムスタンプは、現在のHolographicFrameからアクセスできるHolographicFramePredictionオブジェクトから取得できます。You can get this predicted display timestamp from a HolographicFramePrediction object, which is accessible through the current HolographicFrame. この HolographicFramePrediction オブジェクトは、次のコードの予測変数によって表されます。This HolographicFramePrediction object is represented by the prediction variable in the following code.

有効な SpatialPointerPose を作成すると、head 位置と前方方向にプロパティとしてアクセスできるようになります。Once you have a valid SpatialPointerPose, the head position and forward direction are accessible as properties. 次のコードは、これらのアクセス方法を示しています。The following code shows how to access them.

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
}

視線を使用するUsing eye-gaze

ユーザーが視線入力を使用できるようにするには、各ユーザーが初めてデバイスを使用するときに、視線追跡ユーザーの調整を行う必要があることに注意してください。Please note that for your users to use eye-gaze input, each user has to go through an eye tracking user calibration the first time they use the device. 目を見つめた API は、頭を見つめているのとよく似ています。The eye-gaze API is very similar to head-gaze. これは、同じSpatialPointerPose API を使用します。これは、シーンに対して raycast できる射線 origin と direction を提供します。It uses the same SpatialPointerPose API, which provides a ray origin and direction that you can raycast against your scene. 唯一の違いは、使用前に視線追跡を明示的に有効にする必要があることです。The only difference is that you need to explicitly enable eye tracking before using it. そのためには、次の2つの手順を実行する必要があります。For this, you need to do two steps:

  1. アプリでアイ tracking を使用するためのアクセス許可をユーザーに要求します。Request user permission to use eye tracking in your app.
  2. パッケージマニフェストで "宝石入力" 機能を有効にします。Enable the "Gaze Input" capability in your package manifest.

視線入力へのアクセスを要求していますRequesting access to eye-gaze input

アプリが起動したら、 EyesPose:: RequestAccessAsyncを呼び出して、視線追跡へのアクセスを要求します。When your app is starting up, call EyesPose::RequestAccessAsync to request access to eye tracking. 必要に応じてユーザーにプロンプトが表示され、アクセス権が付与されるとGazeInputAccessStatus:: Allowedが返されます。The system will prompt the user if needed, and return GazeInputAccessStatus::Allowed once access has been granted. これは非同期呼び出しであるため、追加の管理が必要になります。This is an asynchronous call, so it requires a bit of extra management. 次の例では、デタッチされた std:: thread をスピンアップして、結果を待機します。これは、 m_isEyeTrackingEnabledという名前のメンバー変数に格納されます。The following example spins up a detached std::thread to wait for the result, which it stores to a member variable called 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();

デタッチされたスレッドの開始は、非同期呼び出しを処理するためのオプションの1つにすぎません。Starting a detached thread is just one option for handling async calls. または、/winrtで C++サポートされている新しい co_await 機能を使用することもできます。Alternatively, you could use the new co_await functionality supported by C++/WinRT. ユーザーのアクセス許可を要求するもう1つの例を次に示します。Here is another example for asking for user permission:

  • EyesPose:: IsSupported () を使用すると、アプリケーションは、視線トラッカーがある場合にのみアクセス許可ダイアログをトリガーできます。EyesPose::IsSupported() allows the application to trigger the permission dialog only if there is an eye tracker.
  • GazeInputAccessStatus m_gazeInputAccessStatus;これは、アクセス許可プロンプトが何度も表示されないようにするためです。GazeInputAccessStatus m_gazeInputAccessStatus; // This is to prevent popping up the permission prompt over and over again.
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;    
        }
    });
}

宝石入力機能の宣言Declaring the Gaze Input capability

ソリューションエクスプローラーで package.appxmanifest ファイルをダブルクリックします。Double click the appxmanifest file in Solution Explorer. 次に、[機能] セクションに移動して、[宝石入力] 機能を確認します。Then navigate to the Capabilities section and check the Gaze Input capability.

見つめ入力機能

これにより、package.appxmanifest ファイルのPackageセクションに次の行が追加されます。This adds the following lines to the Package section in the appxmanifest file:

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

視線を見つめますGetting the eye-gaze ray

にアクセスした後は、すべてのフレームに対して視線を自由に取得できます。Once you have received access to ET, you are free to grab the eye-gaze ray every frame. ヘッドを見つめた場合と同様に、目的のタイムスタンプと座標系を使用してSpatialPointerPose:: TryGetAtTimestampを呼び出すことによってSpatialPointerPoseを取得します。Just as with head-gaze, get the SpatialPointerPose by calling SpatialPointerPose::TryGetAtTimestamp with a desired timestamp and coordinate system. SpatialPointerPose には、視線プロパティを通じてEyesPoseオブジェクトが含まれています。The SpatialPointerPose contains an EyesPose object through the Eyes property. この値は、視線追跡が有効になっている場合にのみ null です。This is non-null only if eye tracking is enabled. そこから、 EyesPose:: IsCalibrationValidを呼び出すことによって、デバイスのユーザーが監視の調整を行っているかどうかを確認できます。From there you can check if the user in the device has an eye tracking calibration by calling EyesPose::IsCalibrationValid. 次に、見つめプロパティを使用して、視線位置と方向を含むSpatialRayを取得します。Next, use the Gaze property to get the a SpatialRay containing the eye-gaze position and direction. "宝石" プロパティは null になることがあるため、必ず確認してください。The Gaze property can sometimes be null, so be sure to check for this. これは、調整されたユーザーが一時的にその目を閉じる場合に発生する可能性があります。This can happen is if a calibrated user temporarily closes their eyes.

次のコードは、視線光線にアクセスする方法を示しています。The following code shows how to access the eye-gaze 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 when eye tracking is not available

この記事で説明したように、開発者と開発者はどちらも、アプリで目の追跡データを使用できない可能性があるインスタンスがある可能性があることに注意する必要があります。As mentioned in our eye tracking design docs, both designers as well as developers should be aware that there may be instances in which eye tracking data may not be available to your app. これには、ユーザーが調整されていないこと、ユーザーがアプリに対する目の追跡データへのアクセスを拒否した、または単に一時的な干渉 (HoloLens バイザーまたはヘア occluding のユーザーの目で見た汚れなど) へのアクセスが拒否されたなどのさまざまな理由があります。There are various reasons for this ranging from a user not being calibrated, the user having denied the app access to his/her eye tracking data or simply temporary interferences (such as smudges on the HoloLens visor or hair occluding the user's eyes). このドキュメントでは、いくつかの Api について既に説明しましたが、次の例では、クイックリファレンスとしてアイトラッキングを利用できることを検出する方法の概要を説明します。While some of the APIs have already been mentioned in this document, in the following, we provide a summary of how to detect that eye tracking is available as a quick reference:

  • システムが目の追跡をまったくサポートしていることを確認します。Check that the system supports eye tracking at all. 次のメソッドを呼び出します。 EyesPose ()です。Call the following method: Windows.Perception.People.EyesPose.IsSupported()

  • ユーザーが調整されていることを確認します。Check that the user is calibrated. 次のプロパティを呼び出します。 EyesPose. IsCalibrationValidCall the following property: Windows.Perception.People.EyesPose.IsCalibrationValid

  • ユーザーが、視線追跡データを使用するためのアプリのアクセス許可を与えられていることを確認します。現在の ' GazeInputAccessStatus ' を取得します。Check that the user has given your app permission to use their eye tracking data: Retrieve the current 'GazeInputAccessStatus'. これを行う方法の例については、「宝石入力へのアクセスの要求」をご覧ください。An example on how to do this is explained at Requesting access to gaze input.

さらに、次に説明するように、受信した視線追跡データの更新の間にタイムアウトを追加して、またはそれ以外の方法でヘッドから宝石にフォールバックすることで、目の追跡データが古くなっていないことを確認することもできます。In addition, you may want to check that your eye tracking data is not stale by adding a timeout between received eye tracking data updates and otherwise fallback to head-gaze as discussed below.
詳細については、フォールバック設計の考慮事項に関するページを参照してください。Please visit our fallback design considerations for more information.


と他の入力との相関関係Correlating gaze with other inputs

場合によっては、過去のイベントに対応するSpatialPointerPoseが必要になることがあります。Sometimes you may find that you need a SpatialPointerPose that corresponds with an event in the past. たとえば、ユーザーがエアタップを実行した場合、アプリはどのような情報を探しているかを知る必要があります。For example, if the user performs an Air Tap, your app might want to know what they were looking at. このため、 SpatialPointerPose:: TryGetAtTimestampを予測されたフレーム時間と共に使用することは、システムの入力処理と表示時間の間の待機時間が原因で不正確になることがあります。For this purpose, simply using SpatialPointerPose::TryGetAtTimestamp with the predicted frame time would be inaccurate because of the latency between system input processing and display time. さらに、ターゲットのために視線を使用している場合、この目はコミットアクションを終了する前にも移動する傾向があります。In addition, if using eye-gaze for targeting, our eyes tend to move on even before finishing a commit action. これは、単純なエアタップの場合の問題にはなりませんが、長い音声コマンドと高速な動きを組み合わせると、より重要になります。This is less of an issue for a simple Air Tap, but becomes more critical when combining long voice commands with fast eye movements. このシナリオを処理する方法の1つとして、入力イベントに対応する履歴タイムスタンプを使用して、 SpatialPointerPose:: TryGetAtTimestampを追加で呼び出すことができます。One way to handle this scenario is to make an additional call to SpatialPointerPose::TryGetAtTimestamp, using a historical timestamp that corresponds to the input event.

ただし、SpatialInteractionManager を経由した入力については、簡単な方法があります。However, for input that routes through the SpatialInteractionManager, there's an easier method. SpatialInteractionSourceStateには、独自のTrygetattimestamp関数があります。The SpatialInteractionSourceState has its very own TryGetAtTimestamp function. を呼び出すと、完全に相関したSpatialPointerPoseが推測されずに提供されます。Calling that will provide a perfectly correlated SpatialPointerPose without the guesswork. SpatialInteractionSourceStates の使用方法の詳細については、DirectX のドキュメントのハンズオンコントローラーとモーションコントローラーを参照してください。For more information on working with SpatialInteractionSourceStates, take a look at the Hands and Motion Controllers in DirectX documentation.


目盛りCalibration

視線追跡を正確に機能させるには、各ユーザーが視点を追跡する必要があります。For eye tracking to work accurately, each user is required to go through an eye tracking user calibration. これにより、デバイスはシステムを調整して、より快適で品質の高い閲覧エクスペリエンスをユーザーに提供し、同時に正確な視点を追跡することができます。This allows the device to adjust the system for a more comfortable and higher quality viewing experience for the user and to ensure accurate eye tracking at the same time. 開発者は、ユーザーの調整を管理するために、エンドユーザーの作業を行う必要はありません。Developers don’t need to do anything on their end to manage user calibration. システムによって、次のような状況で、デバイスを調整するように求めるメッセージがユーザーに表示されます。The system will ensure that the user gets prompted to calibrate the device under the following circumstances:

  • ユーザーが初めてデバイスを使用しているThe user is using the device for the first time
  • ユーザーが以前に調整プロセスをオプトアウトしたThe user previously opted out of the calibration process
  • ユーザーが最後にデバイスを使用したときに、調整プロセスが成功しませんでしたThe calibration process did not succeed the last time the user used the device

開発者は、視線追跡データが使用できない可能性のあるユーザーに対して十分なサポートを提供する必要があります。Developers should make sure to provide adequate support for users for whom eye tracking data may not be available. 代替ソリューションに関する考慮事項の詳細については、「 Hololens 2 での目の追跡」を参照してください。Learn more about considerations for fallback solutions at Eye tracking on Hololens 2.


関連項目See also