1. 소개
이 API는 사용자의 손 각 골격 관절의 자세를 노출합니다。 이는 제스처 감지 또는 VR 시나리오에서 손 모델을 렌더링하는 데 사용할 수 있습니다。
2. 초기화
애플리케이션이 세션 동안 관절화된 손 자세 정보를 확인하려면, 세션은 적절한 기능 서술자로 요청되어야 합니다。 문자열 "hand-tracking"은 이 모듈에 의해 관절화된 손 추적을 위한 새로운 유효한 기능 서술자로 도입됩니다。
"hand-tracking" 기능 서술자는 해당
XRSession의
XR 장치가 물리적 손 입력 소스를 가지고 있고 그것이 손 추적을 지원할 때에만 부여되어야 합니다。
사용자 에이전트는 이 기능 서술자에 기반하여 손 기반 XRInputSources에
대한 지원을 제한할 수 있습니다。
참고: 이는 XRSession가
"hand-tracking" 기능 서술자를
요청하지 않는 경우, 사용자 에이전트가 손 기반 입력 컨트롤러에 대한 지원을 하지 않기로 선택할 수 있음을 의미합니다。
3. 물리적 손 입력 소스
An XRInputSource
is a physical
hand input source if it tracks a physical hand. A physical hand input
source supports hand tracking if it supports reporting the poses of one or
more skeleton joints
defined in this specification.
Physical
hand input sources MUST include the input profile name of
"generic-hand-select" in their profiles.
많은 물리적 손 입력 소스에서는 주요 동작에 사용되는 제스처와 스퀴즈 동작이 겹칠 수 있습니다。예를 들어, 핀치 제스처는 가까운 객체와 상호 작용할 때는 "select"를, 먼 객체와 상호 작용할 때는 "squeeze" 이벤트를 나타낼 수 있습니다。컨텐츠가 이들을 독립적인 이벤트로 가정할 수 있기 때문에, 사용자 에이전트는 스퀴즈 동작을 기본 스퀴즈 동작으로 노출하는 대신 추가적인 "grasp 버튼"으로 노출하고 "generic-hand-select-grasp" 프로필에서 파생된 입력 프로필을 사용할 수 있습니다。
3.1. XRInputSource
partial interface XRInputSource { [SameObject ]readonly attribute XRHand ?hand ; };
The hand attribute on a physical hand input
source that supports hand tracking will be an XRHand object giving
access to the underlying hand-tracking capabilities。 hand
will have its input
source set to this。
If the XRInputSource
belongs to an XRSession
that has not been requested with the "hand-tracking" feature
descriptor, hand
MUST be null.
3.2. 골격 관절
물리적 손 입력 소스는 여러 개의 골격 관절로 구성됩니다。
골격 관절은 특정 손에 대해 골격 관절 이름으로 고유하게 식별할
수 있으며, 이는 XRHandJoint
타입의 열거형입니다。
골격 관절에는 그 이름을 따서 명명되고 그
-Z 축을 정렬하는 데 사용되는 연관 뼈(associated bone)이 있을 수 있습니다。연관 뼈는 손끝을 향해 이동할 때 관절 다음에 오는 뼈입니다。끝 관절과 손목 관절은
연관 뼈가 없습니다。
골격 관절에는 그 중심에 배치된 구의 반지름인 반지름이 있으며, 이는 손 양측의 피부에 대략적으로 닿도록 합니다。“끝(tip)” 관절은 끝부분과의 충돌이 작동하도록 적절한 0이 아닌 반지름을 가져야 합니다。구현체는 끝 관절이 0이 아닌 반지름을 가지도록 원점을 오프셋할 수 있습니다。
이 관절 목록은 다음의 골격 관절과 그 순서를 정의합니다:
| 골격 관절 | 골격 관절 이름 | 인덱스 | |
|---|---|---|---|
| 손목 | 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 | |
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
The XRHandJoint
enum defines the various joints that each XRHand MUST contain。
모든 XRHand에는 그것이 추적하는
입력
소스가 연결되어 있습니다。
참고: handedness 속성은 XRInputSource가
어떤 손과 연결되어 있는지를 설명합니다(있는 경우)。
XRHand 객체는 [[joints]] 내부 슬롯을 가지며,
이는 키가 XRHandJoint
타입이고 값이 XRJointSpace
타입인 쌍들의 정렬된 맵입니다。
내부 슬롯 [[joints]]의
순서는 관절 목록에 의해 주어집니다。
[[joints]]는
세션 동안 변경되어서는 안 됩니다。
개별 장치가 이 명세서에 정의된 관절을 지원하지 않는 경우에도, 그것을 에뮬레이트해야 합니다。
The size attribute MUST return the number 25。
get(jointName) 메서드는 XRHand의 this에서 호출될 때 다음
단계를 실행해야 합니다:
-
joints를 this의
[[joints]]내부 슬롯의 값으로 둡니다。 -
joints[jointName]를 반환합니다。(알 수 없는 jointName의 경우
undefined를 반환함을 의미합니다。)
3.4. XRJointSpace
[Exposed =Window ]interface :XRJointSpace XRSpace {readonly attribute XRHandJoint ; };jointName
XRJointSpace의
네이티브 원점은 해당 관절의 위치 및 방향이다.
XRJointSpace의
네이티브 원점은 동일 손의 모든 XRJointSpace의
네이티브 원점이 보고되는 경우에만 보고될 수 있다. 손이 부분적으로 가려질 때는, UA는 가려진 관절을 에뮬레이션하거나 모든 관절에 null
포즈를 리포트해야 한다.
참고: 포즈를 얻을 때 손 전체를 얻거나 아무것도 얻지 못하게 됨을 의미한다.
이로 인해 다지/소지 손을 충실히 표현할 수 없으나, 핑거프린팅 우려로 인해 별도 opt-in이 필요할 수 있다. 더 자세한 사항은 Issue 11 참조.
네이티브 원점의 -Y 방향은 손바닥에서 바깥쪽 피부에 수직, -Z 방향은 손목에서 손 끝 방향 뼈를
따라가야 한다.
끝(tip) 골격 관절의 경우 연관 뼈가 없으므로,
-Z 방향은 연결된 원위(distal) 관절과 같아야 한다. 즉, 직전 뼈 방향과 같다. 손목 골격 관절의 -Z 방향은 손바닥 중심을 향해야 한다.
모든 XRJointSpace는
자신을 생성한 XRHand와 연결된
hand를 가진다.
jointName은 추적하는 관절의 이름을 반환한다.
모든 XRJointSpace는
대응되는 골격 관절과 연결된 joint를 가진다. 이 joint는 jointName에 해당하는 골격 관절이다.
4. 프레임 루프
4.1. XR프레임(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)
메서드는 해당 joint의 baseSpace에 대한 포즈를 XRJointPose로
반환하며, XRFrame의
시간을 기준으로 한다.
이 메서드가 호출될 때, UA는 다음을 실행해야 한다:
-
frame을 this로 한다.
-
session을 frame의
session객체로 한다. -
frame의 active가
false면InvalidStateError발생 후 종료한다. -
baseSpace나 joint의 session이 this
session과 다르면InvalidStateError발생 후 종료한다. -
pose를 session의 relevant realm에 새로운
XRJointPose객체로 생성한다. -
frame이 나타내는 시점에서 joint의 baseSpace에서의 포즈 초기화를 pose에 force emulation=false로 해서 채운다.
-
pose가
null이면null을 리턴한다. -
pose를 반환한다.
fillJointRadii(sequence<XRJointSpace> jointSpaces, Float32Array radii)
메서드는 jointSpaces에 해당하는 radii에 반지름 값을 쓰며, 모든 공간이 유효한 포즈를 갖는지 boolean으로 반환한다.
이 메서드가 XRFrame
frame에서 호출될 때 UA는 다음을 반드시 수행해야 한다:
-
frame을 this로 한다.
-
session을 frame의
session객체로 한다. -
frame의 active가
false면InvalidStateError발생 후 종료. -
jointSpaces 내 각 joint에 대해:
-
joint의 session이 session과 다르면
InvalidStateError발생 후 종료.
-
-
jointSpaces 길이가 radii의 요소 개수보다 크면
TypeError발생 후 종료. -
offset을
0으로 하는 새 숫자 변수로 둔다. -
allValid을
true로 한다. -
jointSpaces 내 모든 joint에 대해:
-
allValid을 반환.
참고: UA가 동일 XRHand에 속한 공간 중 어느
하나도 포즈 결정을 못하면, 그 XRHand의 모든 공간은 포즈를 못
가져야 한다.
fillPoses(sequence<XRSpace> spaces, XRSpace baseSpace, Float32Array transforms)
메서드는 spaces 각각의 baseSpace 기준 포즈 행렬을 transforms에 채우고, 모든 공간이 유효한 포즈를
가지는지 boolean으로 반환한다.
이 메서드가 XRFrame
frame에서 호출되면, UA는 다음을 수행해야 한다:
-
frame을 this로 한다.
-
session을 frame의
session객체로 한다. -
frame의 active가
false면InvalidStateError발생 후 종료. -
spaces 시퀀스 내 각 space에 대해:
-
space의 session이 session과 다르면
InvalidStateError발생 후 종료.
-
-
baseSpace의 session이 session과 다르면
InvalidStateError발생 후 종료. -
spaces 길이 *
16이 transforms의 요소 개수보다 크면TypeError발생 후 종료. -
offset을
0으로 하는 새 숫자 변수로 둠. -
pose를 다음 기준으로 초기화:
-
fillPoses()가 이전에 호출된 적 있으면 UA가 객체 재사용 허용 - pose는 앞서 호출에 사용된 객체 동일하게 둔다.
- 아니면
- pose를 session의 relevant realm에 새로운
XRPose객체로 생성.
-
-
allValid을
true로 한다. -
spaces 내 각 space에 대해:
-
allValid을 반환.
참고: 동일 XRHand 소속 공간 중 어떤
것이든 포즈 초기화 시 null을 반환하면, 해당 XRHand의 모든 공간이 포즈 초기화 시 null을 반환해야 한다.
4.2. XR관절포즈(XRJointPose)
XRJointPose는
XRPose이며,
표현하는 골격 관절의 크기 추가 정보를
가진다.
[Exposed =Window ]interface :XRJointPose XRPose {readonly attribute float radius ; };
radius 속성은 해당 골격 관절의 반지름을 미터 단위로 반환한다.
UA는 XR 장치가 이 값을 일반적으로, 또는 현재 애니메이션 프레임에서(예: 해당 골격 관절이 일부 가려짐) 판별이 불가할 때
radius를
반드시 에뮬레이션 값으로 설정해야 한다.
5. 프라이버시 및 보안 고려사항
WebXR Hand Input API는 중요한 프라이버시 리스크를 수반하는 강력한 기능이다.이 기능이 새로운 센서 데이터를 반환하므로, UA는 세션 생성 시점에 반드시 사용자에게 명시적 동의를 구해야 한다.
이 API로부터 반환되는 데이터는 개인을 식별할 수 있을 정도로 구체적이어서는 안 된다. 하드웨어가 너무 정밀한 데이터를 리턴할 경우, UA는 WebXR Hand Input API를 통해 이 데이터를 노출하기 전 반드시 익명화해야 한다.
이 API는 "immersive-vr"
또는 "immersive-ar"
XRSessionMode로 생성된 XRSession에서만 지원되어야 한다.
"inline"
세션은 이 API를 지원해서는 안 된다.
-
노이징보다는 반올림을 권장한다.
-
UA가 반올림을 사용할 때 각 관절을 독립적으로 반올림하면 안 된다. 올바른 방법은 각 손을 정적 손 모델에 매핑시키는 것이다.
-
노이징을 쓰는 경우, 노이즈 데이터가 시간이 지나도 어떤 정보도 누설하지 않아야 한다:
-
익명화는 신뢰된 환경에서 이루어져야 한다.
변경 사항
2020년 10월 22일 첫 공개 워킹드래프트에서의 변경 사항
-
grasp 프로필 언급 (GitHub #68)
-
상수에서 enum으로 변경 + XRHand를 map으로 변경 (GitHub #71)
-
보안 섹션에 추가 설명 (GitHub #87)
-
hand 속성을 sameobject로 표시 + 설명 노트 추가 (GitHub #93)
-
tip에 0이 아닌 반지름 명시 (GitHub #111)