XRHands 동기화
이 모듈은 XR Hands의 손 상태(손가락 추적 포함)를 동기화하는 방법을 보여줍니다.
손을 다음과 같은 방식으로 표시할 수 있습니다:
- 잡은 컨트롤러에서 받은 데이터로부터
- 손가락 추적 데이터로부터
손가락 추적 데이터와 관련하여, 이 애드온은 대역폭 소비를 줄이기 위해 손 뼈 데이터를 고도로 압축하는 방법을 시연합니다.
참고:
이 애드온은 Meta OVR 손 동기화와 유사하지만, OpenXR 컨텍스트(Oculus Quest, Apple Vision Pro 등)를 대상으로 합니다.
하지만 몇 가지 개선 사항이 포함되어 있으며, 제스처 감지 도구도 추가되었습니다.
손 로직 개요
이 애드온은 컨트롤러 기반 손 추적과 손가락 기반 손 추적이 공존할 수 있도록 합니다. 목표는 두 가지 방식 간에 원활한 전환을 제공하는 것입니다.
이를 위해:
- 다른 VR 샘플과 마찬가지로, 컨트롤러가 사용될 때 표시되는 손 모델은 Oculus 샘플 프레임워크에서 제공되는 모델입니다.
- 손가락 추적이 사용될 때, 로컬 사용자를 위해
XRHandCollectableSkeletonDriver
는 XRHands의XRHandSkeletonDriver
컴포넌트의 서브 클래스로, 손과 손가락뼈 데이터를 수집하여 로컬 스켈레톤에 적용합니다.
이 스켈레톤은 로컬 전용 메시를 표시하는 데 사용될 수 있지만, 기본 프리팹에서는 네트워크 버전의 손을 표시하기 위해 숨깁니다. - 원격 사용자에게는
NetworkBonesStateSync
컴포넌트(아래에 설명)가 네트워크에서 손과 손가락뼈 회전 데이터를 복원하여 손 스켈레톤의 변환을 이동시킵니다. 이 스켈레톤은 스킨드 메시 렌더러에 의해 올바르게 손을 렌더링 합니다. - 모든
Render
동안,NetworkBonesStateSync
는 Fusion 보간 데이터를 사용하여 두 개의 손 상태 사이에서 손 뼈 회전을 부드럽게 보간하여 두 틱 사이에서도 부드러운 뼈 회전을 표시합니다.
로컬 하드웨어 리그 손 컴포넌트
로컬 사용자 하드웨어 리그의 손 계층 구조 아래에 배치된 XRHandCollectableSkeletonDriver
컴포넌트는 동기화 컴포넌트에 제공할 손 상태를 수집합니다. 이 컴포넌트는 XRHands의 상위 컴포넌트인 XRHandSkeletonDriver
컴포넌트를 사용하여 손 상태(특히 손가락뼈 회전)에 접근하며, 형제 컴포넌트인 XRHandTrackingEvents
도 필요합니다.
또한 손가락 추적이 현재 사용 중인지 알 수 있는 헬퍼 속성을 포함하고 있습니다.
참고:
XRHandCollectableSkeletonDriver
는 또한 분석 코드를 포함하여 기본 뼈 위치를 찾고 뼈 회전 압축 표현을 제안하며, 아래 "네트워크 리그 손 컴포넌트"에서 설명한 대로 손 상태 동기화에 필요한 스크립트 객체를 채웁니다. 기본 스크립트는 대부분의 요구에 맞게 제공되며, 애드온에 제공된 분석 코드는 뼈 구조가 많이 변경되지 않는 한 사용할 필요가 없습니다.
네트워크 리그 손 컴포넌트
로컬 사용자에 대해, 사용자 네트워크 리그의 NetworkBonesStateSync
컴포넌트는 로컬 하드웨어 손에서 IBonesCollecter
인터페이스를 구현하는 컴포넌트를 찾아 로컬 손 상태를 파악한 후 FixedUpdateNetwork
동안 네트워크 변수에 저장합니다.
이 IBonesCollecter
인터페이스는 XRHandCollectableSkeletonDriver
컴포넌트에 의해 구현되며:
- 손가락 추적 상태를 제공합니다.
- 손 뼈 회전을 제공합니다.
원격 사용자에 대해서는, 이 동기화된 데이터를 분석하여 Render
동안 로컬 손 상태를 재구성합니다.
네트워크를 통해 동기화된 회전 데이터 외에도, BonesStateScriptableObject
는 손 뼈의 로컬 위치를 제공합니다(이 위치는 변경되지 않음).
애드온에는 XRHand의 손에 대한 로컬 위치를 정의하는 LeftXRHandsDefaultBonesState
및 RightXRHandsDefaultBonesState
에셋이 포함되어 있습니다.
주어진 순간에 각 뼈의 부드러운 회전을 보여주기 위해, NetworkBonesStateSync
는 두 개의 수신된 상태 사이에서 뼈 회전을 보간하며, 이를 위해 TryGetSnapshotsBuffers(out var fromBuffer, out var toBuffer, out var alpha)
를 사용하여 보간할 관련 뼈 회전 상태를 찾습니다.
이 손 자세는 XRHandRemoteSkeletonDriver
(XRHands의 XRHandSkeletonDriver
서브 클래스)와 같은 네트워크 손의 컴포넌트에 의해 로컬 스켈레톤에 적용됩니다.
마지막으로, SkinnedMeshRenderer
가 이 스켈레톤을 사용하여 손을 올바르게 표시합니다.
대역폭 최적화
손 뼈 정보는 전송하기에 상당히 비쌉니다: 손 상태에는 동기화할 24개의 쿼터니언이 포함되어 있습니다.
NetworkBonesStateSync
컴포넌트는 손 뼈의 특정 회전 축, 제한된 움직임 범위 등을 사용하여 필요한 대역폭을 줄입니다. 각 뼈에 대해 원하는 정밀도 수준은 전용 HandSynchronizationScriptable
스크립트 객체에서 지정할 수 있습니다.
만약 NetworkBonesStateSync
의 handSynchronizationScriptable
속성에 제공되지 않은 경우, 기본 압축이 사용되며, 이는 애드온에서 제공된 LeftHandSynchronization
및 RightHandSynchronization
에셋에 제공된 것보다 덜 효율적입니다. 이 에셋들은 대부분의 요구를 충족하도록 구축되었습니다:
- 매우 높은 압축을 제공하며, 약 20배 적은 바이트를 사용합니다(손의 전체 뼈 회전 세트가 386 바이트의 쿼터니언 전송 대신 19 바이트에 저장됨).
- 원격 사용자의 손가락 표현에 눈에 띄는 영향을 주지 않고 압축을 수행합니다.
손 모델 전환
HandRepresentationManager
컴포넌트는 현재 사용 중인 손 추적 모드에 따라 표시되는 메시를 처리합니다:
- 컨트롤러 추적이 사용될 때 Oculus 샘플 프레임워크 손 메시를 사용하거나,
- 손가락 추적이 사용될 때
XRHandSkeletonDriver
컴포넌트 로직에 의해 애니메이션 된 뼈 스켈레톤에 의존하는 메시를 사용합니다.
이 스크립트는 로컬 하드웨어 손과 네트워크 손에 대해 두 가지 버전이 존재합니다.
로컬 하드웨어 리그 손
이 버전은 하드웨어 손에 배치된 IBonesCollecter
(예: XRHandCollectableSkeletonDriver
)에 의존합니다.
다른 기능 외에도, 손가락 추적이 사용될 때 손바닥 위치에 위치한 그랩 콜라이더와 컨트롤러 기반 추적이 사용될 때 손바닥 위치에 위치한 그랩 콜라이더 사이를 전환하는 것을 보장합니다(두 모드에서 "손의 중심" 위치는 정확히 동일하지 않음). 두 인덱스 팁 콜라이더에도 동일한 작업을 수행합니다.
참고: 현재 설정에서는 네트워크 손만 표시되도록 선택되었습니다: 하드웨어 리그의 손은 뼈 위치를 수집하고 인덱스 콜라이더 위치를 올바르게 애니메이션 하는 역할만 수행합니다. 따라서 하드웨어 손에 사용된 재료는 투명하게 설정되며(안드로이드에서는 컨트롤러 손에 투명 재료가 필요하며, 그렇지 않으면 렌더러가 비활성화된 경우 애니메이션이 뼈를 움직이지 않음), 하드웨어 손 메시는 MaterialOverrideMode
가 Override
로 설정된 덕분에 자동으로 투명하게 설정됩니다.
네트워크 리그 손
NetworkHandRepresentationManager
컴포넌트는 손 모드에 따라 표시되는 메시를 처리합니다: 컨트롤러 추적이 사용될 때 Oculus 샘플 프레임워크 손 메시를 사용하거나(손가락 추적 상태를 동기화하는 NetworkBonesStateSync
데이터를 확인하여), NetworkBonesStateSync
논리에 의존하는 메시를 사용합니다.
아바타 애드온
프로젝트에서 아바타 애드온을 사용하는 경우, 손 색상을 아바타 피부 색상에 따라 적용할 수 있습니다.
이를 수행하려면:
HandManagerAvatarRepresentationConnector
의 두 번째 줄#define AVATAR_ADDON_AVAILABLE
를 주석 처리 해제하십시오.- 각
NetworkHandRepresentationManager
및/또는HardwareHandRepresentationManager
옆에HandManagerAvatarRepresentationConnector
를 배치하십시오.
제스처 인식
손가락 추적 데이터를 사용하는 데 도움을 주기 위해, 손 자세를 분석하는 데 도움이 되는 몇 가지 헬퍼 스크립트가 제공됩니다.
FingerDrivenGesture
는 이 스크립트의 기본이며, OpenXR 손 서브시스템과의 연결을 처리하고, 추적 손실 콜백을 처리합니다.
FingerDrivenHardwareRigPose
는 사용자가 그랩, 인덱스 포인트 등을 하는지 확인하기 위해 컨트롤러 버튼으로 채워지는 손 명령을 채웁니다. 또한 핀치나 중지 및 약지로 누르기(두 옵션을 동시에 활성화할 수 있으며, 하나만(또는 없음) HardwareHand
의 isGrabbing
을 트리거 합니다)를 통해 그랩을 감지합니다.
FingerDriverBeamer
는 "총" 포즈(인덱스와 엄지손가락만 올린 상태)를 감지하여 RayBeamer
를 트리거 합니다(텔레포트 하기 위해). 활성화되면 엄지손가락이 계속 올라가 있는 한 빔은 활성 상태로 유지됩니다(다른 손가락은 더 이상 체크되지 않음).
FingerDrivenMenu
는 사용자가 짧은 시간 동안 손을 응시할 때 메뉴를 팝업 할 수 있으며, 손이 헤드셋을 향해 계속 돌려져 있는 한 메뉴는 계속 업데이트를 받습니다.
Pincher
는 터처의 서브 클래스로 핀치를 감지하고, IPinchable
인터페이스를 구현하는 객체로 핀치를 전송합니다. 이러한 객체는 TryConsumePinch
로 핀치를 "소모" 하여 단일 핀치 동작이 하나의 작업만 트리거하도록 할 수 있습니다.
데모
데모 씬은 Assets\Photon\FusionAddons\XRHandsSynchronization\Demo\Scenes\
폴더에서 찾을 수 있습니다.
종속성
- XRShared 애드온 2.0
다운로드
이 애드온의 최신 버전은 Industries 애드온 프로젝트에 포함되어 있습니다.
무료 XR 애드온 프로젝트에도 포함되어 있습니다.
지원되는 토폴로지
- 공유 모드
변경 로그
- 버전 2.0.3: Unity 2021.x와의 호환성 보장(2022.x에서 편집된 프리팹의 박스 콜라이더가 2021.x에서 열렸을 때 잘못된 크기를 가짐)
- 버전 2.0.2: define 체크 추가(Fusion이 아직 설치되지 않은 경우 처리)
- 버전 2.0.1: 투명 재료 변경(비조명 셰이더)
- 버전 2.0.0: 첫 번째 릴리스