DirectX での頭の視線入力と目での視線入力
Note
この記事は、従来のWinRTネイティブAPIに関連します。 新しいネイティブ アプリ プロジェクトの場合は、 OpenXR API を使用することをお勧めします。
Windows Mixed Reality では、目と頭による視線入力を使用して、ユーザーが何を見ているかを判断します。 このデータを使用して、頭の視線入力とコミットなどの主要な入力モデルを操作したり、さまざまな相互作用の種類のコンテキストを提供したりできます。 API で利用できる視線ベクトルには、頭の視線入力と目の視線入力の 2 種類があります。 どちらも原点と方向を持つ 3 次元のレイとして提供されます。 その後、アプリケーションは、それらのシーンや現実の世界に レイキャストし、ユーザーの対象を特定できます。
頭の視線入力は、ユーザーの頭が向いている方向を表しています。 頭の視線入力とは、デバイス自体の位置と前方方向と考えてください。その位置は、2 つのディスプレイの間の中心点です。 頭の視線入力は、すべての Mixed Reality デバイスで使用できます。
目の視線入力は、ユーザーの目が見ている方向を表します。 原点はユーザーの目の間にあります。 これは、視線追跡システムを含む Mixed Reality デバイスで使用できます。
頭と目の視線入力には両方とも、SpatialPointerPose API を使用してアクセスできます。 SpatialPointerPose:: TryGetAtTimestamp を呼び出して、指定したタイムスタンプと座標系で新しい SpatialPointerPose オブジェクトを受け取ります。 この SpatialPointerPose には、頭の視線入力の原点と方向が含まれています。 また、視線追跡が利用可能な場合は、目の視線入力の原点と方向も含まれています。
デバイス サポート
機能 | HoloLens (第 1 世代) | HoloLens 2 | イマーシブ ヘッドセット |
頭の視線入力 | ✔️ | ✔️ | ✔️ |
アイ視線入力 | ❌ | ✔️ | ❌ |
頭の視線入力の使用
頭の視線入力にアクセスするには、まず 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 を使用します。これは、シーンに対してレイキャストできる光線の原点と方向を提供します。 唯一の違いは、視線追跡を使用する前に明示的に有効にする必要がある点です。
- アプリで視線追跡を使用するためのアクセス許可をユーザーに要求します。
- パッケージ マニフェストで 「視線入力」機能を有効にします。
目の視線入力へのアクセスを要求する
アプリの起動時に、EyesPose::RequestAccessAsync を呼び出して、視線追跡へのアクセスを要求します。 必要に応じてユーザーにメッセージが表示され、アクセスが許可されたら GazeInputAccessStatus::Allowed が返されます。 これは非同期呼び出しなので、追加の管理が少し必要です。 次の例では、デタッチされた std::スレッドをスピンアップして結果を待機しその結果を 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 つにすぎません。 また、C++/WinRT でサポートされる新しい co_await 機能を使うこともできます。 ユーザーのアクセス許可を求めるもう 1 つの例を次に示します。
- 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 は、このドキュメントで既に説明されています。以下では、視線追跡がクイック リファレンスとして使用できるのを検出する方法の概要を示します。
システムで視線追跡がサポートされていることを確認します。 次のメソッドを呼び出します: Windows.Perception.People.EyesPose.IsSupported()
ユーザーが調整済みであることを確認します。 次のプロパティを呼び出します: Windows.Perception.People.EyesPose.IsCalibrationValid
ユーザーがアプリに対して視線追跡データを使用するアクセス許可を付与したことを確認します、現在の'GazeInputAccessStatus' を取得します。 これを行う方法の例については、視線入力へのアクセスを要求するに関するページを参照してください。
また、受信される視線追跡データの更新間にタイムアウトを追加して、視線追跡データが古くないことを確認するか、それ以外の場合は以下で説明するように頭の視線入力にフォールバックする必要があります。 詳細については、 フォールバック 設計に関する考慮事項 に関するページを参照してください。
視線入力を他の入力と関連付ける
過去のイベントに対応する SpatialPointerPose が必要な場合があります。 たとえば、ユーザーがエアタップを実行する場合、アプリは、ユーザーが何を見ているかを知る必要があります。 このため SpatialPointerPose::TryGetAtTimestamp を予測されたフレーム時間と一緒に使用するだけでは、システム入力処理と表示時間の間の待機時間が原因で不正確になります。 また、ターゲット設定に目の視線入力を使用すると、コミット アクションが完了する前でも目が動く傾向があります。 これは単純なエアタップでは大した問題にはなりませんが、長い音声コマンドと高速の目の動きを組み合わせた場合には、より重大になります。 このシナリオを処理する方法の 1 つとして、入力イベントに対応するタイムスタンプの履歴を使用して SpatialPointerPose::TryGetAtTimestamp を追加で呼び出す方法があります。
ただし、SpatialInteractionManager を経由する入力の場合は、より簡単な方法があります。 SpatialInteractionSourceState には、独自の TryGetAtTimestamp 関数があります。 これを呼び出すと、完全に相関する SpatialPointerPose が推測なしで提供されます。 SpatialInteractionSourceStates の操作の詳細については、「DirectX での手とモーション コントローラー」のドキュメントを参照してください。
調整
視線追跡が正しく機能するためには、各ユーザーが視線追跡ユーザー調整を行う必要があります。 これにより、デバイスはシステムを調整して、ユーザーにとってより快適で高品質な視聴エクスペリエンスを実現すると同時に、正確な視線追跡を行うことができます。 開発者は、ユーザーの調整を管理するために、何もする必要はありません。 システムにより、次の状況下でデバイスの調整を求めるメッセージがユーザーに表示されます。
- ユーザーが初めてデバイスを使用する
- ユーザーが前回の調整プロセスを実施しなかった
- ユーザーが前回デバイスを使用したときに、調整プロセスに失敗した
開発者は、視線追跡データを使用できない可能性があるユーザーに対して適切なサポートを提供する必要があります。 フォールバック ソリューションに関する考慮事項の詳細については、「HoloLens 2 での視線追跡」を参照してください。