WebXRハンド入力モジュール - レベル1

W3Cワーキングドラフト,

このドキュメントに関する詳細情報
このバージョン:
https://www.w3.org/TR/2024/WD-webxr-hand-input-1-20240605/
最新公開バージョン:
https://www.w3.org/TR/webxr-hand-input-1/
編集者ドラフト:
https://immersive-web.github.io/webxr-hand-input/
以前のバージョン:
履歴:
https://www.w3.org/standards/history/webxr-hand-input-1/
フィードバック:
GitHub
仕様内コメント
編集者:
(Google [2020年までMozilla])
参加方法:
課題を作成 (オープン中の課題)
メーリングリストアーカイブ
W3Cの #immersive-web IRC

概要

WebXR Hand Input モジュールは、WebXR Device APIに手の関節追跡機能を拡張します。

この文書の位置付け

このセクションは、公開時点でのこの文書の位置付けについて説明しています。現在のW3C出版物の一覧およびこの技術報告書の最新版は、W3C技術報告書インデックス(https://www.w3.org/TR/)でご覧いただけます。

Immersive Web作業グループは、グループがまだ対応していない全てのバグ報告の一覧を管理しています。このドラフトでは、作業グループで今後議論される保留中の課題のいくつかを強調しています。これらの課題が有効かどうかなど、その結論についてはまだ決定がなされていません。未解決の課題に対する仕様案のプルリクエストを強く推奨します。

この文書は、Immersive Web作業グループによって、勧告トラックを用いてワーキングドラフトとして公開されました。本書はW3C勧告となることを意図しています。

ワーキングドラフトとしての公開は、W3Cおよびそのメンバーによる保証を意味しません。この文書はドラフトであり、随時更新、置換、または廃止される可能性があります。進行中の作業以外として本文書を引用するのは不適切です。

この文書は、W3C特許ポリシーの下で活動するグループによって作成されました。W3Cは、同グループ成果物に関してなされた公開特許開示リストを維持しています;そのページには特許開示方法の案内も含まれています。ある個人が本質的請求(Essential Claim(s))を含む特許を認識した場合、その情報をW3C特許ポリシー第6節に従って開示しなければなりません。

この文書は、2023年11月3日付W3Cプロセス文書によって管理されます。

このWebXR拡張現実モジュールは、WebXR Device APIに追加するモジュールとして設計されており、もとはコアおよびモジュールに分割される前のWebXR Device APIに含まれていました。

1. はじめに

一部のXRデバイスでは、入力ソースとして使用された際にユーザーの手の完全な関節情報を取得できます。

このAPIはユーザーの両手のスケルトンジョイントごとの姿勢を公開します。これを利用してジェスチャー検出や、VRシナリオでの手モデルの描画が可能になります。

2. 初期化

アプリケーションがセッション中に手の関節姿勢情報を取得したい場合は、該当するフィーチャディスクリプタでセッションをリクエストする必要があります。文字列"hand-tracking"は、本モジュールにより導入される、新たな有効な手関節トラッキング用フィーチャディスクリプタです。

"hand-tracking" フィーチャディスクリプタは、XRSessionXRデバイス物理ハンド入力ソースを持ち、ハンドトラッキングに対応している場合のみに許可されるべきです。

ユーザーエージェントは、このフィーチャディスクリプタに基づき、手ベースのXRInputSourcesサポートを制御することができます。

注: つまり、XRSessionが"hand-tracking"フィーチャディスクリプタをリクエストしない場合、ユーザーエージェントは手ベースの入力コントローラーをサポートしないことがあります。

3. 物理ハンド入力ソース

XRInputSource は、物理的な手をトラッキングする場合、物理ハンド入力ソースです。物理ハンド入力ソースハンドトラッキングに対応するのは、本仕様で定義されるスケルトンジョイントの1つ以上の姿勢報告をサポートしている場合です。

物理ハンド入力ソースには、入力プロファイル名として"generic-hand-select"をprofilesに必ず含めなければなりません。

多くの物理ハンド入力ソースの場合、プライマリアクションとスクイーズアクションに使われるジェスチャーが重複することがあります。例えば、つまみ(ピンチ)ジェスチャーは、近くや遠くのオブジェクトとの相互作用に応じて、「選択」イベントあるいは「スクイーズ」イベントの両方を示せます。コンテンツがこれらを独立したイベントとして扱う可能性があるため、ユーザーエージェントはスクイーズアクションをプライマリースクイーズアクションとして公開する代わりに、"generic-hand-select-grasp"プロファイル由来の入力プロファイルを用いて追加の「グラスプボタン」として公開することができます。

3.1. XRInputSource

partial interface XRInputSource {
   [SameObject] readonly attribute XRHand? hand;
};

ハンドトラッキングに対応するhand属性は、基盤となるハンドトラッキング機能にアクセスできるXRHandオブジェクトです。handは、その入力ソースthisに設定されます。

XRInputSource が、"hand-tracking"フィーチャディスクリプタをリクエストしていないXRSessionに属している場合、handnullでなければなりません。

3.2. スケルトンジョイント

物理ハンド入力ソースは多数のスケルトンジョイントで構成されます。

手のスケルトンジョイントは、スケルトンジョイント名で一意に識別できます。これはXRHandJoint型の列挙値です。

スケルトンジョイントには、それにちなんだ名称で-Z軸の向きを決定するために使われる対応する骨がある場合があります。対応する骨とは、スケルトンジョイントから指先方向に進んだとき、ジョイントの次に現れる骨のことです。tipおよびwristジョイントには対応する骨はありません。

スケルトンジョイントには、その中心に球を配置して手の両側の皮膚に概ね接するようにした時の半径を持ちます。"tip" スケルトンジョイントは、指先との衝突判定のために適切な非ゼロ半径を持つべきです。実装では、tipジョイントの原点をずらして非ゼロ半径の球体形状にしても構いません。

このジョイントリストは、以下のスケルトンジョイントと順序を定めます:

スケルトンジョイント スケルトンジョイント名 インデックス
手首 wrist 0
親指 中手骨 thumb-metacarpal 1
近位指骨 thumb-phalanx-proximal 2
遠位指骨 thumb-phalanx-distal 3
先端 thumb-tip 4
人差し指 中手骨 index-finger-metacarpal 5
近位指骨 index-finger-phalanx-proximal 6
中節骨 index-finger-phalanx-intermediate 7
遠位指骨 index-finger-phalanx-distal 8
先端 index-finger-tip 9
中指 中手骨 middle-finger-metacarpal 10
近位指骨 middle-finger-phalanx-proximal 11
中節骨 middle-finger-phalanx-intermediate 12
遠位指骨 middle-finger-phalanx-distal 13
先端 middle-finger-tip 14
薬指 中手骨 ring-finger-metacarpal 15
近位指骨 ring-finger-phalanx-proximal 16
中節骨 ring-finger-phalanx-intermediate 17
遠位指骨 ring-finger-phalanx-distal 18
先端 ring-finger-tip 19
小指 中手骨 pinky-finger-metacarpal 20
近位指骨 pinky-finger-phalanx-proximal 21
中節骨 pinky-finger-phalanx-intermediate 22
遠位指骨 pinky-finger-phalanx-distal 23
先端 pinky-finger-tip 24

Visual aid demonstrating joint layout

3.3. XRHand

enum XRHandJoint {
  "wrist",

  "thumb-metacarpal",
  "thumb-phalanx-proximal",
  "thumb-phalanx-distal",
  "thumb-tip",

  "index-finger-metacarpal",
  "index-finger-phalanx-proximal",
  "index-finger-phalanx-intermediate",
  "index-finger-phalanx-distal",
  "index-finger-tip",

  "middle-finger-metacarpal",
  "middle-finger-phalanx-proximal",
  "middle-finger-phalanx-intermediate",
  "middle-finger-phalanx-distal",
  "middle-finger-tip",

  "ring-finger-metacarpal",
  "ring-finger-phalanx-proximal",
  "ring-finger-phalanx-intermediate",
  "ring-finger-phalanx-distal",
  "ring-finger-tip",

  "pinky-finger-metacarpal",
  "pinky-finger-phalanx-proximal",
  "pinky-finger-phalanx-intermediate",
  "pinky-finger-phalanx-distal",
  "pinky-finger-tip"
};

[Exposed=Window]
interface XRHand {
    iterable<XRHandJoint, XRJointSpace>;

    readonly attribute unsigned long size;
    XRJointSpace get(XRHandJoint key);
};

XRHandJoint列挙型は各XRHandが必ず備えるべきジョイント種別を定義します。

すべてのXRHandには、トラッキング対象の物理ハンド入力ソースである入力ソースが関連付けられています。

注: handednessプロパティは、XRInputSourceがどちらの手に対応するかを示します。

XRHandオブジェクトには[[joints]]内部スロットがあり、キーがXRHandJoint型、値がXRJointSpace型の順序付きマップです。

[[joints]]の順序はスケルトンジョイントの下のジョイントリストによります。

[[joints]]はセッション中に変化してはいけません。

反復する値ペアXRHandオブジェクトにとって、キーがXRHandJoint、値がそのXRJointSpaceであり、スケルトンジョイント下のジョイントリスト順になります。

個々のデバイスが本仕様で定めるジョイントをサポートしない場合、必ずエミュレートしなければなりません。

size属性は、必ず数値25を返さなければなりません。

get(jointName)メソッドをXRHandで呼び出すと、次の手順を実行します:
  1. jointsを、this[[joints]]内部スロット値とする。

  2. joints[jointName]を返す。(未知のjointNameの場合、undefinedとなることを意味します。)

3.4. XRJointSpace

[Exposed=Window]
interface XRJointSpace: XRSpace {
  readonly attribute XRHandJoint jointName;
};

XRJointSpaceネイティブ原点は、基盤となる関節の位置と向きです。

XRJointSpaceネイティブ原点は、同じ上の他のすべてのXRJointSpaceネイティブ原点が報告されている場合のみ報告されます。手が一部隠れている場合、ユーザーエージェントは隠れた関節をエミュレートするか、すべての関節の姿勢をnullで報告しなければなりません。

注: 姿勢の取得時には手全体か何も得られないかのいずれかとなります。

これにより、多指症や少指症の手の忠実な公開は現状できませんが、フィンガープリンティング懸念からも、いずれにせよ別途オプトインが必要となる可能性が高いです。詳細はIssue 11を参照してください。

ネイティブ原点-Y方向は手のひらから外側に向けて皮膚と直交し、-Z方向は対応する骨に沿って、手首から指先方向を指します。

先端のスケルトンジョイント 対応する骨が存在しない場合、-Z方向は対応する遠位ジョイントと同じ、すなわち一つ前の骨の方向になります。手首のスケルトンジョイントでは、-Z方向は手のひらの中心方向を大まかに指すべきです。

すべてのXRJointSpaceには、作成元であるXRHandであるが関連付けられています。

jointNameは、トラッキングしている関節の名称を返します。

すべてのXRJointSpaceには、スケルトンジョイントリスト内で対応するjointName関節が関連付けられています。

4. フレームループ

4.1. XRFrame

partial interface XRFrame {
    XRJointPose? getJointPose(XRJointSpace joint, XRSpace baseSpace);
    boolean fillJointRadii(sequence<XRJointSpace> jointSpaces, Float32Array radii);

    boolean fillPoses(sequence<XRSpace> spaces, XRSpace baseSpace, Float32Array transforms);
};

getJointPose(XRJointSpace joint, XRSpace baseSpace)メソッドは、XRFrametime時点で、jointbaseSpaceに対する姿勢をXRJointPoseで返します。

このメソッドが呼び出されると、ユーザーエージェントは以下の手順を実行します:

  1. framethisとする。

  2. sessionframesessionオブジェクトとする。

  3. frameactivefalseなら、InvalidStateErrorを投げる。

  4. baseSpaceまたはjointsessionsessionと異なれば、InvalidStateErrorを投げる。

  5. poseを、sessionrelevant realm新しいXRJointPoseとする。

  6. jointbaseSpaceframe時点の姿勢情報を投入し、force emulationfalseにする。

  7. posenullなら、nullを返す。

  8. poseradiusjoint半径(必要に応じエミュレート)でセットする。

  9. poseを返す。

fillJointRadii(sequence<XRJointSpace> jointSpaces, Float32Array radii)メソッドはjointSpacesの半径値をradiiに格納し、すべてのspaceに有効な姿勢があるかどうかを示すブール値を返します。

このメソッドをXRFrameインスタンスframeで呼ぶと、ユーザーエージェントは以下を実行します:

  1. framethisとする。

  2. sessionframesessionオブジェクトとする。

  3. frameactivefalseであれば、InvalidStateErrorを投げる。

  4. jointSpaces内の各jointについて:

    1. jointsessionsessionが異なれば、InvalidStateErrorを投げる。

  5. jointSpacesの長さがradiiの要素数より大きければ、TypeErrorを投げる。

  6. offset0とする。

  7. allValidtrueとする。

  8. jointSpaces内の各jointについて:

    1. offset番目のradiiの値を以下でセット:

      UAがそのjointのすべての関節姿勢を取得できる場合:
      そのjoint半径でセット。
      それ以外
      radiioffsetNaNをセット。
      allValidfalseに。
    2. offset1増やす。

  9. allValidを返す。

注: UAが同じXRHandに属するspaceいずれの姿勢も取得できなければ、そのXRHandの全spaceの姿勢も取得できません。

fillPoses(sequence<XRSpace> spaces, XRSpace baseSpace, Float32Array transforms)メソッドは、spacesの各姿勢行列をbaseSpaceに対してtransformsに格納し、すべてのspaceに有効な姿勢があるかどうかのブール値を返します。

このメソッドをXRFrameインスタンスframeで呼ぶと、ユーザーエージェントは以下を実行:

  1. framethisとする。

  2. sessionframesessionオブジェクトとする。

  3. frameactivefalseであれば、InvalidStateErrorを投げる。

  4. spaces内の各spaceについて:

    1. spacesessionsessionと異なれば、InvalidStateErrorを投げる。

  5. baseSpacesessionsessionと異なれば、InvalidStateErrorを投げる。

  6. spacesの長さに16かけた値がtransformsの要素数より大きければTypeErrorを投げる。

  7. offset0とする。

  8. poseを次のように初期化:

    fillPoses()が以前に呼ばれていれば、UAは:
    poseを前回と同じオブジェクトとできる。
    それ以外
    posesessionrelevant realm内の新しいXRPoseとする。
  9. allValidtrueとする。

  10. spaces内の各spaceについて:

    1. 姿勢情報を投入し、spacebaseSpaceに対するframe時点のposeにセット。

    2. posenullなら次を行う:

    3. transforms配列のoffsetから16要素連続にNaNをセット。

    4. allValidfalseに。

    5. posenullでなければ、そのposematrixメンバー全要素をtransformsoffsetからコピー。

    6. offset16増やす。

  11. allValidを返す。

注: 同じXRHandのspaceが姿勢情報を投入時にnullを返す場合、すべてのspaceも同様にnullを返さなければなりません。

4.2. XRJointPose

XRJointPoseスケルトンジョイントの大きさ情報を持つXRPoseです。

[Exposed=Window]
interface XRJointPose: XRPose {
    readonly attribute float radius;
};

radius属性は、対応するスケルトンジョイントの半径(単位: m)を返します。

UAはradiusを、XRデバイスがこの値を判定できない場合(全体またはそのアニメーションフレームで 例:スケルトンジョイントが一部隠れている場合)、エミュレート値にしなければなりません。

5. プライバシーとセキュリティの考慮事項

WebXRハンド入力APIは強力な機能であり、重大なプライバシーリスクが伴います。

この機能は新たなセンサーデータを返すため、ユーザーエージェントはセッション作成時にユーザーから明示的同意を必ず取得しなければなりません。

このAPIから返されるデータは、個人を特定できない程度に特異であってはなりません。ハードウェアが精度の高すぎるデータを返す場合、ユーザーエージェントはWebXRハンド入力API経由で公開する前にデータを匿名化しなければなりません。

このAPIは、"immersive-vr"または"immersive-ar"で作成されたXRSessionのみでサポートされるべきです。 "inline" セッションでは本APIはサポートしてはなりません。

手データを匿名化する際、UAは次の指針に従えます:

変更点

2020年10月22日初版ワーキングドラフトからの変更点

適合性

文書の表記規則

適合要件は、記述的な断定とRFC 2119用語の組み合わせで表現されています。 この文書の規範的な箇所で使用されている “MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“MAY”、“OPTIONAL” というキーワードは、RFC 2119の定義通りに解釈されます。 ただし可読性を重視し、本仕様書ではこれらの単語はすべて大文字では表記していません。

本仕様の記述は、特に非規範的と明記されたセクション・例・注記を除き、すべて規範的とみなされます。 [RFC2119]

この仕様の例は “for example” という言葉で始まるか、class="example" で規範的な本文から区別されて挿入されます。 例:

これはインフォマティブな例の一つです。

情報的な注記は “Note” で始まり、class="note" で規範的な本文から区別されます。 例:

Note, これは情報的な注記です。

適合アルゴリズム

アルゴリズム手順の一部として命令形で記述された要件(たとえば「先頭の空白文字をすべて取り除く」や「falseを返してこれらの手順を中止する」など)は、 そのアルゴリズム記述の導入部で使われているキーワード("must", "should", "may" など)に従って解釈されます。

アルゴリズムや特定のステップとして表現された適合要件は、最終的な結果が等価である限り、どのような方法でも実装して構いません。 特に本仕様で定義されるアルゴリズムは理解しやすさを重視しており、性能重視ではありません。 実装者には最適化を奨励します。

索引

本仕様で定義される用語

他で定義される用語

参考文献

規範的引用文献

[HTML]
Anne van Kesterenほか. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[SERVICE-WORKERS]
Jake Archibald; Marijn Kruisselbrink. Service Workers. 2022年7月12日. CR. URL: https://www.w3.org/TR/service-workers/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/
[WEBXR]
Brandon Jones; Manish Goregaokar; Rik Cabanier. WebXR Device API. 2024年4月16日. CR. URL: https://www.w3.org/TR/webxr/
[WEBXR-AR-MODULE-1]
Brandon Jones; Manish Goregaokar; Rik Cabanier. WebXR Augmented Reality Module - Level 1. 2022年11月2日. CR. URL: https://www.w3.org/TR/webxr-ar-module-1/

IDLインデックス

partial interface XRInputSource {
   [SameObject] readonly attribute XRHand? hand;
};

enum XRHandJoint {
  "wrist",

  "thumb-metacarpal",
  "thumb-phalanx-proximal",
  "thumb-phalanx-distal",
  "thumb-tip",

  "index-finger-metacarpal",
  "index-finger-phalanx-proximal",
  "index-finger-phalanx-intermediate",
  "index-finger-phalanx-distal",
  "index-finger-tip",

  "middle-finger-metacarpal",
  "middle-finger-phalanx-proximal",
  "middle-finger-phalanx-intermediate",
  "middle-finger-phalanx-distal",
  "middle-finger-tip",

  "ring-finger-metacarpal",
  "ring-finger-phalanx-proximal",
  "ring-finger-phalanx-intermediate",
  "ring-finger-phalanx-distal",
  "ring-finger-tip",

  "pinky-finger-metacarpal",
  "pinky-finger-phalanx-proximal",
  "pinky-finger-phalanx-intermediate",
  "pinky-finger-phalanx-distal",
  "pinky-finger-tip"
};

[Exposed=Window]
interface XRHand {
    iterable<XRHandJoint, XRJointSpace>;

    readonly attribute unsigned long size;
    XRJointSpace get(XRHandJoint key);
};

[Exposed=Window]
interface XRJointSpace: XRSpace {
  readonly attribute XRHandJoint jointName;
};

partial interface XRFrame {
    XRJointPose? getJointPose(XRJointSpace joint, XRSpace baseSpace);
    boolean fillJointRadii(sequence<XRJointSpace> jointSpaces, Float32Array radii);

    boolean fillPoses(sequence<XRSpace> spaces, XRSpace baseSpace, Float32Array transforms);
};

[Exposed=Window]
interface XRJointPose: XRPose {
    readonly attribute float radius;
};

課題一覧

これはデフォルトで多指症/少指症の手を忠実に公開できないことを意味しますが、フィンガープリンティング懸念のため、いずれにせよ別途オプトインが必要になると考えられます。詳細はIssue 11を参照してください。
MDN

XRFrame/fillJointRadii

In no current engines.

FirefoxNoneSafariNoneChromeNone
Opera?EdgeNone
Edge (Legacy)NoneIENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

XRFrame/fillPoses

In no current engines.

FirefoxNoneSafariNoneChromeNone
Opera?EdgeNone
Edge (Legacy)NoneIENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

XRFrame/getJointPose

In no current engines.

FirefoxNoneSafariNoneChromeNone
Opera?EdgeNone
Edge (Legacy)NoneIENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

XRHand

In no current engines.

FirefoxNoneSafariNoneChromeNone
Opera?EdgeNone
Edge (Legacy)NoneIENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?

XRJointSpace

In no current engines.

FirefoxNoneSafariNoneChromeNone
Opera?EdgeNone
Edge (Legacy)NoneIENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

XRInputSource/hand

In no current engines.

FirefoxNoneSafariNoneChromeNone
Opera?EdgeNone
Edge (Legacy)NoneIENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

XRJointPose/radius

In no current engines.

FirefoxNoneSafariNoneChromeNone
Opera?EdgeNone
Edge (Legacy)NoneIENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

XRJointPose

In no current engines.

FirefoxNoneSafariNoneChromeNone
Opera?EdgeNone
Edge (Legacy)NoneIENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

XRJointSpace/jointName

In no current engines.

FirefoxNoneSafariNoneChromeNone
Opera?EdgeNone
Edge (Legacy)NoneIENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?