WebXR 디바이스 API

W3C 후보 권고안 초안,

이 문서에 대한 추가 정보
이 버전:
https://www.w3.org/TR/2025/CRD-webxr-20250930/
최신 공식 버전:
https://www.w3.org/TR/webxr/
에디터스 드래프트:
https://immersive-web.github.io/webxr/
이전 버전:
히스토리:
https://www.w3.org/standards/history/webxr/
구현 보고서:
https://wpt.fyi/results/webxr?label=master&label=experimental&aligned
피드백:
GitHub
에디터:
(Google)
(Google [Mozilla, 2020년까지])
(Meta)
이전 에디터:
(Amazon [2018년까지 Microsoft])
참여하기:
이슈 등록 (오픈 이슈)
메일링 리스트 아카이브
W3C의 #immersive-web IRC

요약

이 명세서는 웹에서 가상현실(VR) 및 증강현실(AR) 기기(센서 및 헤드 마운트 디스플레이 포함)에 접근하는 지원 방법을 설명합니다.

이 문서의 상태

이 섹션은 이 문서가 발행된 시점의 상태를 설명합니다. 현재 W3C 발행물 목록과 이 기술 보고서의 최신 개정본은 W3C 표준 및 초안 인덱스에서 확인할 수 있습니다.

Immersive Web Working Group은 아직 해결되지 않은 모든 버그 리포트 목록을 관리합니다. 이 초안은 워킹 그룹에서 아직 논의 중인 일부 보류 중인 이슈들을 강조합니다. 이러한 이슈가 유효한지 여부를 포함하여, 결과에 대한 결정은 내려지지 않았습니다. 미해결 이슈에 대한 명세 텍스트 제안이 담긴 풀 리퀘스트 제출을 적극 권장합니다.

이 문서는 Immersive Web Working Group에서 Recommendation track을 사용하여 Candidate Recommendation Draft로 발행되었습니다. 이 문서는 W3C 권고안이 되는 것을 목표로 합니다.

Candidate Recommendation으로 발행되었다고 해서 W3C 및 회원사가 이를 보증한다는 뜻은 아닙니다. Candidate Recommendation Draft에는 워킹 그룹이 이후 Candidate Recommendation Snapshot에 포함하고자 하는, 이전 Candidate Recommendation에서 변경된 내용이 통합되어 있습니다. 이 문서는 초안이며, 언제든지 업데이트, 대체, 폐기될 수 있습니다. 이 문서를 진행 중인 작업 이외의 목적으로 인용하는 것은 적절하지 않습니다.

이 문서가 Proposed Recommendation 단계로 진입하기 위한 기준은, 이 명세의 모든 기능을 구현한 최소 두 개의 독립적이고 상호 운용 가능한 사용자 에이전트가 존재하는 것입니다. 이는 워킹 그룹에서 개발한 테스트 스위트에 정의된 사용자 에이전트 테스트를 통과함으로써 확인됩니다. 워킹 그룹은 진행 상황을 추적하기 위해 구현 보고서를 준비할 예정입니다.

이 문서는 W3C 특허 정책에 따라 운영되는 그룹에서 작성되었습니다. W3C는 그룹 산출물과 관련된 특허 공개의 공개 목록을 관리합니다. 해당 페이지에는 특허 공개 방법도 안내되어 있습니다. 특정 특허가 Essential Claim(s)에 해당한다고 실제로 아는 경우, W3C 특허 정책 6항에 따라 정보를 공개해야 합니다.

이 문서는 2025년 8월 18일 W3C 프로세스 문서의 적용을 받습니다.

마지막 초안 이후의 변경 사항은 변경 사항 섹션을 참조하세요.

1. 소개

가상현실(VR) 및 증강현실(AR) 애플리케이션을 가능하게 하는 하드웨어가 이제 소비자들에게 널리 제공되고 있으며, 이는 새로운 기회와 도전을 동시에 제공하는 몰입형 컴퓨팅 플랫폼을 제공합니다. 몰입형 하드웨어와 직접 상호작용할 수 있는 능력은 웹이 이 환경에서 일급 시민으로서 제대로 작동하도록 보장하는 데 필수적입니다.

몰입형 컴퓨팅은 수용할 만한 경험을 제공하기 위해 고정밀, 저지연 통신에 대한 엄격한 요구사항을 도입합니다. 이는 또한 웹과 같은 플랫폼에 고유한 보안 문제도 가져옵니다. WebXR 디바이스 API는 다양한 하드웨어 폼팩터에서 웹 개발자가 몰입감 있고, 편안하며, 안전한 애플리케이션을 만들 수 있도록 필요한 인터페이스를 제공합니다.

RelativeOrientationSensorAbsoluteOrientationSensor 와 같은 다른 웹 인터페이스는 일부 기기에서 입력을 받아 제한적인 상황에서 WebXR 디바이스 API를 폴리필하는 데 재활용될 수 있습니다. 그러나 이러한 인터페이스는 6DoF 트래킹, 헤드셋 주변기기로의 출력, 또는 트래킹 입력 디바이스와 같은 고급 몰입형 경험의 여러 기능을 지원할 수 없습니다.

1.1. 용어

이 문서에서는 XR이라는 약어를 가상현실, 증강현실 및 기타 관련 기술을 위한 하드웨어, 애플리케이션, 기법 전체를 일컫는 말로 사용합니다. 예시는 다음과 같으나 이에 국한되지 않습니다:

이들 사이의 중요한 공통점은 모두 공간 트래킹 기능을 제공하여 가상 콘텐츠의 시점을 시뮬레이션할 수 있다는 점입니다.

"XR 디바이스", "XR 애플리케이션" 등의 용어는 일반적으로 위에 언급한 모든 경우에 적용됩니다. 이 문서의 일부 내용이 특정 기기 하위 집합에만 적용되는 경우, 해당 부분에서 명확히 표시됩니다.

3DoF6DoF라는 용어는 이 문서 전반에서 XR 디바이스의 트래킹 능력을 설명하는 데 사용됩니다.

1.2. 애플리케이션 흐름

WebXR 디바이스 API를 사용하는 대부분의 애플리케이션은 다음과 같은 순서를 따릅니다:

2. 모델

2.1. XR 디바이스

XR 디바이스는 사용자에게 몰입형 콘텐츠를 제공할 수 있는 물리적 하드웨어 유닛입니다. 콘텐츠는 시각, 청각, 햅틱 또는 기타 감각 출력을 통해 사용자의 환경의 다양한 측면을 시뮬레이션하거나 보강하면 "몰입형"이라고 간주됩니다. 대부분의 경우 사용자의 공간 내 움직임을 추적하고, 사용자의 움직임에 동기화된 출력을 생성하는 것을 포함합니다. 데스크톱 클라이언트에서는 주로 헤드셋 주변기기를 의미합니다. 모바일 클라이언트에서는 뷰어 하네스와 함께 모바일 기기 자체를 의미할 수 있습니다. 스테레오 표시 기능이 없지만 더 발전된 트래킹 기능을 가진 장치를 의미할 수도 있습니다.

XR 디바이스지원 모드 목록(리스트 형태의 문자열 리스트) 을 가지며, 내포하는 값들은 해당 XR 디바이스가 지원하는 XRSessionMode 열거값입니다.

XR 디바이스는 자신의 지원 모드 목록에 있는 각 XRSessionMode에 대해 허용된 기능 집합(순서 있는 집합기능 디스크립터)을 가지며, 반드시 처음에는 빈 집합이어야 합니다.

유저 에이전트는 몰입형 XR 디바이스 목록(리스트 형태의 XR 디바이스)을 가지며, 반드시 처음에는 빈 리스트여야 합니다.

유저 에이전트는 몰입형 XR 디바이스(null 또는 XR 디바이스)을 가지며, 이는 처음에는 null이고 몰입형 XR 디바이스 목록 중 활성 XR 디바이스를 나타냅니다. 이 객체는 별도의 스레드에서 비동기적으로 갱신될 수 있습니다.

유저 에이전트는 기본 인라인 XR 디바이스를 반드시 가져야 하며, 이는 XR 디바이스이고, 내포해야 하는 값은 "inline"입니다. 기본 인라인 XR 디바이스는 어떤 포즈 정보도 제공해서는 안 되며, 포인터 이벤트로 생성된 XR 입력 소스 또는 이벤트 이외의 XR 입력 소스나 이벤트를 제공해서는 안 됩니다.

참고: 기본 인라인 XR 디바이스는 개발자의 편의를 위해 존재하며, 인라인 및 몰입형 콘텐츠 모두에 동일한 렌더링 및 입력 로직을 사용할 수 있도록 해줍니다. 기본 인라인 XR 디바이스는 페이지 내 다른 메커니즘(입력의 경우 포인터 이벤트 등)으로 이미 개발자에게 제공되는 정보 외에 별도의 정보를 노출하지 않으며, 단지 XR 중심의 형식으로 해당 값을 제공합니다.

유저 에이전트는 인라인 XR 디바이스를 반드시 가져야 하며, 이는 XR 디바이스이고, 내포해야 하는 값은 "inline"입니다. 인라인 XR 디바이스는, 제공하는 트래킹 정보가 인라인 콘텐츠에 노출되기에 적절하다면 몰입형 XR 디바이스일 수 있고, 아니면 기본 인라인 XR 디바이스일 수 있습니다.

참고: 휴대폰에서는 인라인 XR 디바이스가 자이로스코프·가속도계 등 휴대폰 내부 센서에서 유도된 포즈 정보를 제공할 수 있습니다. 유사한 센서가 없는 데스크톱/노트북에서는 인라인 XR 디바이스가 포즈 정보를 제공할 수 없으므로 기본 인라인 XR 디바이스로 대체됩니다. 이미 XR 디바이스에서 실행 중인 경우 인라인 XR 디바이스는 동일한 디바이스가 되며, 여러 를 지원할 수 있습니다. 기본 인라인 XR 디바이스가 노출하는 것 이상의 트래킹 또는 입력 기능을 제공하려면 반드시 사용자 동의가 필요합니다.

현재 몰입형 XR 디바이스 목록, 인라인 XR 디바이스, 몰입형 XR 디바이스의 값은 별도의 스레드에서 비동기적으로 갱신될 수 있습니다. 이 객체들은 병렬로 실행되지 않는 단계에서 직접 접근해서는 안 됩니다.

3. 초기화

partial interface Navigator {
  [SecureContext, SameObject] readonly attribute XRSystem xr;
};

xr 속성 getter는 반드시 해당 객체와 연결된 XRSystem 객체를 반환해야 합니다.

3.2. XRSystem

[SecureContext, Exposed=Window] interface XRSystem : EventTarget {
  // Methods
  Promise<boolean> isSessionSupported(XRSessionMode mode);
  [NewObject] Promise<XRSession> requestSession(XRSessionMode mode, optional XRSessionInit options = {});

  // Events
  attribute EventHandler ondevicechange;
};

유저 에이전트는 XRSystem 객체를 Navigator 객체가 생성될 때 만들어 그 객체에 연관시켜야 합니다.

XRSystem 객체는 API의 진입점으로, 유저 에이전트에서 사용 가능한 XR 기능을 쿼리하고 XRSession을 생성하여 XR 하드웨어와 통신을 시작하는 데 사용됩니다.

유저 에이전트는 시스템에 연결된 몰입형 XR 디바이스를 열거할 수 있어야 하며, 이때 사용 가능한 각 디바이스를 몰입형 XR 디바이스 목록에 넣어야 합니다. 이후 열거 요청 알고리즘은 반드시 캐시된 몰입형 XR 디바이스 목록을 재사용해야 합니다. 디바이스를 열거하는 과정에서 디바이스 트래킹을 초기화해서는 안 됩니다. 최초 열거 이후로 유저 에이전트는 디바이스 연결 및 연결 해제를 모니터링하며, 연결된 디바이스를 몰입형 XR 디바이스 목록에 추가하고, 연결 해제된 디바이스는 목록에서 제거해야 합니다.

몰입형 XR 디바이스 목록이 변경될 때마다 유저 에이전트는 다음 단계를 실행하여 몰입형 XR 디바이스를 선택해야 합니다:

  1. oldDevice몰입형 XR 디바이스로 둡니다.

  2. 몰입형 XR 디바이스 목록이 빈 리스트라면, 몰입형 XR 디바이스null로 설정합니다.

  3. 몰입형 XR 디바이스 목록크기가 1이라면, 몰입형 XR 디바이스몰입형 XR 디바이스 목록[0]으로 설정합니다.

  4. 몰입형 XR 디바이스를 다음과 같이 설정합니다:

    활성화된 XRSession이 있고 몰입형 XR 디바이스 목록oldDevice포함되어 있으면:

    몰입형 XR 디바이스oldDevice로 설정합니다.

    그 외:

    몰입형 XR 디바이스를 유저 에이전트가 선택한 디바이스로 설정합니다.

  5. 필요하다면 인라인 XR 디바이스몰입형 XR 디바이스로 갱신하거나, 그렇지 않으면 기본 인라인 XR 디바이스로 갱신할 수 있습니다.

  6. 기기가 처음 열거된 경우 또는 oldDevice몰입형 XR 디바이스와 같다면, 이 단계를 중단합니다.

  7. 활성 XRSession종료합니다.

  8. 작업을 큐에 추가하여 XR compatible 불리언 값을 모든 WebGLRenderingContextBase 인스턴스에 대해 false로 설정합니다.

  9. 작업을 큐에 추가하여 이벤트를 발생시키는데, devicechange라는 이름의 이벤트를 관련 전역 객체navigatorxr에 발생시킵니다.

  10. 작업을 큐에 추가하여 몰입형 XR 디바이스 또는 인라인 XR 디바이스의 변경에 영향을 받는 모든 XRPermissionStatus 객체에 대해 적절한 change 이벤트를 발생시킵니다.

참고: 이 단계들은 반드시 병렬로 실행되어야 합니다.

참고: 몰입형 XR 디바이스 목록에 여러 디바이스가 있을 때 유저 에이전트는 원하는 기준으로 몰입형 XR 디바이스를 선택할 수 있습니다. 예를 들어, 유저 에이전트는 항상 목록의 첫 번째 항목을 선택하거나, 사용자가 디바이스 우선순위를 관리할 수 있도록 UI를 제공할 수 있습니다. 기본 디바이스 선택 알고리즘이 안정적이라면 여러 브라우징 세션에 걸쳐 동일한 디바이스가 선택될 것입니다.

유저 에이전트는 다음 단계를 실행하여 몰입형 XR 디바이스가 선택되었는지 확인할 수 있습니다:

  1. 몰입형 XR 디바이스null이 아니면 몰입형 XR 디바이스를 반환하고, 이 단계를 중단합니다.

  2. 몰입형 XR 디바이스를 열거합니다.

  3. 몰입형 XR 디바이스를 선택합니다.

  4. 몰입형 XR 디바이스를 반환합니다.

참고: 이 단계들은 반드시 병렬로 실행되어야 합니다.

ondevicechange 속성은 이벤트 핸들러 IDL 속성이며, devicechange 이벤트 타입에 해당합니다.

isSessionSupported(mode) 메서드는 지정된 mode가 유저 에이전트 및 디바이스에서 지원될 수 있는지 쿼리합니다.

이 메서드가 호출되면 다음 단계를 실행해야 합니다:

  1. promise를 이 XRSystem관련 realm에서 새 Promise로 둡니다.

  2. mode"inline"이면, resolve promisetrue로 하고 반환합니다.

  3. 요청하는 문서의 origin이 "xr-spatial-tracking" permissions policy를 사용할 수 없다면, reject promise를 "SecurityError" DOMException 으로 하고 반환합니다.

  4. 세션 mode가 지원되는지 다음과 같이 확인합니다:

    유저 에이전트와 시스템이 절대 지원하지 않는 mode 세션이라면

    resolve promisefalse로 합니다.

    유저 에이전트와 시스템이 보통 지원하는 mode 세션이라면

    여기서 유저 에이전트 문자열로 구별할 수 없는 모든 인스턴스가 동일한 결과를 내는 경우에 한해 promiseresolve true로 할 수 있습니다.

    그 외의 경우

    다음 단계를 병렬로 실행합니다:

    1. device몰입형 XR 디바이스가 선택되었는지 확인의 결과로 설정합니다.

    2. device가 null이면, resolve promisefalse로 하고 이 단계를 중단합니다.

    3. device지원 모드 목록mode가 없다면, 작업을 큐에 추가하여 resolve promisefalse로 하고 이 단계를 중단합니다.

    4. 권한 요청강력한 기능 "xr-session-supported"에 대해 XRSessionSupportedPermissionDescriptormode 값을 mode로 하여 요청합니다. 결과가 "denied"라면, 작업을 큐에 추가하여 resolve promisefalse로 하고 이 단계를 중단합니다. 자세한 정보는 핑거프린팅 고려사항을 참고하세요.

    5. 작업을 큐에 추가하여 resolve promisetrue로 합니다.

  5. promise를 반환합니다.

참고: isSessionSupported()의 목적은 유저 에이전트가 XRSession을 생성할 수 있는 능력을 완벽하게 보고하는 것이 아니라, 주어진 모드 세션을 광고할지 여부를 페이지에 알리는 것입니다. 필요한 하드웨어/소프트웨어의 존재를 확인하더라도 일정 수준의 false-positive가 발생할 수 있습니다. (예: 하드웨어가 있더라도, 세션 요청 시점에 이미 다른 애플리케이션에 독점적으로 할당되어 있을 수 있습니다.)

대부분의 XR 콘텐츠 페이지는 문서 수명 주기 초기에 isSessionSupported()를 호출할 것으로 예상됩니다. 따라서 isSessionSupported() 호출 시 모달 또는 침입성 UI 표시를 피해야 합니다. isSessionSupported()는 디바이스 선택 UI를 트리거해서는 안 되며, 시스템에서 실행 중인 XR 애플리케이션을 방해해서도 안 되고, 시스템 트레이나 스토어 등 XR 관련 애플리케이션을 실행시켜서도 안 됩니다.

아래 코드는 immersive-vr 세션이 지원되는지 확인합니다.
const supported = await navigator.xr.isSessionSupported('immersive-vr');
if (supported) {
  // 'immersive-vr' 세션을 지원할 수 있습니다.
  // 페이지에서 사용자에게 지원을 안내하세요.
} else {
  // 'immersive-vr' 세션을 지원하지 않습니다.
}

XRSystem 객체는 대기 중인 몰입형 세션 불리언(초기값 false), 활성 몰입형 세션(초기값 null), 인라인 세션 목록(초기값 빈 배열)을 가집니다.

requestSession(mode, options) 메서드는 가능한 경우 주어진 mode에 대한 XRSession을 초기화하려 시도하며, 필요하다면 몰입형 모드로 진입합니다.

이 메서드가 호출되면 유저 에이전트는 다음 단계를 실행해야 합니다:

  1. promise를 이 XRSystem관련 realm에서 새 Promise로 둡니다.

  2. mode몰입형 세션 모드라면 immersivetrue로, 아니면 false로 둡니다.

  3. global object를 이 메서드가 호출된 XRSystem관련 전역 객체로 둡니다.

  4. 세션 요청이 허용되는지 다음과 같이 확인합니다:

    immersivetrue인 경우:
    1. 몰입형 세션 요청이 허용되는지 global object에 대해 확인하고, 허용되지 않으면 reject promise를 "SecurityError" DOMException 으로 하고 반환합니다.

    2. 대기 중인 몰입형 세션true이거나 활성 몰입형 세션null이 아니면, reject promise를 "InvalidStateError" DOMException 으로 하고 반환합니다.

    3. 대기 중인 몰입형 세션true로 설정합니다.

    그 외:

    인라인 세션 요청이 허용되는지 global object에 대해 확인하고, 허용되지 않으면 reject promise를 "SecurityError" DOMException 으로 하고 반환합니다.

  5. 다음 단계를 병렬로 실행합니다:

    1. requiredFeaturesoptionsrequiredFeatures로 둡니다.

    2. optionalFeaturesoptionsoptionalFeatures로 둡니다.

    3. device현재 디바이스 얻기의 결과로 mode, requiredFeatures, optionalFeatures를 전달하여 설정합니다.

    4. 작업을 큐에 추가하여 다음 단계를 실행합니다:

      1. devicenull이거나 device지원 모드 목록mode가 없다면, 다음 단계를 실행합니다:

        1. reject promise를 "NotSupportedError" DOMException으로 합니다.

        2. immersivetrue라면 대기 중인 몰입형 세션false로 설정합니다.

        3. 이 단계를 중단합니다.

      2. descriptorXRPermissionDescriptor로 초기화하며, mode, requiredFeatures, optionalFeatures로 초기화합니다.

      3. statusXRPermissionStatus로, 초기값 null로 둡니다.

      4. xr 권한 요청descriptor, status로 실행합니다.

      5. statusstate"denied"라면, 다음 단계를 실행합니다:

        1. reject promise를 "NotSupportedError" DOMException으로 합니다.

        2. immersivetrue라면 대기 중인 몰입형 세션false로 설정합니다.

        3. 이 단계를 중단합니다.

      6. grantedstatusgranted에서 얻은 집합으로 둡니다.

      7. session을 이 XRSystem관련 realm에서 XRSession 객체로 둡니다.

      8. 세션 초기화session, mode, granted, device로 실행합니다.

      9. 활성 몰입형 세션을 필요에 따라 다음과 같이 설정합니다:

        immersivetrue인 경우:

        활성 몰입형 세션session으로, 대기 중인 몰입형 세션false로 설정합니다.

        그 외:

        session인라인 세션 목록에 추가합니다.

      10. resolve promisesession으로 합니다.

      11. 작업을 큐에 추가하여 다음 단계를 실행합니다:

        참고: 이 단계는 초기 inputsourceschange 이벤트가 초기 세션 resolve 이후 발생하도록 보장합니다.
        1. sessionpromise resolved 플래그를 true로 설정합니다.

        2. sourcessession에 연결된 기존 입력 소스로 둡니다.

        3. sources가 비어있지 않으면 다음 단계를 실행합니다:

          1. session활성 XR 입력 소스 목록sources로 설정합니다.

          2. XRInputSourcesChangeEventinputsourceschange 이벤트를 session에 대해 addedsources로 하여 발생시킵니다.

  6. promise를 반환합니다.

현재 디바이스 얻기XRSessionMode mode, requiredFeatures, optionalFeatures에 대해 유저 에이전트가 다음 단계를 실행해야 합니다:

  1. 다음 중 하나로 device를 선택합니다:

    mode몰입형 세션 모드인 경우:

    device몰입형 XR 디바이스가 선택되었는지 확인의 결과로 설정합니다.

    requiredFeatures 또는 optionalFeatures가 비어 있지 않다면:

    device인라인 XR 디바이스로 설정합니다.

    그 외:

    device기본 인라인 XR 디바이스로 설정합니다.

  2. device를 반환합니다.

참고: 이 단계들은 반드시 병렬로 실행되어야 합니다.

아래 코드는 immersive-vr XRSession을 요청합니다.
const xrSession = await navigator.xr.requestSession("immersive-vr");

3.3. XRSessionMode

XRSessionMode 열거형은 XRSession이 동작할 수 있는 모드를 정의합니다.

enum XRSessionMode {
  "inline",
  "immersive-vr",
  "immersive-ar"
};

이 문서에서 인라인 세션이라는 용어는 inline 세션과 동의어이며, 몰입형 세션이라는 용어는 immersive-vr 또는 immersive-ar 세션을 의미합니다.

몰입형 세션은 반드시 일정 수준의 viewer 트래킹을 제공해야 하며, 콘텐츠는 사용자 및/또는 주변 환경에 대해 올바른 크기로 표시되어야 합니다. 또한, 몰입형 세션은 반드시 독점 접근 권한을 몰입형 XR 디바이스에 받아야 하며, 몰입형 세션"visible" 상태일 때 HTML 문서는 몰입형 XR 디바이스의 디스플레이에 표시되지 않으며, 다른 소스의 콘텐츠도 독점 접근 권한을 갖지 않습니다. 독점 접근은 user agent가 자신의 UI를 오버레이하는 것을 막지는 않지만, 이 UI는 최소화되어야 합니다.

참고: UA는 접근성 또는 안전을 위해 guardian 경계, 장애물, 대체 입력 소스가 없을 때 사용자의 손 등의 콘텐츠를 오버레이할 수 있습니다.

참고: 향후 명세 또는 모듈에서 몰입형 세션의 정의에 추가 세션 모드를 포함시킬 수 있습니다.

참고: 독점 접근이 제공되는 방식의 예로는 가상현실 헤드셋에 표시되는 스테레오 콘텐츠가 있습니다.

참고: 예를 들어, 몰입형 세션에서 user agent 또는 운영체제가 렌더링된 콘텐츠 위에 알림을 표시하는 식의 오버레이 UI를 보여줄 수 있습니다.

참고: 몰입형 XR 디바이스의 디스플레이에서 몰입형 세션이 실행 중일 때 HTML 문서는 표시되지 않지만, 사용자가 자신의 컴퓨터에서 2D 브라우저로 몰입형 세션에 진입하는 경우와 같이 별도의 화면에는 표시될 수 있습니다.

3.4. 기능 종속성

XRSession의 일부 기능은 여러 가지 이유로 보편적으로 제공되지 않을 수 있습니다. 그중 하나는 모든 XR 디바이스가 전체 기능 집합을 지원하지 않는다는 점입니다. 또 다른 고려사항은 일부 기능이 민감한 정보를 노출할 수 있으므로, 동작 전에 사용자 의도에 대한 명확한 신호가 필요할 수 있다는 점입니다.

기저 XR 플랫폼을 초기화하고 XRSession을 생성했는데 애플리케이션이 올바르게 동작할 수 없다는 사실을 즉시 사용자에게 알리는 것은 좋지 않은 사용자 경험이므로, 개발자는 필수 기능XRSessionInit 딕셔너리를 requestSession()에 전달하여 표시할 수 있습니다. 이 경우, 필수 기능 중 하나라도 디바이스의 한계나 사용자 의도에 대한 명확한 신호가 없는 경우 생성이 차단됩니다.

또한, 개발자는 더 뛰어난 디바이스에서 실행될 때 점진적으로 기능을 향상시키는 경험을 설계하는 것이 권장됩니다. 경험에 필수적이지는 않지만 사용할 수 있을 경우 활용할 선택적 기능XRSessionInit 딕셔너리에서 명시해야 필요한 경우 사용자 의도를 판단할 수 있습니다.

dictionary XRSessionInit {
  sequence<DOMString> requiredFeatures;
  sequence<DOMString> optionalFeatures;
};

requiredFeatures 배열에는 경험에 필요한 필수 기능이 들어 있습니다. 목록에 인식되지 않는 기능 디스크립터가 있으면 XRSession이 생성되지 않습니다. requiredFeatures 배열에 있는 기능 중 XR 디바이스에서 지원하지 않거나, 필요한 경우 사용자 의도에 대한 명확한 신호를 받지 못한 기능이 있으면 XRSession이 생성되지 않습니다.

optionalFeatures 배열에는 경험에 필요한 선택적 기능이 들어 있습니다. 목록에 인식되지 않는 기능 디스크립터가 있으면 해당 값은 무시됩니다. optionalFeatures 배열에 있는 기능은 XR 디바이스에서 지원하고, 필요한 경우 사용자 의도가 명확하면 활성화되지만, 지원하지 않더라도 XRSession 생성이 차단되지는 않습니다.

기능 목록에 지정된 값이 다음 중 하나라면 유효한 기능 디스크립터로 간주합니다:

향후 명세 개정과 추가 모듈이 허용 기능 디스크립터 목록을 확장할 수 있습니다.

참고: 기능에 추가 초기화가 필요한 경우, XRSessionInit 딕셔너리에 해당 기능을 위한 필드를 확장해야 합니다.

요청된 XRSessionMode 에 따라 특정 기능 디스크립터가 기본적으로 requiredFeatures 또는 optionalFeatures 목록에 추가됩니다. 아래 표는 각 세션 타입과 기능 목록에 연관된 기본 기능을 설명합니다:

기능 세션 목록
"viewer" 인라인 세션몰입형 세션 requiredFeatures
"local" 몰입형 세션 requiredFeatures

requiredFeaturesoptionalFeatures기능 디스크립터 전체 목록을 요청된 기능이라고 하며, 이는 XRSession에 대해 적용됩니다.

일부 기능 디스크립터요청된 기능 목록에 포함되어 있을 때 권한 정책 및/또는 사용자 의도가 명확하게 확인되어야 활성화됩니다(명시적 동의 또는 묵시적 동의). 아래 표는 활성화 전에 충족되어야 하는 기능 요구사항을 설명합니다:

기능 권한 정책 필요 동의 필요
"local" "xr-spatial-tracking" 인라인 세션 은 동의 필요
"local-floor" "xr-spatial-tracking" 항상 동의 필요
"bounded-floor" "xr-spatial-tracking" 항상 동의 필요
"unbounded" "xr-spatial-tracking" 항상 동의 필요

참고: "local"몰입형 세션요청된 기능에 항상 기본 기능으로 포함되며, 따라서 모든 몰입형 세션은 반드시 명시적 동의 또는 묵시적 동의를 받아야 합니다.

요청된 기능XR 디바이스가 해당 기능을 지원할 수 있음을 전제로 세션에서만 활성화될 수 있습니다. 즉, XR 디바이스가 일부 설정에서 해당 기능을 지원함이 알려진 경우(현 설정에서 지원 여부가 아직 검증되지 않았더라도) 활성화가 가능합니다. 유저 에이전트는 더 일관된 사용자 경험을 위해 더 엄격한 제약을 적용할 수 있습니다.

참고: 예를 들어, 여러 VR 디바이스는 사용자가 이동할 수 있는 안전 경계를 구성하거나, 경계 없이 제자리 모드로 동작할 수 있습니다. 이런 기기는 "bounded-floor" XRReferenceSpace를 지원할 수 있는 것으로 간주되지만, 현재 안전 경계가 구성되어 있지 않아도 경험이 요구할 경우 사용자가 디바이스를 적절히 설정할 수 있다고 기대하기 때문입니다. 이는 유저 에이전트가 XR 디바이스를 완전히 초기화하거나 사용자의 환경을 인식할 때까지 기다리지 않고도 요청된 기능을 resolve할 수 있도록 허용합니다. 하지만, 세션 요청 시점에 안전 경계 상태를 추가 초기화 없이 알고 있다면, 안전 경계가 이미 구성되지 않은 경우 "bounded-floor" 기능을 거부할 수 있습니다.

4. 세션

4.1. XRSession

XR 하드웨어와의 모든 상호작용은 XRSession 객체를 통해 이루어지며, 이 객체는 requestSession()XRSystem 객체에서 호출해야만 얻을 수 있습니다. 세션을 성공적으로 획득하면, 이를 사용해 뷰어 포즈 폴링을 하거나, 사용자의 환경에 대한 정보를 쿼리하거나, 이미지를 사용자에게 표시할 수 있습니다.

유저 에이전트는 가능하다면 디바이스 트래킹이나 렌더링 기능을 XRSession을 획득하기 전까지 초기화하지 않아야 합니다. 이는 XR 시스템이 실제로 사용되지 않을 때 불필요하게 XR 시스템을 활성화해서 발생할 수 있는 배터리 소모 증가, 관련 유틸리티 앱이 노출되는 등 바람직하지 않은 부작용을 방지하기 위함입니다. 일부 XR 플랫폼은 트래킹 초기화 없이 하드웨어 존재를 감지할 수 있는 방법을 제공하지 않으므로, 이는 강한 권고 사항입니다.

enum XRVisibilityState {
  "visible",
  "visible-blurred",
  "hidden",
};

[SecureContext, Exposed=Window] interface XRSession : EventTarget {
  // Attributes
  readonly attribute XRVisibilityState visibilityState;
  readonly attribute float? frameRate;
  readonly attribute Float32Array? supportedFrameRates;
  [SameObject] readonly attribute XRRenderState renderState;
  [SameObject] readonly attribute XRInputSourceArray inputSources;
  [SameObject] readonly attribute XRInputSourceArray trackedSources;
  readonly attribute FrozenArray<DOMString> enabledFeatures;
  readonly attribute boolean isSystemKeyboardSupported;

  // Methods
  undefined updateRenderState(optional XRRenderStateInit state = {});
  Promise<undefined> updateTargetFrameRate(float rate);
  [NewObject] Promise<XRReferenceSpace> requestReferenceSpace(XRReferenceSpaceType type);

  unsigned long requestAnimationFrame(XRFrameRequestCallback callback);
  undefined cancelAnimationFrame(unsigned long handle);

  Promise<undefined> end();

  // Events
  attribute EventHandler onend;
  attribute EventHandler oninputsourceschange;
  attribute EventHandler onselect;
  attribute EventHandler onselectstart;
  attribute EventHandler onselectend;
  attribute EventHandler onsqueeze;
  attribute EventHandler onsqueezestart;
  attribute EventHandler onsqueezeend;
  attribute EventHandler onvisibilitychange;
  attribute EventHandler onframeratechange;
};

XRSessionmode를 가지며, 이는 XRSessionMode 값 중 하나입니다.

XRSession애니메이션 프레임을 가지며, 이는 activefalse, animationFrametrue, session이 해당 XRSession으로 초기화된 XRFrame입니다.

XRSession허용된 기능 집합을 가지며, 이는 해당 XRSession에 부여된 기능 디스크립터에 해당하는 DOMString집합입니다.

enabledFeatures 속성은 허용된 기능 집합의 기능을 DOMString의 새 배열로 반환합니다.

isSystemKeyboardSupported 속성은 XRSystemXRSession이 활성 상태일 때 시스템 키보드를 표시할 수 있는지 나타냅니다. isSystemKeyboardSupportedtrue이면, 오버레이 키보드를 트리거하는 Web API(예: focus)가 시스템 키보드를 표시합니다. XRSession은 키보드가 표시되는 동안 해당 visibility state"visible-blurred"로 설정해야 합니다.

세션 초기화를 위해 session, mode, granted, device를 받아 다음 단계를 유저 에이전트가 실행해야 합니다:

  1. sessionmodemode로 설정합니다.

  2. sessionXR 디바이스device로 설정합니다.

  3. session허용된 기능 집합granted로 설정합니다.

  4. 렌더 상태 초기화를 실행합니다.

  5. 유저 에이전트의 다른 기능에서 이미 초기화하지 않았다면, 플랫폼 특화 단계(트래킹 및 렌더링 초기화, 필요한 경우 사용자를 위한 안내 포함)를 수행합니다.

참고: 일부 기기는 활성화를 위해 추가 사용자 안내가 필요할 수 있습니다. 예를 들어, 휴대폰 기반 헤드셋에서 몰입형 모드로 진입하려면 폰을 헤드셋에 꽂아야 하고, 데스크톱 브라우저에서 외부 헤드셋에 연결되어 있다면 헤드셋을 착용해야 합니다. 이러한 안내를 표시하는 책임은 저자가 아니라 유저 에이전트에 있습니다.

여러 상황에서 세션이 종료될 수 있으며, 이는 영구적이고 되돌릴 수 없습니다. 한 번 세션이 종료되면 XR 디바이스의 트래킹 및 렌더링 기능에 다시 접근하려면 새 세션을 요청해야 합니다. 각 XRSessionended 불리언을 가지며, 초기값은 false이고, 세션이 종료되었는지를 나타냅니다.

XRSession session이 종료되면 다음 단계를 실행합니다:

  1. sessionended 값을 true로 설정합니다.

  2. 활성 몰입형 세션session과 같으면, 활성 몰입형 세션null로 설정합니다.

  3. session인라인 세션 목록에서 제거합니다.

  4. session이 반환한 미해결 promise를 InvalidStateErrorReject합니다(end()가 반환한 promise 제외).

  5. 유저 에이전트의 다른 기능이 더 이상 사용하지 않는다면, 디바이스의 트래킹 및 렌더링 기능을 종료하기 위한 플랫폼 특화 단계를 반드시 수행해야 합니다. 이 단계에는 반드시 다음이 포함됩니다:

  6. 작업 큐에 추가하여 XRSessionEvent 타입의 end 이벤트를 session에 발생시킵니다.

end() 메서드는 세션을 수동으로 종료할 수 있는 방법을 제공합니다. 호출되면 다음 단계를 반드시 실행해야 합니다:

  1. promise를 이 XRSession관련 realm에서 새 Promise로 둡니다.

  2. ended 값이 true이면, reject promise를 "InvalidStateError" DOMException 으로 하고 반환합니다.

  3. 세션 종료 단계를 this에 대해 실행합니다.

  4. 작업 큐에 추가하여 다음 단계를 실행합니다:

    1. 세션 종료와 관련된 모든 플랫폼 특화 단계가 완료될 때까지 대기합니다.

    2. resolve promise.

  5. promise를 반환합니다.

XRSession활성 렌더 상태(새 XRRenderState)와 대기 중 렌더 상태(초기값 nullXRRenderState)를 가집니다.

renderState 속성은 해당 XRSession활성 렌더 상태를 반환합니다.

XRSession최소 인라인 시야각최대 인라인 시야각을 가지며, 라디안 단위로 정의됩니다. 값은 반드시 유저 에이전트에 의해 결정되며 0 이상 PI 이하여야 합니다.

XRSession최소 근거리 클리핑 평면최대 원거리 클리핑 평면을 가지며, 미터 단위로 정의됩니다. 값은 반드시 유저 에이전트에 의해 결정되어야 하며 음수가 아니어야 합니다. 최소 근거리 클리핑 평면0.1 미만이어야 하며, 최대 원거리 클리핑 평면1000.0 이상(무한대일 수도 있음)이 권장됩니다.

유저 에이전트가 XRSession sessionXRRenderStateInit newState대기 중 레이어 상태 업데이트를 할 때 다음 단계를 실행해야 합니다:

  1. newStatelayers 값이 null이 아니면, NotSupportedError를 throw 합니다.

참고: WebXR layers module에서 이 알고리즘에 대한 새로운 의미를 도입할 예정입니다.

유저 에이전트가 XRSession sessionrate기본 프레임률 적용을 하고자 할 때 다음 단계를 반드시 실행해야 합니다:

  1. ratesession내부 기본 프레임률과 같으면 이 단계를 중단합니다.

  2. sessionended 값이 true이면 이 단계를 중단합니다.

  3. session내부 기본 프레임률rate로 설정합니다.

  4. XRSessionEvent 타입의 frameratechange 이벤트를 session에 발생시킵니다.

updateTargetFrameRate(rate) 메서드는 목표 프레임률 rateXRSession에 전달합니다.

이 메서드가 호출되면 유저 에이전트는 다음 단계를 실행해야 합니다:

  1. sessionthis로 둡니다.

  2. promisesession관련 realm에서 새 Promise로 둡니다.

  3. session내부 기본 프레임률이 없으면, reject promise를 "InvalidStateError" DOMException 으로 하고 반환합니다.

  4. sessionended 값이 true이면, reject promise를 "InvalidStateError" DOMException 으로 하고 반환합니다.

  5. ratesupportedFrameRates 에 포함되지 않으면, reject promise를 "TypeError" DOMException 으로 하고 반환합니다.

  6. session내부 목표 프레임률rate로 설정합니다.

  7. 작업 큐에 추가하여 다음 단계를 실행합니다:

    1. XR 컴포지터rate를 사용해 새로운 디스플레이 프레임률 및/또는 기본 프레임률을 계산할 수 있습니다.

    2. newrate를 새 기본 프레임률로 둡니다.

    3. 작업 큐에 추가하여 다음 단계를 실행합니다:

      1. XRSystem기본 프레임률newrate로 갱신하는 작업이 완료될 때까지 대기합니다.

      2. 기본 프레임률 적용newrate, session으로 실행합니다.

      3. resolve promise.

  8. promise를 반환합니다.

XR 컴포지터가 어떤 이유로든 기본 프레임률을 변경하면(예: "visible-blurred" 이벤트 중), 원인 이벤트가 끝난 후에는 내부 목표 프레임률을 사용해야 합니다.

updateRenderState(newState) 메서드는 활성 렌더 상태에 다음 프레임에 적용될 업데이트를 큐에 올립니다. 이 메서드에 전달된 XRRenderStateInit newState의 설정되지 않은 필드는 변경되지 않습니다.

이 메서드가 호출되면 유저 에이전트는 다음 단계를 실행해야 합니다:

  1. sessionthis로 둡니다.

  2. sessionended 값이 true이면, InvalidStateError를 throw 하고 단계를 중단합니다.

  3. newStatebaseLayersession이 아닌 XRSession으로 생성됐다면 InvalidStateError를 throw 하고 단계를 중단합니다.

  4. newStateinlineVerticalFieldOfView 가 설정되어 있고 session몰입형 세션이면, InvalidStateError를 throw 하고 단계를 중단합니다.

  5. newStatedepthNear, depthFar, inlineVerticalFieldOfView, baseLayer, layers 중 어떤 것도 설정되어 있지 않다면, 단계를 중단합니다.

  6. 대기 중 레이어 상태 업데이트session, newState로 실행합니다.

  7. activeStatesession활성 렌더 상태로 둡니다.

  8. session대기 중 렌더 상태null이면, activeState의 복사본으로 설정합니다.

  9. newStatepassthroughFullyObscured 값이 설정되어 있으면, session대기 중 렌더 상태passthroughFullyObscurednewState의 해당 값으로 설정합니다.

  10. newStatedepthNear 값이 설정되어 있으면 session대기 중 렌더 상태depthNearnewState의 해당 값으로 설정합니다.

  11. newStatedepthFar 값이 설정되어 있으면 session대기 중 렌더 상태depthFarnewState의 해당 값으로 설정합니다.

  12. newStateinlineVerticalFieldOfView 값이 설정되어 있으면 session대기 중 렌더 상태inlineVerticalFieldOfViewnewState의 해당 값으로 설정합니다.

  13. newStatebaseLayer 값이 설정되어 있으면 session대기 중 렌더 상태baseLayernewState의 해당 값으로 설정합니다.

요청 시, XRSession session은 반드시 다음 단계를 실행하여 대기 중 렌더 상태를 적용해야 합니다:

  1. activeStatesession활성 렌더 상태로 둡니다.

  2. newStatesession대기 중 렌더 상태로 둡니다.

  3. session대기 중 렌더 상태null로 설정합니다.

  4. oldBaseLayeractiveStatebaseLayer로 둡니다.

  5. oldLayersactiveStatelayers로 둡니다.

  6. 작업을 큐에 추가하여 다음 단계를 실행합니다:

    1. activeStatenewState로 설정합니다.

    2. oldBaseLayeractiveStatebaseLayer와 다르거나, oldLayersactiveStatelayers와 다르거나, 또는 어떤 레이어의 크기가 변경되었다면, 뷰포트 업데이트session에 대해 실행합니다.

    3. activeStateinlineVerticalFieldOfViewsession최소 인라인 시야각보다 작으면, activeStateinlineVerticalFieldOfViewsession최소 인라인 시야각으로 설정합니다.

    4. activeStateinlineVerticalFieldOfViewsession최대 인라인 시야각보다 크면, activeStateinlineVerticalFieldOfViewsession최대 인라인 시야각으로 설정합니다.

    5. activeStatedepthNearsession최소 근거리 클리핑 평면보다 작으면, activeStatedepthNearsession최소 근거리 클리핑 평면으로 설정합니다.

    6. activeStatedepthFarsession최대 원거리 클리핑 평면보다 크면, activeStatedepthFarsession최대 원거리 클리핑 평면으로 설정합니다.

    7. baseLayeractiveStatebaseLayer로 둡니다.

    8. activeStatecomposition enabledoutput canvas를 다음과 같이 설정합니다:

      sessionmode"inline" 이고, baseLayerXRWebGLLayer의 인스턴스이며 composition enabledfalse인 경우:

      activeStatecomposition enabled 불리언을 false로 설정합니다.

      activeStateoutput canvasbaseLayercontextcanvas로 설정합니다.

      그 외의 경우:

      activeStatecomposition enabled 불리언을 true로 설정합니다.

      activeStateoutput canvasnull로 설정합니다.

requestReferenceSpace(type) 메서드는 가능하다면 주어진 type의 새로운 XRReferenceSpace 를 생성합니다.

이 메서드가 호출되면, 유저 에이전트는 다음 단계를 실행해야 합니다:

  1. promise를 이 XRSession관련 realm에서 새 Promise로 둡니다.

  2. 다음 단계를 병렬로 실행합니다:

    1. reference space is supportedtypesession에 대해 실행한 결과가 false이면, 작업을 큐에 추가하여 reject promiseNotSupportedError로 하고 이 단계를 중단합니다.

    2. type 타입의 reference space를 트래킹하기 위해 필요한 플랫폼 리소스를 셋업합니다.

      유저 에이전트는 해당 reference space의 트래킹이 설정될 때까지 기다릴 필요 없이 requestReferenceSpace() 를 resolve할 수 있습니다. 세션이 초기 트래킹을 시도하는 동안 getViewerPose()null을 반환해도 괜찮으며, 콘텐츠는 이 시간을 스플래시 화면 등으로 활용할 수 있습니다. type"bounded-floor" 이고 경계가 아직 설정되지 않았다면, 유저 에이전트는 경계를 작은 초기 영역으로 두고 나중에 reset 이벤트로 갱신할 수 있습니다.
    3. 작업을 큐에 추가하여 다음 단계를 실행합니다:

    4. reference space 생성type, session으로 하여 referenceSpace로 둡니다.

    5. resolve promisereferenceSpace로 합니다.

  3. promise를 반환합니다.

XRSession활성 XR 입력 소스 목록(리스트 형태의 XRInputSource) 과 활성 XR 추적 소스 목록(리스트 형태의 XRInputSource) 을 가지며, 둘 다 반드시 처음에는 빈 리스트여야 합니다.

XRSessionXR 디바이스를 가지며, 이는 초기화 시 지정된 XR 디바이스입니다.

inputSources 속성은 해당 XRSession활성 XR 입력 소스 목록을 반환합니다.

trackedSources 속성은 해당 XRSession활성 XR 추적 소스 목록을 반환합니다.

유저 에이전트는 해당 XR 디바이스에 연결된 모든 XR 입력 소스를 모니터링해야 하며, XR 입력 소스가 추가, 제거, 변경될 때를 감지해야 합니다.

XRSessionpromise resolved 플래그를 가지며, 초기값은 false입니다.

참고: 이 플래그의 목적은 입력 소스 추가, 입력 소스 제거, 입력 소스 변경 알고리즘이 실제로 사용자 코드가 이벤트 리스너를 부착할 기회를 얻기 전에는 실행되지 않도록 보장하는 것입니다. 구현체는 세션 resolve 후에 입력 소스 변경 리스닝을 시작하는 방식으로 동작한다면 이 플래그가 필요하지 않을 수도 있습니다.

새로운 XR 입력 소스가 사용 가능해질 때 XRSession session에 대해 유저 에이전트는 다음 단계를 실행해야 합니다:

  1. sessionpromise resolved 플래그가 설정되지 않았다면, 이 단계를 중단합니다.

  2. added primary sources를 새로운 리스트로 둡니다.

  3. added tracked sources를 새로운 리스트로 둡니다.

  4. 각 새로운 XR 입력 소스에 대해:

    1. inputSource를 이 XRSession관련 realm에서 XRInputSource로 두고, 다음 단계를 실행합니다:

      inputSource기본 입력 소스인 경우:

      inputSourceadded primary sources에 추가합니다.

      그 외의 경우:

      inputSourceadded tracked sources에 추가합니다.

  5. 작업을 큐에 추가하여 다음 단계를 실행합니다:

    1. 확장 session활성 XR 입력 소스 목록added primary sources를 추가합니다.

    2. added primary sources가 비어있지 않으면, XRInputSourcesChangeEvent 타입의 inputsourceschange 이벤트를 session에 대해 addedadded primary sources로 하여 발생시킵니다.

    3. 확장 session활성 XR 추적 소스 목록added tracked sources를 추가합니다.

    4. added tracked sources가 비어있지 않으면, XRInputSourcesChangeEvent 타입의 trackedsourceschange 이벤트를 session에 대해 addedadded tracked sources로 하여 발생시킵니다.

이전에 추가된 XR 입력 소스가 더 이상 사용 가능하지 않을 때 XRSession session에 대해, 유저 에이전트는 반드시 다음 단계를 실행해야 합니다:

  1. sessionpromise resolved 플래그가 설정되어 있지 않으면, 이 단계를 중단합니다.

  2. removed primary sources를 새로운 리스트로 둡니다.

  3. removed tracked sources를 새로운 리스트로 둡니다.

  4. 더 이상 사용 가능하지 않은 각 XR 입력 소스에 대해:

    1. inputSourcesession활성 XR 입력 소스 목록에 연결된 XR 입력 소스XRInputSource로 둔 뒤, 다음 단계를 수행합니다:

      inputSource기본 입력 소스인 경우:

      inputSourceremoved primary sources에 추가합니다.

      그 외의 경우:

      inputSourceremoved tracked sources에 추가합니다.

  5. 작업을 큐에 추가하여 다음 단계를 실행합니다:

    1. 제거 removed primary sources에 있는 각 XRInputSourcesession활성 XR 입력 소스 목록에서 제거합니다.

    2. removed primary sources가 비어있지 않으면, XRInputSourcesChangeEvent 타입의 inputsourceschange 이벤트를 session에 대해 removedremoved primary sources로 하여 발생시킵니다.

    3. 제거 removed tracked sources에 있는 각 XRInputSourcesession활성 XR 추적 소스 목록에서 제거합니다.

    4. removed tracked sources가 비어있지 않으면, XRInputSourcesChangeEvent 타입의 trackedsourceschange 이벤트를 session에 대해 removedremoved tracked sources로 하여 발생시킵니다.

참고: 유저 에이전트는 입력 소스가 위치와 방향 트래킹을 모두 일시적으로 잃을 때 이 이벤트를 발생시킬 수 있습니다. 이는 물리적 핸드헬드 컨트롤러 입력 소스에만 권장되며, 트래킹 손 입력 소스의 경우 자주 발생하므로 이 이벤트를 발생시키는 것은 권장되지 않고, 트래커 오브젝트 입력 소스의 경우도 애플리케이션이 신원 개념을 유지하기 어렵게 하므로 권장되지 않습니다.

handedness, targetRayMode, profiles, gripSpace 의 존재, 그리고 기본 입력 소스 또는 트래킹 입력 소스로서의 상태가 XR 입력 소스에서 변경되었을 때 XRSession session에 대해, 유저 에이전트는 반드시 다음 단계를 실행해야 합니다:

  1. sessionpromise resolved 플래그가 설정되어 있지 않으면, 이 단계를 중단합니다.

  2. added primary sources를 새로운 리스트로 둡니다.

  3. removed primary sources를 새로운 리스트로 둡니다.

  4. added tracked sources를 새로운 리스트로 둡니다.

  5. removed tracked sources를 새로운 리스트로 둡니다.

  6. 변경된 각 XR 입력 소스에 대해:

    1. oldInputSourcesession활성 XR 입력 소스 목록에 연결된 이전 XR 입력 소스XRInputSource로 두고, 다음 단계를 수행합니다:

      oldInputSource기본 입력 소스이거나, 상태가 기본 입력 소스에서 트래킹 입력 소스로 바뀐 경우:

      oldInputSourceremoved primary sources에 추가합니다.

      그 외의 경우:

      oldInputSourceremoved tracked sources에 추가합니다.

    2. newInputSourcesession관련 realm에서 XRInputSource로 두고, 다음 단계를 수행합니다:

      newInputSource기본 입력 소스이거나, 상태가 트래킹 입력 소스에서 기본 입력 소스로 바뀐 경우:

      newInputSourceadded primary sources에 추가합니다.

      그 외의 경우:

      newInputSourceadded tracked sources에 추가합니다.

  7. 작업을 큐에 추가하여 다음 단계를 실행합니다:

    1. 제거 removed primary sources에 있는 각 XRInputSourcesession활성 XR 입력 소스 목록에서 제거합니다.

    2. 확장 session활성 XR 입력 소스 목록added primary sources를 추가합니다.

    3. added primary sources 또는 removed primary sources가 비어있지 않으면, XRInputSourcesChangeEvent 타입의 inputsourceschange 이벤트를 session에 대해 addedadded primary sources로 하고, removedremoved primary sources로 하여 발생시킵니다.

    4. 제거 removed tracked sources에 있는 각 XRInputSourcesession활성 XR 추적 소스 목록에서 제거합니다.

    5. 확장 session활성 XR 입력 소스 목록added tracked sources를 추가합니다.

    6. added tracked sources 또는 removed tracked sources가 비어있지 않으면, XRInputSourcesChangeEvent 타입의 trackedsourceschange 이벤트를 session에 대해 addedadded tracked sources로 하고, removedremoved tracked sources로 하여 발생시킵니다.

XRSessionvisibility state 값을 가지며, 이는 enum입니다. 인라인 세션의 경우 visibility state는 반드시 DocumentvisibilityState와 일치해야 합니다. 몰입형 세션의 경우 visibility state는 아래 상태 중 세션의 상태에 가장 부합하는 값으로 설정되어야 합니다.

visibilityState 속성은 XRSessionvisibility state를 반환합니다. onvisibilitychange 속성은 이벤트 핸들러 IDL 속성이며 visibilitychange 이벤트 타입에 해당합니다.

visibility state는 사용자 에이전트에 의해 XR 애니메이션 프레임 처리 중이 아닐 때 언제든 변경될 수 있으며, 사용자 에이전트는 세션 가시성이 외부에서 영향을 받는 경우를 감지할 수 있도록 XR 플랫폼을 모니터링하고, 상태를 적절히 갱신해야 합니다.

참고: XRSessionvisibility state는 반드시 HTML 문서의 가시성을 의미하지는 않습니다. 시스템 구성에 따라 몰입형 세션이 활성화되어 있어도 페이지는 계속 보일 수 있습니다(예: PC에 연결된 헤드셋이 몰입형 세션의 콘텐츠를 보는 동안 모니터에 페이지가 계속 표시될 수 있음). 페이지 가시성 판단은 Page Visibility를 사용해야 합니다.

참고: XRSessionvisibility state는 테더드 세션에서 2D 콘텐츠가 여전히 보이는 경우 마우스 동작에는 영향을 주거나 제한하지 않습니다. 콘텐츠가 마우스 동작을 더 강하게 제어하려면 [pointerlock] API 사용을 고려하세요.

XRSystem에는 프레임률을 설명할 수 있는 여러 정의가 있습니다:

XRSession내부 목표 프레임률을 가질 수 있으며, 이는 목표 프레임률입니다.

XRSession내부 명목상 프레임률을 가질 수 있으며, 이는 명목상 프레임률입니다. 실제 프레임률명목상 프레임률보다 낮으면, XR 컴포지터가 리프로젝션 등 기술을 사용할 수 있습니다. inline 세션에는 선택적이며 반드시 없어야 합니다.

frameRate 속성은 내부 명목상 프레임률을 반영합니다. XRSession내부 명목상 프레임률이 없으면 null을 반환합니다.

onframeratechange 속성은 이벤트 핸들러 IDL 속성이며 frameratechange 이벤트 타입에 해당합니다. XRSession명목상 프레임률이 어떤 이유로든 바뀌면, 반드시 기본 프레임률 적용을 새 명목상 프레임률과 해당 XRSession에 대해 실행해야 합니다.

supportedFrameRates 속성은 지원되는 목표 프레임률 값 목록을 반환합니다. 이 속성은 선택적이며, inline 세션이나 프레임률 제어를 지원하지 않는 XRSystem에서는 반드시 없어야 합니다. XRSessionsupportedFrameRates 속성을 지원한다면 frameRate도 반드시 지원해야 합니다.

XRSession뷰어 참조 공간을 가지며, 이는 XRReferenceSpace 타입의 "viewer"로, 단위 변환 origin offset을 가집니다.

XRSession뷰 목록을 가지며, 이는 리스트 형태의 로, XR 디바이스가 제공하는 뷰에 해당합니다. XRSessionrenderStatecomposition enabled 불리언이 false이면, 뷰 목록은 반드시 하나의 만 포함해야 합니다. 뷰 목록XRSession 중에는 불변이며, 세션 중 노출될 수 있는 모든 를 포함해야 하며, 초기에는 active하지 않을 수도 있는 보조 뷰도 포함해야 합니다.

onend 속성은 이벤트 핸들러 IDL 속성이며 end 이벤트 타입에 해당합니다.

oninputsourceschange 속성은 이벤트 핸들러 IDL 속성이며 inputsourceschange 이벤트 타입에 해당합니다.

onselectstart 속성은 이벤트 핸들러 IDL 속성이며 selectstart 이벤트 타입에 해당합니다.

onselectend 속성은 이벤트 핸들러 IDL 속성이며 selectend 이벤트 타입에 해당합니다.

onselect 속성은 이벤트 핸들러 IDL 속성이며 select 이벤트 타입에 해당합니다.

onsqueezestart 속성은 이벤트 핸들러 IDL 속성이며 squeezestart 이벤트 타입에 해당합니다.

onsqueezeend 속성은 이벤트 핸들러 IDL 속성이며 squeezeend 이벤트 타입에 해당합니다.

onsqueeze 속성은 이벤트 핸들러 IDL 속성이며 squeeze 이벤트 타입에 해당합니다.

4.2. XRRenderState

XRRenderStateXRSession의 출력이 어떻게 합성되는지에 영향을 주는 설정 가능한 값들의 집합을 나타냅니다. 주어진 XRSession에 대한 활성 렌더 상태는 프레임 경계 사이에서만 변경될 수 있으며, updateRenderState()를 통해 업데이트를 큐에 쌓을 수 있습니다.

dictionary XRRenderStateInit {
  double depthNear;
  double depthFar;
  boolean passthroughFullyObscured;
  double inlineVerticalFieldOfView;
  XRWebGLLayer? baseLayer;
  sequence<XRLayer>? layers;
};

[SecureContext, Exposed=Window] interface XRRenderState {
  readonly attribute double depthNear;
  readonly attribute double depthFar;
  readonly attribute boolean? passthroughFullyObscured;
  readonly attribute double? inlineVerticalFieldOfView;
  readonly attribute XRWebGLLayer? baseLayer;
};

XRRenderStateoutput canvas를 가지며, 이는 HTMLCanvasElement이고 초기값은 null입니다. output canvas"inline" XRSession을 위해 렌더링된 콘텐츠가 표시되는 DOM 요소입니다.

XRRenderStatecomposition enabled 불리언도 가지며, 초기값은 true입니다. XRRenderStatecomposition enabled 상태란, 렌더링 명령이 API가 제공하는 표면에서 실행되고 XR Compositor에 의해 표시될 때를 의미합니다. 만약 "inline" XRSession에 대해 렌더링이 output canvas에 직접 표시된다면, XRRenderStatecomposition enabled 플래그는 반드시 false여야 합니다.

참고: 이 시점에서 XRRenderStatecomposition enabledfalse일 때만 output canvas를 가집니다. 하지만 향후 명세에서는 미러링이나 레이어 합성과 같은 고급 사용을 지원하는 output canvas 설정 메서드가 추가될 수 있습니다.

XRRenderState 객체가 XRSession session에 대해 생성될 때, 유저 에이전트는 반드시 다음 단계를 실행하여 렌더 상태를 초기화해야 합니다:

  1. statesession관련 realm에서 XRRenderState 객체로 둡니다.

  2. statedepthNear0.1로 초기화합니다.

  3. statedepthFar1000.0으로 초기화합니다.

  4. statepassthroughFullyObscuredfalse로 초기화합니다.

  5. stateinlineVerticalFieldOfView 는 다음과 같이 초기화합니다:

    session인라인 세션인 경우:

    stateinlineVerticalFieldOfViewPI * 0.5로 초기화합니다.

    그 외의 경우:

    stateinlineVerticalFieldOfViewnull로 초기화합니다.

  6. statebaseLayernull로 초기화합니다.

depthNear 속성은 viewer로부터 근거리 클리핑 평면까지의 거리를 미터 단위로 정의합니다. depthFar 속성은 viewer로부터 원거리 클리핑 평면까지의 거리를 미터 단위로 정의합니다.

depthNeardepthFarprojectionMatrix 계산에 사용됩니다. XRViewprojectionMatrix가 렌더링에서 사용될 때 viewer로부터의 거리가 depthNeardepthFar 사이에 있는 지오메트리만 그려집니다. 또한 XRWebGLLayer의 depth buffer 값 해석에도 사용됩니다. depthNeardepthFar보다 클 수도 있습니다.

참고: 일반적으로 렌더링을 위한 원근 투영 행렬을 만들 때 개발자는 시야각, 근거리/원거리 클리핑 평면을 지정합니다. 몰입형 XR 디바이스에 표시할 때는 적절한 시야각이 광학계, 디스플레이, 카메라 조합에 의해 결정됩니다. 그러나 근거리/원거리 클리핑 평면은 렌더링되는 콘텐츠의 유형에 따라 적절한 값을 애플리케이션이 조정할 수 있습니다.

passthroughFullyObscured 속성은 작성자가 뷰포트를 가상 콘텐츠로 완전히 덮을 의도가 있음을 XRSystem에 힌트로 전달합니다. 불투명 픽셀로 뷰포트를 더 이상 덮지 않게 되면 반드시 이 플래그를 false로 다시 설정해야 합니다.

참고: XRSystem은 이 플래그를 힌트로 사용하여 일시적으로 패스스루를 비활성화할 수 있습니다. 시스루 광학계가 탑재된 디바이스에서는 사용자가 환경을 계속 볼 수 있으므로 이 플래그는 아무 효과가 없습니다.

inlineVerticalFieldOfView 속성은 "inline" XRSession의 projection matrix 계산에 사용되는 기본 수직 시야각(라디안 단위)을 정의합니다. projection matrix 계산에는 output canvas의 종횡비도 반영됩니다. 이 값은 몰입형 세션에서는 반드시 null이어야 합니다.

baseLayer 속성은 XRWebGLLayer를 정의하며, XR 컴포지터가 이 레이어로부터 이미지를 가져옵니다.

4.3. 애니메이션 프레임

XRSessionXR 디바이스의 트래킹 상태 정보를 제공하는 주요 방법은 requestAnimationFrame()XRSession 인스턴스에서 호출하여 스케줄된 콜백을 통해서입니다.

callback XRFrameRequestCallback = undefined (DOMHighResTimeStamp time, XRFrame frame);

XRFrameRequestCallback 객체는 cancelled 불리언을 가지며, 초기값은 false입니다.

XRSession애니메이션 프레임 콜백 목록(초기값은 빈 배열), 현재 실행 중인 애니메이션 프레임 콜백 목록(역시 초기값은 빈 배열), 애니메이션 프레임 콜백 식별자(초기값 0인 숫자)을 가집니다.

requestAnimationFrame(callback) 메서드는 다음 번 유저 에이전트가 디바이스의 애니메이션 프레임을 실행하고자 할 때 callback을 실행하도록 큐에 올립니다.

이 메서드가 호출되면, 유저 에이전트는 반드시 다음 단계를 실행해야 합니다:

  1. sessionthis로 둡니다.

  2. sessionended 값이 true이면, 0을 반환하고 단계를 중단합니다.

  3. session애니메이션 프레임 콜백 식별자를 1 증가시킵니다.

  4. callbacksession애니메이션 프레임 콜백 목록에, session애니메이션 프레임 콜백 식별자 현재 값과 연결하여 추가합니다.

  5. session애니메이션 프레임 콜백 식별자의 현재 값을 반환합니다.

cancelAnimationFrame(handle) 메서드는 애니메이션 프레임 콜백 식별자 handle에 해당하는 기존 애니메이션 프레임 콜백을 취소합니다.

이 메서드가 호출되면, 유저 에이전트는 반드시 다음 단계를 실행해야 합니다:

  1. sessionthis로 둡니다.

  2. session애니메이션 프레임 콜백 목록 또는 session현재 실행 중인 애니메이션 프레임 콜백 목록handle과 연결된 항목을 찾습니다.

  3. 해당 항목이 있으면, 그 항목의 cancelled 불리언을 true로 설정하고 session애니메이션 프레임 콜백 목록에서 제거합니다.

레이어 상태 확인renderState state로 할 때, 유저 에이전트는 반드시 다음 단계를 실행해야 합니다:
  1. statebaseLayernull이면 false를 반환합니다.

  2. true를 반환합니다.

참고: WebXR layers module에서 이 알고리즘의 새로운 의미가 도입될 예정입니다.

프레임이 렌더링되어야 하는지XRSession session에 대해 결정할 때, 유저 에이전트는 반드시 다음 단계를 실행해야 합니다:
  1. 레이어 상태 확인sessionrenderState로 실행한 결과가 false이면 false를 반환합니다.

  2. sessionmode"inline"이고, sessionrenderStateoutput canvasnull이면 false를 반환합니다.

  3. true를 반환합니다.

XRSession sessionviewer 상태를 타임스탬프 frameTime으로 XR 디바이스에서 갱신받았을 때, XR 애니메이션 프레임을 실행하며, 애니메이션 프레임 콜백 목록이 비어 있든 아니든 반드시 다음 단계를 실행해야 합니다:

  1. 작업을 큐에 추가하여 다음 단계를 수행합니다:

    1. now현재 고해상도 시간으로 둡니다.

    2. framesession애니메이션 프레임으로 둡니다.

    3. frametimeframeTime으로 설정합니다.

    4. framepredictedDisplayTimeframeTime으로 설정합니다.

    5. sessionmode"inline"이 아니면, framepredictedDisplayTimeXR 컴포지터가 이 XR 애니메이션 프레임을 표시할 것으로 예상되는 평균 타임스탬프로 설정합니다.

    6. 뷰 목록의 각 view에 대해, viewviewport modifiable 플래그를 true로 설정합니다.

    7. 만약 뷰 목록 내 어떤 viewactive 플래그가 이전 XR 애니메이션 프레임 이후로 변경되었으면, 뷰포트 업데이트를 실행합니다.

    8. 프레임이 렌더링되어야 한다면 session에 대해:

      1. session현재 실행 중인 애니메이션 프레임 콜백 목록session애니메이션 프레임 콜백 목록으로 설정합니다.

      2. session애니메이션 프레임 콜백 목록을 빈 배열로 설정합니다.

      3. frameactive 불리언을 true로 설정합니다.

      4. 프레임 업데이트 적용frame에 대해 실행합니다.

      5. session현재 실행 중인 애니메이션 프레임 콜백 목록의 각 entry에 대해 순서대로:

      6. entrycancelled 불리언이 true면 다음 entry로 넘어갑니다.

      7. 콜백 함수 호출entry에 대해 « now, frame » 및 "report"로 실행합니다.

      8. session현재 실행 중인 애니메이션 프레임 콜백 목록을 빈 리스트로 설정합니다.

      9. frameactive 불리언을 false로 설정합니다.

    9. session대기 중 렌더 상태null이 아니면, 대기 중 렌더 상태 적용을 실행합니다.

Window 인터페이스의 requestAnimationFrame() 메서드는 어떤 XRSession이 활성 상태이어도 변경되지 않으며, requestAnimationFrame()XRSession에서 호출해도 WindowrequestAnimationFrame() 과 아무런 상호작용이 없습니다. 활성 몰입형 세션은 페이지가 가려지는 경우 렌더링 기회에 영향을 줄 수 있습니다. 2D 브라우저 뷰가 활성 몰입형 세션 동안에도 보이는 경우(즉, 테더드 헤드셋에서 세션이 실행 중인 경우), WindowrequestAnimationFrame()requestIdleCallback() 의 콜백 타이밍은 세션의 requestAnimationFrame() 과 일치하지 않을 수 있으므로, XR 콘텐츠 렌더링에는 의존하지 않아야 합니다.

참고: 유저 에이전트는 XRSessionrequestAnimationFrame()WindowrequestAnimationFrame() 콜백 내에서 호출될 경우 개발자 콘솔에 경고를 표시할 수 있습니다. 활성 몰입형 세션렌더링 기회에 영향을 줄 수 있으며, 실행된다 하더라도 올바른 타이밍이 아닐 수 있기 때문입니다.

만약 몰입형 세션렌더링 기회를 차단한다면, Window requestAnimationFrame() 에 전달된 콜백은 세션이 활성화되어 있는 동안 처리되지 않을 수 있습니다. 이는 사용 중인 디바이스 종류에 따라 다르며, 몰입형 콘텐츠가 HTML 문서를 완전히 가리는 모바일이나 독립형(standalone) 디바이스에서 가장 자주 발생합니다. 따라서 개발자는 Window requestAnimationFrame() 콜백을 이용해 XRSession requestAnimationFrame() 콜백을 예약하거나, 그 반대(공유 렌더링 로직을 사용하더라도)도 해서는 안 됩니다. 이 가이드를 따르지 않는 애플리케이션은 일부 플랫폼에서 올바르게 동작하지 않을 수 있습니다. 아래는 이런 두 종류의 애니메이션 루프 간 전환을 원활하게 처리할 수 있는 더 효과적인 패턴 예시입니다:
let xrSession = null;

function onWindowAnimationFrame(time) {
  window.requestAnimationFrame(onWindowAnimationFrame);

  // 일부 기기(예: 데스크탑 + 테더드 헤드셋)에서는 몰입형 세션이 실행 중에도 이 콜백이 호출될 수 있습니다.
  // 두 애니메이션 루프가 동시에 렌더링하지 않도록, 세션이 종료될 때까지 이 루프에서는 그리기를 건너뜁니다.
  if (!xrSession) {
    renderFrame(time, null);
  }
}

// window 애니메이션 루프는 페이지가 로딩되자마자 바로 시작할 수 있습니다.
window.requestAnimationFrame(onWindowAnimationFrame);

function onXRAnimationFrame(time, xrFrame) {
  xrSession.requestAnimationFrame(onXRAnimationFrame);
  renderFrame(xrFrame.predictedDisplayTime, xrFrame);
}

function renderFrame(time, xrFrame) {
  // 공통 렌더링 로직.
}

// 아래 함수는 코드 내 다른 곳에서 사용자 동작에 의해 호출된다고 가정합니다.
async function startXRSession() {
  xrSession = await navigator.xr.requestSession('immersive-vr');
  xrSession.addEventListener('end', onXRSessionEnded);
  // 세션에 필요한 설정을 여기서 진행합니다.
  // 세션의 애니메이션 루프를 시작합니다.
  xrSession.requestAnimationFrame(onXRAnimationFrame);
}

function onXRSessionEnded() {
  xrSession = null;
}

HTML 문서에 렌더링하기 위해 "inline" 세션을 사용하는 애플리케이션은 별도의 애니메이션 루프 동기화 작업을 할 필요가 없습니다. 몰입형 세션이 활성화되면 유저 에이전트가 "inline" 세션의 애니메이션 루프를 자동으로 일시 중지해주기 때문입니다.

4.4. XR 컴포지터

유저 에이전트는 반드시 XR 컴포지터를 유지해야 하며, 이는 XR 디바이스로의 표시 및 프레임 타이밍을 담당합니다. XR 컴포지터는 반드시 문서에서 생성된 어떤 그래픽 컨텍스트와도 상태가 격리된 독립적인 렌더링 컨텍스트를 사용해야 합니다. 컴포지터는 페이지가 컴포지터 상태를 손상시키거나 다른 페이지/앱의 콘텐츠를 읽어오는 것을 반드시 방지해야 합니다. 또한, 컴포지터는 페이지 성능과 사용자에게 적절한 프레임률로 새로운 이미지를 표시하는 기능을 분리하기 위해 별도의 스레드나 프로세스에서 실행되어야 합니다. 컴포지터는 디바이스 메뉴 등 추가적인 디바이스 또는 유저 에이전트 UI를 렌더링된 콘텐츠 위에 합성할 수 있습니다.

참고: 향후 명세 확장에서는 같은 페이지에서 오는 여러 레이어도 컴포지터가 합성하도록 할 수 있습니다.

5. 프레임 루프

5.1. XRFrame

XRFrameXRSession에 대해 트래킹되는 모든 객체의 상태 스냅샷을 나타냅니다. 애플리케이션은 XRSession에서 requestAnimationFrame()을 호출하여 XRFrame을 획득할 수 있습니다. 콜백이 호출될 때 XRFrame이 전달됩니다. select 이벤트처럼 트래킹 상태를 전달해야 하는 이벤트 또한 XRFrame을 제공합니다.

[SecureContext, Exposed=Window] interface XRFrame {
  [SameObject] readonly attribute XRSession session;
  readonly attribute DOMHighResTimeStamp predictedDisplayTime;

  XRViewerPose? getViewerPose(XRReferenceSpace referenceSpace);
  XRPose? getPose(XRSpace space, XRSpace baseSpace);
};

XRFrameactive 불리언(초기값 false)과 animationFrame 불리언(초기값 false)을 가집니다.

session 속성은 XRFrame을 생성한 XRSession을 반환합니다.

몰입형 세션의 경우 predictedDisplayTime 속성은 이 XRFrame이 디바이스 디스플레이에 표시될 것으로 예상되는 평균 시점을 나타내는 DOMHighResTimeStamp를 반환해야 합니다. "inline" XRSession의 경우, predictedDisplayTimeXRFrameRequestCallback에 전달된 타임스탬프와 동일한 값을 반환해야 합니다.

predictedDisplayTimerequestAnimationFrame() 콜백이 예약되거나 실행된 시점이 아닌, 프레임이 표시될 때의 상태로 XR 씬을 렌더링할 수 있게 하기 위한 목적입니다.

predictedDisplayTime 은 렌더링에 할당된 시간을 추론하는 용도가 아닙니다. XR 컴포지터가 프레임 제출 후 추가적인 처리를 하기 때문입니다. 만약 경험이 predictedDisplayTime까지 처리할 수 있다고 가정하면, XR 컴포지터가 프레임을 제대로 활용하지 못해 목표 프레임률을 달성할 수 없습니다.

XRFrame은 주어진 time에 대해 모든 트래킹 객체의 상태를 나타내며, 이 상태에 대한 구체적 정보를 저장하거나 질의할 수 있습니다.

getViewerPose(referenceSpace) 메서드는 XRFrametime 시점에 referenceSpace를 기준으로 viewer의 pose를 XRViewerPose로 제공합니다.

이 메서드가 호출되면 유저 에이전트는 반드시 다음 단계를 실행해야 합니다:

  1. framethis로 둡니다.

  2. sessionframesession 객체로 둡니다.

  3. frameanimationFrame 불리언이 false면, InvalidStateError를 throw 하고 단계를 중단합니다.

  4. posesession관련 realm에서 XRViewerPose 객체로 둡니다.

  5. sessionviewer reference space의 pose를 referenceSpace 기준에서 frame이 나타내는 시점에 posepopulate하고, force emulationtrue로 설정합니다.

  6. posenull이면 null을 반환합니다.

  7. xrviews를 빈 리스트로 둡니다.

  8. offset0으로 설정한다.

  9. 활성 뷰(view) view마다 session뷰 목록(list of views)에서 다음 단계를 수행한다:

    1. xrviewsession의 relevant realm에서 새로운 XRView 객체로 생성한다.

    2. xrviewunderlying viewview로 초기화한다.

    3. xrvieweyevieweye로 초기화한다.

    4. xrviewindexoffset으로 초기화한다.

    5. xrviewframeframe으로 초기화한다.

    6. xrviewsessionsession으로 초기화한다.

    7. xrviewreference spacereferenceSpace로 초기화한다.

    8. viewtransformview offset과 동일한 session의 relevant realm에서 new XRRigidTransform 객체로 생성한다.

    9. xrviewtransform 속성을 곱셈(multiplying)의 결과값, 즉 XRViewerPosetransformviewtransform 변환을 session의 relevant realm에서 곱한 결과로 설정한다.

    10. xrviews에 xrview를 추가한다.

    11. offset1만큼 증가시킨다.

  10. poseviewsxrviews로 설정합니다.

  11. pose를 반환합니다.

getPose(space, baseSpace) 메서드는 XRFrame이 나타내는 시점의 baseSpace 기준으로 space의 pose를 XRPose로 제공합니다.

이 메서드가 호출되면 유저 에이전트는 반드시 다음 단계를 실행해야 합니다:

  1. framethis로 둡니다.

  2. poseframe관련 realm에서 XRPose 객체로 둡니다.

  3. frame이 나타내는 시점의 spacebaseSpace 기준으로 posepopulate합니다.

  4. pose를 반환합니다.

프레임 업데이트XRFrame을 받아 실행될 수 있는 알고리즘으로, 각 XRFrame마다 실행됩니다.

모든 XRSession프레임 업데이트 목록을 가지며, 이는 리스트 형태의 프레임 업데이트이고, 초기값은 빈 리스트입니다.

프레임 업데이트 적용XRFrame frame에 대해 다음 단계를 반드시 실행해야 합니다:

  1. framesession프레임 업데이트 목록의 각 frame update에 대해:

    1. frame updateframe으로 실행합니다.

참고: 이 명세는 프레임 업데이트를 정의하지 않지만, 다른 명세에서 추가할 수 있습니다.

6. 공간(Spaces)

WebXR Device API의 핵심 기능 중 하나는 공간 트래킹을 제공하는 것입니다. Space는 애플리케이션이 트래킹되는 엔티티들이 사용자의 물리적 환경 및 서로 간에 어떻게 공간적으로 연관되어 있는지 파악할 수 있게 해주는 인터페이스입니다.

6.1. XRSpace

XRSpace 는 물리적 위치에 해당하는 원점을 가진 가상 좌표계를 나타냅니다. API에서 요청하거나 전달하는 모든 공간 데이터는 항상 특정 XRSpace와 특정 XRFrame 시점에 대해 표현됩니다. 예를 들어 pose position과 같은 수치 값은 해당 space의 원점을 기준으로 한 좌표입니다. 이 인터페이스는 의도적으로 불투명하게 설계되었습니다.

[SecureContext, Exposed=Window] interface XRSpace : EventTarget {

};

XRSpace는 이를 생성한 XRSessionsession으로 가집니다.

XRSpace는 공간상의 위치와 방향 값을 가지는 네이티브 원점을 가집니다. XRSpace네이티브 원점XR 디바이스의 트래킹 시스템에 의해 업데이트될 수 있으며, 각 XRSpace는 자신의 네이티브 원점이 어떻게 트래킹되고 갱신되는지에 대한 의미를 다르게 정의할 수 있습니다.

XRSpace유효 원점(effective origin)을 가지며, 이는 XRSpace좌표계의 기준이 됩니다.

유효 space에서 네이티브 원점의 space로 변환하는 변환은 원점 오프셋(origin offset)으로 정의되며, 이는 XRRigidTransform이며 초기값은 단위 변환(identity transform)입니다. 즉, 유효 원점원점 오프셋네이티브 원점곱해 구할 수 있습니다.

XRSpace유효 원점은 다른 XRSpace의 좌표계에서만 XRPose로 관찰할 수 있으며, 이는 XRFramegetPose() 메서드가 반환합니다. XRSpace들 간의 공간적 관계는 XRFrame마다 변경될 수 있습니다.

포즈를 채우기(populate the pose) 위해, XRSpacespaceXRSpacebaseSpace를, XRFrame frame이 나타내는 시점에서 XRPose pose에 대해, 선택적 force emulation 플래그와 함께, 사용자 에이전트는 반드시 다음 단계를 실행해야 한다:

  1. frameactive 불리언이 false이면 InvalidStateError를 throw 하고 단계를 중단합니다.

  2. sessionframesession 객체로 둡니다.

  3. spacesessionsession과 다르면, InvalidStateError를 throw 하고 단계를 중단합니다.

  4. baseSpacesessionsession과 다르면, InvalidStateError를 throw 하고 단계를 중단합니다.

  5. poses may be reported 조건을 확인하고, 만족하지 않으면 SecurityError를 throw 하고 단계를 중단합니다.

  6. sessionvisibilityState"visible-blurred"이고, space 또는 baseSpaceXRInputSource에 연관되어 있다면, posenull로 하고 단계를 중단합니다.

  7. limitspacebaseSpace 사이에서 poses must be limited 결과로 둡니다.

  8. transformposetransform으로 둡니다.

  9. XR 디바이스의 트래킹 시스템에 frametime 시점의 space의 pose를 baseSpace 기준으로 질의한 다음, 다음 단계를 실행합니다:

    limitfalse이고, 트래킹 시스템이 space의 pose에 대해 baseSpace 기준으로 actively tracked 또는 statically known한 6DoF pose를 제공하는 경우:

    transformorientationview유효 원점baseSpace좌표계에서 갖는 orientation으로 설정합니다.

    transformpositionview유효 원점baseSpace좌표계에서 갖는 position으로 설정합니다.

    지원된다면, poselinearVelocityspace유효 원점baseSpace좌표계에서 갖는 선속도로 설정합니다.

    지원된다면, poseangularVelocityspace유효 원점baseSpace좌표계에서 갖는 각속도로 설정합니다.

    poseemulatedPositionfalse로 설정합니다.

    그 외 limitfalse이고, 트래킹 시스템이 3DoF pose 또는 위치가 actively tracked/static하지 않은 6DoF pose만 제공하는 경우:

    transformorientationspace유효 원점baseSpace좌표계에서 갖는 orientation으로 설정합니다.

    transformposition을 트래킹 시스템이 추정한 space유효 원점baseSpace좌표계에서 갖는 위치로 설정합니다. 이 값에는 목/팔 모델 등 계산된 오프셋이 포함될 수 있습니다. 위치 추정이 불가능하면 마지막으로 알려진 위치를 사용해야 합니다.

    poselinearVelocitynull로 설정합니다.

    poseangularVelocitynull로 설정합니다.

    poseemulatedPositiontrue로 설정합니다.

    또는, space의 pose가 과거에 baseSpace 기준으로 확인된 적이 있고, force emulationtrue인 경우:

    transformpositionspace유효 원점baseSpace좌표계에서 마지막으로 알려진 위치로 설정합니다.

    transformorientationspace유효 원점baseSpace좌표계에서 마지막으로 알려진 orientation으로 설정합니다.

    poselinearVelocitynull로 설정합니다.

    poseangularVelocitynull로 설정합니다.

    poseemulatedPosition 불리언을 true로 설정합니다.

    그 외의 경우:

    posenull로 설정합니다.

참고: XRPoseemulatedPosition 불리언은 baseSpace의 위치가 에뮬레이션되는지 여부가 아니라, space의 위치를 baseSpace 기준으로 평가할 때 에뮬레이션이 필요한지를 나타냅니다. 예를 들어, 3DoF 트래킹 컨트롤러는 targetRaySpace 또는 gripSpaceXRReferenceSpace에 대해 쿼리할 때는 emulatedPositiontrue로 보고되지만, targetRaySpace의 pose를 gripSpace 기준으로 쿼리하면 정확하게 관계가 알려져 있으므로 emulatedPositionfalse로 보고됩니다.

6.2. XRReferenceSpace

XRReferenceSpace 는 애플리케이션이 사용자의 물리적 환경과 공간적 관계를 설정할 때 사용할 수 있는 대표적인 XRSpace 중 하나입니다.

XRReferenceSpace는 대체로 XRSession의 전체 기간 동안 고정되어 있을 것으로 기대되며, 가장 흔한 예외는 세션 도중 사용자가 재설정하는 경우입니다. 각 XRReferenceSpace네이티브 원점+X는 "오른쪽", +Y는 "위", -Z는 "앞"으로 간주되는 좌표계를 정의합니다.

enum XRReferenceSpaceType {
  "viewer",
  "local",
  "local-floor",
  "bounded-floor",
  "unbounded"
};

[SecureContext, Exposed=Window]
interface XRReferenceSpace : XRSpace {
  [NewObject] XRReferenceSpace getOffsetReferenceSpace(XRRigidTransform originOffset);

  attribute EventHandler onreset;
};

XRReferenceSpacetype을 가지며, 이는 XRReferenceSpaceType입니다.

XRReferenceSpace 는 주로 requestReferenceSpace()를 호출하여 획득하며, 이때 전달한 XRReferenceSpaceType 열거형 값이 지원됨을 확인한 뒤 인스턴스가 생성됩니다. type은 해당 reference space가 어떤 트래킹 동작을 보일지를 나타냅니다:

참고: 여러 종류의 XRReferenceSpace를 지원하는 XR 시스템은 reference space의 Y축이 항상 서로 평행이고, 같은 방향을 가리키도록 유지해야 한다고 가정합니다(생성된 XRSession 전체 동안). 단, "viewer"는 플랫폼의 관행과 무관하게 동작합니다. "unbounded" reference space는 원점이 가까울 때는 Y축을 다른 reference space와 맞추어야 하지만, 사용자가 멀리 이동하면 달라질 수 있습니다.

"local" reference space를 지원하는 디바이스는 반드시 "local-floor" reference space도 지원해야 하며, 필요하면 에뮬레이션을 통해서라도 마찬가지입니다.

onreset 속성은 이벤트 핸들러 IDL 속성이며, reset 이벤트 타입에 해당합니다.

XRReferenceSpaceXRReferenceSpaceType typeXRSession session으로 요청될 때, 유저 에이전트는 반드시 다음 단계를 실행해서 reference space 생성을 해야 합니다:

  1. referenceSpace를 다음과 같이 초기화합니다:

    typebounded-floor인 경우:

    referenceSpace XRBoundedReferenceSpacesession관련 realm에서 생성합니다.

    그 외의 경우:

    referenceSpace XRReferenceSpacesession관련 realm에서 생성합니다.

  2. referenceSpacetypetype으로 초기화합니다.

  3. referenceSpacesessionsession으로 초기화합니다.

  4. referenceSpace를 반환합니다.

reference space 지원 여부 확인을 주어진 reference space type typeXRSession session에 대해 다음과 같이 실행합니다:
  1. typesession허용된 기능 집합포함되어 있지 않으면 false를 반환합니다.

  2. typeviewer이면 true를 반환합니다.

  3. typelocal 또는 local-floor이고, session몰입형 세션이면 true를 반환합니다.

  4. typelocal 또는 local-floor이고, XR 디바이스가 방향 데이터를 지원하면 true를 반환합니다.

  5. typebounded-floor이고, session몰입형 세션이면 bounded reference space 지원 여부XR 디바이스에 대해 확인한 결과를 반환합니다.

  6. typeunbounded이고, session몰입형 세션이며, XR 디바이스가 사용자 근처에서 무제한 거리까지 안정적인 트래킹을 지원하면 true를 반환합니다.

  7. false를 반환합니다.

getOffsetReferenceSpace(originOffset) 메서드가 호출되면 반드시 다음 단계를 실행해야 합니다:
  1. base를 이 메서드를 호출한 XRReferenceSpace로 둡니다.

  2. offsetSpace를 다음과 같이 초기화합니다:

    baseXRBoundedReferenceSpace의 인스턴스인 경우:

    offsetSpace XRBoundedReferenceSpacebase관련 realm에서 생성하고, offsetSpaceboundsGeometrybaseboundsGeometry로 설정하되, 각 점에 originOffsetinverse를 곱합니다.

    그 외의 경우:

    offsetSpace XRReferenceSpacebase관련 realm에서 생성합니다.

  3. offsetSpacetypebasetype으로 설정합니다.

  4. offsetSpace원점 오프셋(origin offset)base원점 오프셋originOffset곱한 값으로 base관련 realm에서 설정합니다.

  5. offsetSpace를 반환합니다.

참고: 많은 애플리케이션이 getOffsetReferenceSpace() 를 마우스, 키보드, 터치, 게임패드 입력 기반 씬 네비게이션 등에 활용할 것으로 예상됩니다. 이로 인해 getOffsetReferenceSpace() 호출이 최소 프레임마다 한 번 이상 반복될 수 있으므로, UA는 XRReferenceSpacegetOffsetReferenceSpace()로 생성할 때 매우 가볍게 동작하도록 구현하는 것이 권장됩니다.

6.3. XRBoundedReferenceSpace

XRBoundedReferenceSpaceXRReferenceSpace 를 확장하여 boundsGeometry를 포함합니다. 이는 사용자의 공간에 미리 설정된 경계(boundary)를 나타냅니다.

[SecureContext, Exposed=Window]
interface XRBoundedReferenceSpace : XRReferenceSpace {
  readonly attribute FrozenArray<DOMPointReadOnly> boundsGeometry;
};

XRBoundedReferenceSpace 의 원점(origin)은 반드시 바닥에 위치해야 하며, Y 축이 바닥에서 0이어야 합니다. XZ 위치 및 방향은 기본적으로 플랫폼의 관행에 따라 초기화되며, 일반적으로 방 중앙 근처에서 논리적인 "앞" 방향을 바라보도록 설정됩니다.

참고: 다른 XR 플랫폼에서는 bounded-floor reference space에서 제공되는 트래킹을 "룸 스케일(room scale)" 트래킹이라고 부르기도 합니다. XRBoundedReferenceSpace 는 여러 방을 포함하는 공간, 바닥이 울퉁불퉁한 공간, 매우 넓은 개방 공간을 설명하기 위한 것이 아닙니다. 그런 시나리오를 다루어야 하는 콘텐츠는 unbounded reference space를 사용해야 합니다.

XRBoundedReferenceSpace네이티브 경계 기하(native bounds geometry)를 가지며, 이는 XRBoundedReferenceSpace 주변의 경계를 나타내고, 사용자가 안전하게 이동할 수 있는 영역을 설명합니다. 이 다각형 경계는 DOMPointReadOnly 배열로 제공되며, 안전 공간의 모서리를 이루는 점들의 루프를 나타냅니다. 각 점은 네이티브 원점 기준(미터 단위) 오프셋을 설명합니다. 점들은 위에서 아래(음의 Y축 방향)로 볼 때 시계방향으로 지정되어야 합니다. 각 점의 y 값은 반드시 0이어야 하며, w 값은 반드시 1이어야 합니다. 이 경계는 바닥에서 시작해 무한히 위로 뻗는 것으로 간주할 수 있습니다. 경계의 형태는 볼록(convex) 또는 오목(concave)일 수 있습니다.

네이티브 경계 기하의 각 점은 reference space의 네이티브 원점에서 합리적인 거리 내로 제한되어야 합니다.

참고: 네이티브 경계 기하의 점들은 모든 방향에서 네이티브 원점으로부터 15미터 내로 제한하는 것이 권장됩니다.

네이티브 경계 기하의 각 점은 지문 채취 방지를 위해 양자화(quantized)되어야 하며, 양자화된 점의 값이 플랫폼이 보고하는 경계 바깥에 위치해서는 안 됩니다.

참고: 네이티브 경계 기하의 점들은 5cm 단위로 양자화하는 것이 권장됩니다.

boundsGeometry 속성은 DOMPointReadOnly 객체들의 배열이며, 각 항목은 XRBoundedReferenceSpace네이티브 경계 기하inverse origin offset으로 미리 곱한 값과 같습니다. 즉, XRBoundedReferenceSpace 좌표계에서 effective origin을 기준으로 동일한 경계를 제공합니다.

네이티브 경계 기하가 일시적으로 사용 불가한 경우(예: XR 디바이스 초기화 중, 트래킹 장기 손실, 미리 설정된 공간 간 이동 등), boundsGeometry는 반드시 빈 배열을 반환해야 합니다.

bounded reference space 지원 여부 확인을 위해 다음 단계를 실행합니다:
  1. XR 디바이스가 경계를 보고할 수 없다면 false를 반환합니다.

  2. XR 디바이스가 사용자의 물리적 바닥 높이를 식별할 수 없다면 false를 반환합니다.

  3. true를 반환합니다.

참고: 경계나 바닥 높이가 reference space 요청 시점에 아직 확인되지 않았더라도, XR 디바이스가 이를 지원하는 것으로 알려진 경우 bounded reference space가 반환될 수 있습니다.

참고: 콘텐츠는 사용자가 boundsGeometry를 넘어서 움직일 필요가 없도록 설계해야 합니다. 물리적 환경에 따라 사용자가 경계를 넘어서 이동할 수 있으며, 그 경우 위치 값이 경계 다각형 바깥이 될 수도 있습니다. 이는 오류 상황이 아니며, 페이지 콘텐츠에서 부드럽게 처리되어야 합니다.

참고: 콘텐츠가 boundsGeometry 시각화를 직접 제공하는 것은 권장되지 않습니다. 안전과 관련된 정보 제공은 유저 에이전트의 책임입니다.

7. 뷰(Views)

7.1. XRViewGeometry

XRViewGeometry 인터페이스 믹스인(XRViewGeometry interface mixin)이 포함된 객체들은 XR 디바이스가 사용자에게 이미지를 표시하는 디스플레이이거나, 실제 세계의 시각 정보를 수집하는 센서일 수 있습니다. 이러한 객체들은 뷰 기하(view geometry)를 포함합니다.

뷰 기하(view geometry)뷰어 참조 공간(viewer reference space)좌표계에서 포함 객체(containing object)스크린 공간(screen space)으로 좌표를 변환하는 데 사용되는 내재적(intrinsics) 및 외재적(extrinsics) 값들의 집합에 해당합니다.

뷰 기하포함 객체(containing object)를 가지며, 이는 뷰 기하의 데이터가 적용되는 물리적 하드웨어입니다.

뷰 기하포함 객체스크린 공간을 가지며, 이는 포함 객체가 데이터를 읽거나 그리는 2차원 평면입니다.

뷰 기하뷰 오프셋(view offset)을 가지며, 이는 XRRigidTransform으로, 포함 객체뷰어 참조 공간좌표계에서 어느 위치·방향에 있는지 설명합니다.

참고: 뷰 오프셋 값에는 어떤 제약도 없으며, 각 뷰의 방향이 다를 수도 있습니다. 이는 눈 디스플레이가 비스듬하게 배치된 HMD(head-mounted device)에서 종종 나타나며, CAVE 렌더링 등 극단적인 경우에도 발생할 수 있습니다. 이로 인해 z-정렬(z-sorting)이나 컬링(culling) 등의 처리가 눈마다 따로 필요할 수 있습니다.

뷰 기하투영 행렬(projection matrix)을 가지며, 이는 포함 객체에 렌더링할 때 사용할 투영을 설명하는 행렬입니다. 이 투영 행렬에는 전단(shearing) 등 단순한 프러스텀으로는 정확히 설명할 수 없는 변환이 포함될 수도 있습니다.

참고: 이 행렬의 역행렬은 스크린 공간에서 픽셀을 읽어내고, 포함 객체를 원점으로 하는 좌표계로 변환하는 데에 적합합니다.

[SecureContext, Exposed=Window] interface mixin XRViewGeometry {
  readonly attribute Float32Array projectionMatrix;
  [SameObject] readonly attribute XRRigidTransform transform;
};

XRViewGeometry내부 투영 행렬(internal projection matrix)을 가지며, 이는 투영 행렬을 저장합니다. 초기값은 null입니다.

참고: transform 은 여러 렌더링 라이브러리에서 카메라 객체 위치 지정에 사용할 수 있습니다. 전통적인 view 행렬이 필요하다면 transform.inverse.matrix로 얻을 수 있습니다.

projectionMatrix 속성은 해당 뷰 기하의 투영 행렬입니다. 이 행렬은 반드시 수정하거나 분해하지 말고 그대로 사용할 것을 강력히 권장합니다. 제공된 투영 행렬을 렌더링에 사용하지 않으면 화면에 왜곡이 생기거나 정렬이 어긋나 사용자에게 불편함을 줄 수 있습니다. 이 속성은 투영 행렬 얻기 알고리즘으로 계산되어야 합니다.

transform 속성은 해당 객체의 XRRigidTransform입니다. 이는 객체가 XRReferenceSpace에서 어느 위치·방향에 있는지를 나타냅니다.

투영 행렬 얻기(obtain the projection matrix)XRViewGeometry view geometry 에 대해 실행할 때:

  1. view geometry내부 투영 행렬null이 아니면 다음을 수행합니다:

    1. IsDetachedBuffer 연산을 내부 투영 행렬에 대해 실행한 결과가 false이면, view geometry내부 투영 행렬을 반환합니다.

  2. view geometry내부 투영 행렬 행렬관련 realm에서 생성하고, view geometryprojection matrix와 동일하게 만듭니다.

  3. view geometry내부 투영 행렬을 반환합니다.

7.2. XRView

XRView는 특정 프레임에 대한 XR 씬의 단일 를 설명합니다.

는 XR 디바이스가 사용자에게 이미지를 표시하는 디스플레이 또는 디스플레이 일부에 해당합니다. 뷰는 시야각, 눈 오프셋, 기타 광학 특성 등 의 물리적 출력 특성에 잘 정렬된 콘텐츠를 렌더링하는 데 필요한 모든 정보를 제공합니다. 여러 뷰가 사용자의 시야에서 겹치는 영역을 포함할 수도 있습니다. XR 디바이스가 사용하는 의 개수나 순서에 대해서, 또는 의 개수가 XRSession 전체 기간 동안 일정하리라는 보장은 없습니다.

eye라는 값을 가지며, 이는 XREye로 이 뷰가 어느 눈에 표시될지 설명합니다. 뷰에 고유하게 연관된 눈이 없으면(예: 모노 디스플레이), 이 값은 반드시 "none"이어야 합니다.

active 플래그를 가지며, 이는 XRSession의 생명주기 동안 변경될 수 있습니다. 기본 뷰(primary view)는 반드시 active 플래그가 true로 유지되어야 합니다.

참고: 많은 HMD는 왼쪽·오른쪽 눈 각각에 대해 두 개의 를 요청하지만, 대부분의 매직 윈도우 디바이스는 한 개의 만 요청합니다. 하지만 애플리케이션은 특정 뷰 구성을 가정해서는 안 됩니다. 예를 들어, 매직 윈도우 디바이스가 스테레오 출력이 가능하면 두 개의 뷰를 요청할 수 있지만, 성능상의 이유로 스테레오 모드를 끄면 한 개의 뷰로 돌아갈 수 있습니다. 비슷하게 HMD는 넓은 시야각이나 해상도가 다른 디스플레이를 위해 두 개보다 많은 뷰를 요청할 수도 있습니다.

는 내부적으로 viewport modifiable 플래그를 가지며, 이는 세션의 이 시점에서 requestViewportScale() 호출로 viewport 비율을 변경할 수 있는지를 나타냅니다. 이 플래그는 애니메이션 프레임 시작 시 true로 설정되고, getViewport()가 호출될 때 false로 바뀝니다.

는 내부적으로 requested viewport scale 값을 가지며, 이 값은 이 뷰에 요청된 viewport 비율을 나타냅니다. 초기값은 1.0이고, 시스템이 동적 viewport 스케일링을 지원한다면 requestViewportScale() 메서드로 변경 가능합니다.

는 내부적으로 current viewport scale 값을 가지며, 이는 시스템이 내부적으로 사용하는 현재 viewport 비율을 나타냅니다. 초기값은 1.0이고, getViewport()가 viewport 변경을 적용할 때 requested viewport scale과 일치하도록 갱신됩니다.

reference space를 가지며, 이는 XRReferenceSpacegetViewerPose()에서 이 를 얻을 때 사용됩니다.

참고: 동적 viewport 스케일링을 사용하면 애플리케이션이 전체 viewport 대신 스케일 팩터를 적용해 뷰포트의 일부만 렌더링할 수 있습니다. 이는 매 프레임마다 효율적으로 변경될 수 있도록 설계되었습니다. 정확한 렌더링을 위해 XR 시스템과 애플리케이션이 활성 viewport에 대해 합의하는 것이 중요합니다. 애플리케이션은 하나의 애니메이션 프레임 안에서 requestViewportScale()를 여러 번 호출할 수 있지만, 요청한 스케일은 해당 뷰의 getViewport()가 호출될 때 적용됩니다. 애니메이션 프레임 내 첫 getViewport 호출이 변경을 적용하며(즉시 적용), 그 프레임 동안 해당 viewport를 고정시키고, 이후 프레임의 기본값으로 반영합니다. 시스템은 내부 성능 휴리스틱 또는 목표 프레임률에 따라 recommendedViewportScale 속성을 통해 권장값을 제공할 수도 있습니다.

enum XREye {
  "none",
  "left",
  "right"
};

[SecureContext, Exposed=Window] interface XRView {
  readonly attribute XREye eye;
  readonly attribute unsigned long index;
  readonly attribute double? recommendedViewportScale;

  undefined requestViewportScale(double? scale);
};

XRView includes XRViewGeometry;

transform은 해당 reference space의 좌표계로 제공됩니다.

eye 속성은 해당 eye 정보를 설명합니다. 이 속성의 주요 목적은 미리 렌더링된 스테레오 콘텐츠가 각각의 눈에 맞는 콘텐츠를 정확히 표시하도록 보장하는 것입니다.

index 속성은 XRViewgetViewerPose()에 의해 views 배열에서 반환될 때의 오프셋을 나타냅니다.

옵션인 recommendedViewportScale 속성에는, UA가 requestViewportScale()에 사용할 것을 권장하는 viewport 스케일 값이 들어 있습니다. 시스템이 권장값 휴리스틱이나 메서드를 구현하지 않으면 null이 되고, null이 아니면 반드시 0.0보다 크고 1.0 이하의 수치여야 하며, 양자화(quantized)되어 세밀한 성능 또는 GPU 사용률 정보 노출을 방지해야 합니다.

참고: 권장 viewport scale은 가능한 값 목록 중 가장 가까운 값으로 반올림(quantize)하고, 경계값 근처에서는 히스테리시스(hysteresis)를 적용해 순간 변화가 일어나지 않게 하는 것이 좋습니다. (이로 인해 빠른 scale 변화가 시각적으로 거슬리거나 불편하지 않게 할 수 있습니다.)

XRViewsession을 가지며, 이는 해당 뷰를 생성한 XRSession입니다.

XRViewframe을 가지며, 이는 해당 뷰를 생성한 XRFrame입니다.

XRViewunderlying view를 가지며, 이는 이 객체가 나타내는 실제 입니다.

requestViewportScale(scale) 메서드는 이 뷰포트의 requested viewport scale을 지정한 값으로 설정하도록 유저 에이전트에 요청합니다.

이 메서드를 XRView xrview에 대해 호출하면, 유저 에이전트는 반드시 다음 단계를 실행해야 합니다:

  1. scale이 null 또는 undefined이면 단계를 중단합니다.

  2. scale이 0.0 이하이면 단계를 중단합니다.

  3. scale이 1.0보다 크면 scale을 1.0으로 설정합니다.

  4. viewxrviewunderlying view로 둡니다.

  5. viewrequested viewport scale 값을 scale로 설정합니다.

참고: 이 메서드는 null 또는 undefined인 scale 값을 무시하므로, 시스템에서 권장값을 제공하지 않더라도 view.requestViewportScale(view.recommendedViewportScale)를 안전하게 사용할 수 있습니다.

active 플래그가 뷰 목록의 어떤 에서든 변경되면, XRSession session에 대해 다음 단계를 실행하여 뷰포트 업데이트를 할 수 있습니다:

  1. layerrenderStatebaseLayer로 둡니다.

  2. layernull이면 단계를 중단합니다.

  3. layerviewport 객체 목록을 빈 리스트로 설정합니다.

  4. 뷰 목록의 각 active view에 대해:

    1. viewportXRViewport로 두고, 스케일된 뷰포트 얻기viewsession에 연관된 전체 크기 뷰포트 목록에서 수행한 결과로 설정한다.

    2. Append viewportlayer뷰포트 객체 목록에 추가한다.

XRView viewXRSession session에 대해 스케일링된 뷰포트 획득(obtain a scaled viewport) 알고리즘은 다음과 같습니다:

  1. glFullSizedViewport전체 크기 뷰포트 목록에서 view에 연관된 WebGL viewport로 둡니다.

  2. scaleviewcurrent viewport scale로 둡니다.

  3. 유저 에이전트는 scale에 대해 최소 viewport scale factor를 적용해 clamp할 수 있습니다.

  4. glViewportWebGL viewport의 새 인스턴스로 둡니다.

  5. glViewportwidthglFullSizedViewportwidthscale을 곱한 값 이하의 정수로 설정합니다.

  6. glViewportwidth가 1보다 작으면 1로 설정합니다.

  7. glViewportheightglFullSizedViewportheightscale을 곱한 값 이하의 정수로 설정합니다.

  8. glViewportheight가 1보다 작으면 1로 설정합니다.

  9. glViewportxglFullSizedViewportx(포함)~glFullSizedViewportx + width - glViewportwidth(포함) 사이의 정수로 설정합니다.

  10. glViewportyglFullSizedViewporty(포함)~glFullSizedViewporty + height - glViewportheight(포함) 사이의 정수로 설정합니다.

  11. viewport XRViewportsession의 관련 realm에서 생성합니다.

  12. viewportxglViewportx로 초기화합니다.

  13. viewportyglViewporty로 초기화합니다.

  14. viewportwidthglViewportwidth로 초기화합니다.

  15. viewportheightglViewportheight로 초기화합니다.

  16. viewport를 반환합니다.

참고: 구체적인 정수 계산은 UA(유저 에이전트)에 위임됩니다. 단순히 width/height를 내림하고 x/y 오프셋을 그대로 적용하는 것도 유효하지만, 효율을 위해 2의 거듭제곱 픽셀 그리드에 맞추는 등 약간 조정할 수 있습니다. 스케일링된 뷰포트는 반드시 전체 크기 뷰포트 내에 완전히 포함되어야 하며, 위치·크기 계산은 세션 내에서 입력값이 동일하면 항상 동일한 결과를 반환해야 합니다.

7.3. 기본 뷰와 보조 뷰

는 몰입형 경험을 위해 그리기가 반드시 필요한 경우 기본 뷰(primary view)입니다. 기본 뷰XRSession 전체 기간 동안 active 상태여야 합니다.

는 콘텐츠가 해당 뷰를 렌더링하지 않아도 몰입형 경험이 가능할 때 보조 뷰(secondary view)입니다. 콘텐츠가 이 뷰를 렌더링하지 않을 경우, 유저 에이전트는 reprojection을 통해 이를 재구성할 수 있습니다. 보조 뷰는 "secondary-views" 기능이 활성화되지 않는 한 active가 될 수 없습니다.

기본 뷰의 예시로는 핸드헬드 AR 세션의 주요 단일 뷰, 헤드셋 AR/VR 세션의 주요 두 스테레오 뷰, CAVE 세션의 모든 벽 뷰 등이 있습니다.

보조 뷰의 예로는 비디오 캡처용 1인칭 관찰자 뷰, 또는 해상도와 시야가 다른 두 개의 쿼드 뷰(eye당 두 개) 등이 있습니다.

콘텐츠는 임의 개수의 뷰가 존재할 수 있음을 가정해야 하지만, 상당수 콘텐츠는 views 배열에 대한 잘못된 가정으로 인해 두 개 초과의 뷰가 주어지면 동작이 깨질 수 있습니다.

유저 에이전트가 reprojection 등 메커니즘을 통해 보조 뷰를 콘텐츠 대신 렌더링할 수 있으므로, 콘텐츠가 이런 보조 뷰를 직접 처리할 의도가 있는지, 아니면 그런 보조 뷰의 존재를 모르거나 처리하지 않을 의도인지를 구분하는 것이 바람직합니다.

이를 위해, 보조 뷰를 노출하는 유저 에이전트는 힌트로서 "secondary-views" 기능 디스크립터(feature descriptor)를 지원해야 합니다. 이 기능을 활성화한 콘텐츠는 다음을 수행해야 합니다:

"secondary-views"가 활성화된 경우, 유저 에이전트는 필요에 따라 디바이스가 지원하는 보조 뷰XRSession에 노출할 수 있습니다. 이 경우 유저 에이전트는 보조 뷰를 reprojection으로 재구성하지 않고 콘텐츠가 렌더하는 대로 사용해야 합니다.

참고: 최대 호환성을 위해 콘텐츠에서는 optionalFeatures 를 통해 "secondary-views"를 활성화하는 것이 권장됩니다.

보조 뷰의 프레임률이 더 낮을 경우, XRSession은 다음 중 하나 이상의 동작을 할 수 있습니다:

7.4. XRViewport

XRViewport 객체는 그래픽 표면의 뷰포트(사각형 영역)를 설명합니다.

[SecureContext, Exposed=Window] interface XRViewport {
  readonly attribute long x;
  readonly attribute long y;
  readonly attribute long width;
  readonly attribute long height;
};

xy 속성은 표면 원점으로부터의 오프셋을, widthheight 속성은 뷰포트의 사각형 크기를 정의합니다.

뷰포트 값의 해석은 뷰포트가 연관된 그래픽 API의 관행에 따라 달라집니다:

다음 코드는 XRView의 모든 항목을 반복하면서, 각 XRViewerPose에 대해 XRViewportXRWebGLLayer에서 가져와, 각 항목에 대해 적절한 WebGL 뷰포트를 설정하여 렌더링에 사용합니다.
xrSession.requestAnimationFrame((time, xrFrame) => {
  const viewer = xrFrame.getViewerPose(xrReferenceSpace);

  gl.bindFramebuffer(xrWebGLLayer.framebuffer);
  for (xrView of viewer.views) {
    let xrViewport = xrWebGLLayer.getViewport(xrView);
    gl.viewport(xrViewport.x, xrViewport.y, xrViewport.width, xrViewport.height);

    // WebGL draw calls will now be rendered into the appropriate viewport.
  }
});

8. 기하 프리미티브(Geometric Primitives)

8.1. 행렬(Matrices)

WebXR은 행렬(matrix)의 형태로 다양한 변환을 제공합니다. WebXR은 행렬 통신 시 WebGL의 관행을 따르며, 4x4 행렬은 16개 요소의 Float32Array (열 우선 저장, column major)로 제공되며, 행렬은 컬럼 벡터에 대해 왼쪽에서 곱하는 방식으로 적용됩니다. 이들은 WebGL의 uniformMatrix4fv 함수에 바로 전달하거나, DOMMatrix로 변환하거나, 다양한 외부 수학 라이브러리와 함께 사용할 수 있습니다.

WebXR Device API에서 반환되는 행렬은 다음과 같이 16개 요소의 Float32Array로 배치됩니다:
[a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15]

이 행렬을 DOMPointReadOnly 타입의 컬럼 벡터에 다음과 같이 변환 연산으로 적용하면:

{x:X, y:Y, z:Z, w:1}

다음 결과를 얻습니다:

a0 a4 a8  a12  *  X  =  a0 * X + a4 * Y +  a8 * Z + a12
a1 a5 a9  a13     Y     a1 * X + a5 * Y +  a9 * Z + a13
a2 a6 a10 a14     Z     a2 * X + a6 * Y + a10 * Z + a14
a3 a7 a11 a15     1     a3 * X + a7 * Y + a11 * Z + a15

8.2. 정규화(Normalization)

벡터 또는 쿼터니언을 정규화(normalize)해야 하는 알고리즘이 여럿 있으며, 이는 각 컴포넌트의 크기를 합쳐 1.0이 되도록 스케일하는 것을 의미합니다.

정규화(normalize)를 컴포넌트 리스트에 적용할 때 UA는 다음 단계를 실행해야 합니다:

  1. length를 각 컴포넌트 제곱의 합의 제곱근으로 둡니다.

  2. length0이면 InvalidStateError 를 throw하고 단계를 중단합니다.

  3. 각 컴포넌트를 length로 나눈 값으로 설정합니다.

8.3. XRRigidTransform

XRRigidTransformpositionorientation 으로 설명되는 변환(transform)입니다. XRRigidTransform 을 해석할 때 orientation 이 항상 position 보다 먼저 적용됩니다.

XRRigidTransform내부 행렬(internal matrix)을 가지며, 이는 행렬입니다.

[SecureContext, Exposed=Window]
interface XRRigidTransform {
  constructor(optional DOMPointInit position = {}, optional DOMPointInit orientation = {});
  [SameObject] readonly attribute DOMPointReadOnly position;
  [SameObject] readonly attribute DOMPointReadOnly orientation;
  readonly attribute Float32Array matrix;
  [SameObject] readonly attribute XRRigidTransform inverse;
};

XRRigidTransform(position, orientation) 생성자가 호출될 때 반드시 다음 단계를 실행해야 합니다:

  1. transform XRRigidTransform 객체로 현재 realm에서 생성합니다.

  2. transformposition DOMPointReadOnly 객체로 현재 realm에서 생성합니다.

  3. positionw 값이 1.0이 아니면 TypeError를 throw하고 단계를 중단합니다.

  4. position 또는 orientation의 값 중 하나라도 NaN 또는 infinity 등 비유한수이면 TypeError를 throw하고 단계를 중단합니다.

  5. transformpositionx 값에 position의 x dictionary 값, y 값에 y dictionary 값, z 값에 z dictionary 값, w 값에 w dictionary 값을 설정합니다.

  6. transformorientation DOMPointReadOnly 객체로 현재 realm에서 생성합니다.

  7. transformorientationx 값에 orientation의 x dictionary 값, y 값에 y dictionary 값, z 값에 z dictionary 값, w 값에 w dictionary 값을 설정합니다.

  8. transform내부 행렬null로 둡니다.

  9. transformorientationx, y, z, w 컴포넌트를 정규화합니다.

  10. transform을 반환합니다.

position 속성은 변환의 평행이동 성분을 미터 단위 3차원 점으로 나타냅니다. positionw 속성은 반드시 1.0이어야 합니다.

orientation 속성은 변환의 회전 성분을 설명하는 쿼터니언입니다. orientation 은 반드시 길이 1.0으로 정규화되어야 합니다.

matrix 속성은 positionorientation 속성으로 설명되는 변환을 행렬로 반환합니다. 이 속성은 반드시 행렬 얻기 알고리즘으로 계산되어야 합니다.

참고: 이 행렬을 컬럼 벡터에 선행 곱(premultiply)하면 orientation 에 해당하는 3D 회전이 먼저 적용되고, position 만큼 평행이동됩니다. 수학적으로는 컬럼 벡터 표기에서 M = T * R이며, Tposition 에 해당하는 평행이동 행렬이고 Rorientation 에 해당하는 회전 행렬입니다.

XRRigidTransform transform에 대해 행렬 얻기(obtain the matrix) 알고리즘은 다음과 같습니다:

  1. transform내부 행렬null이 아니면 다음을 수행합니다:

    1. IsDetachedBuffer 연산을 내부 행렬에 대해 실행한 결과가 false이면 transform내부 행렬을 반환합니다.

  2. translation행렬로, position에 해당하는 컬럼 벡터 평행이동 행렬로 둡니다. 수학적으로 position(x, y, z)이면 이 행렬은 다음과 같습니다:

    Mathematical expression for column-vector translation matrix

  3. rotation행렬로, orientation에 해당하는 컬럼 벡터 회전 행렬로 둡니다. 수학적으로 orientation 이 단위 쿼터니언 (qx, qy, qz, qw)일 때 이 행렬은 다음과 같습니다:

    Mathematical expression for column-vector rotation matrix

  4. transform내부 행렬 Float32Arraytransform의 관련 realm에서, translationrotationtranslation이 왼쪽(translation * rotation)이 되도록 곱한 결과로 설정합니다. 수학적으로 이 행렬은 다음과 같습니다:

    Mathematical expression for matrix of multiplying translation and rotation with translation on the left

  5. transform내부 행렬을 반환합니다.

inverse 속성은 XRRigidTransform transform에 대해, transform으로 변환된 객체에 다시 적용하면 원래 자세로 돌려놓는 XRRigidTransformtransform의 관련 realm에서 반환합니다. 이 속성은 lazy 평가가 권장됩니다. inverse 로 반환된 XRRigidTransforminverse는 반드시 transform을 반환해야 합니다.

XRRigidTransformposition{ x: 0, y: 0, z: 0 w: 1 }이고 orientation{ x: 0, y: 0, z: 0, w: 1 }인 경우를 단위 변환(identity transform)이라고 합니다.

XRRigidTransform BARealm realm에서 곱하기 알고리즘은 다음과 같습니다:

  1. result XRRigidTransform 객체로 realm에서 생성합니다.

  2. resultmatrix Float32Arrayrealm에서 생성, Bmatrix 를 왼쪽에서 Amatrix에 곱한 결과로 설정합니다.

  3. resultorientation DOMPointReadOnlyrealm에서 생성, resultmatrix의 좌상단 3x3 부분행렬이 나타내는 쿼터니언으로 설정합니다.

  4. resultposition DOMPointReadOnlyrealm에서 생성, resultmatrix의 4번째 컬럼에 해당하는 벡터로 설정합니다.

  5. result를 반환합니다.

resultA의 소스 공간에서 B의 목적지 공간으로의 변환입니다.

참고: 이것은 XRRigidTransform 을 구성하는 것과 같으며, orientationAB의 orientation 합성, positionA의 position을 B의 orientation으로 회전시킨 뒤 B의 position을 더한 값과 같습니다.

9. 자세(Pose)

9.1. XRPose

XRPoseXRSpace를 기준으로 한 공간 내 위치와 방향을 설명합니다.

[SecureContext, Exposed=Window] interface XRPose {
  [SameObject] readonly attribute XRRigidTransform transform;
  [SameObject] readonly attribute DOMPointReadOnly? linearVelocity;
  [SameObject] readonly attribute DOMPointReadOnly? angularVelocity;

  readonly attribute boolean emulatedPosition;
};

transform 속성은 기준 XRSpace에 대한 위치와 방향을 설명합니다.

linearVelocity 속성은 기준 XRSpace에 대해 초당 미터(m/s) 단위의 선속도를 설명합니다. 유저 에이전트가 이 값을 채울 수 없는 경우 null을 반환할 수 있습니다.

angularVelocity 속성은 기준 XRSpace에 대해 초당 라디안(rad/s) 단위의 각속도를 설명합니다. 유저 에이전트가 이 값을 채울 수 없는 경우 null을 반환할 수 있습니다.

emulatedPosition 속성은 transform 이 센서 판독에 기반한 실시간 트래킹 6DoF 자세를 나타낼 때는 false이고, position 값에 목/팔 모델 등 계산 오프셋(computed offset)이 포함될 때는 true입니다. 추정 바닥 높이XRPose계산 오프셋을 포함하는지 여부를 판단할 때 고려되지 않아야 합니다.

9.2. XRViewerPose

XRViewerPoseXRPose로, XR 디바이스가 트래킹하는 XR 씬의 뷰어(viewer)의 상태를 설명합니다. 뷰어는 트래킹된 하드웨어, 하드웨어에 대한 사용자의 머리 위치, 또는 XR 씬에서 일련의 뷰포인트를 계산하는 다른 방법을 나타낼 수 있습니다. XRViewerPose는 반드시 XRReferenceSpace를 기준으로만 질의할 수 있습니다. XRPose의 값들 외에도, 뷰포인트 및 투영 행렬을 나타내는 강체 변환(rigid transform)을 포함하는 뷰(view) 배열을 제공합니다. 이 값들은 XR 씬의 프레임을 렌더링할 때 애플리케이션에서 사용해야 합니다.

[SecureContext, Exposed=Window] interface XRViewerPose : XRPose {
  [SameObject] readonly attribute FrozenArray<XRView> views;
};

views 배열은 XR 장면의 각 뷰포인트를 설명하는 XRView들의 시퀀스이며, XRViewerPose가 질의된 XRReferenceSpace를 기준으로 합니다. 배열에 있는 XR 장면의 모든 view를 렌더링해야 XR 디바이스에 올바르게 표시됩니다. 각 XRView에는 뷰포인트와 프로젝션 행렬을 나타내는 강체 변환이 포함되어 있으며, 필요할 때 레이어에서 XRViewport를 질의하는 데 사용할 수 있습니다.

참고: XRViewerPosetransform 값은 씬의 관전자 뷰(spectator view)나 다중 사용자 상호작용용 뷰어의 그래픽적 표현 위치 지정에 사용할 수 있습니다.

10. 입력

10.1. XRInputSource

XRInputSourceXR 입력 소스를 나타내며, 이는 사용자가 뷰어와 동일한 가상 공간에서 특정 동작을 수행할 수 있도록 하는 모든 입력 메커니즘입니다. XR 입력 소스의 예로는 핸드헬드 컨트롤러, 광학적으로 트래킹되는 손, 뷰어의 포즈를 기반으로 동작하는 시선 기반 입력 방식 등이 있습니다. 전통적인 게임패드, 마우스, 키보드처럼 XR 디바이스와 명시적으로 연결되지 않은 입력 메커니즘은 XR 입력 소스로 간주해서는 안 됩니다.

enum XRHandedness {
  "none",
  "left",
  "right"
};

enum XRTargetRayMode {
  "gaze",
  "tracked-pointer",
  "screen",
  "transient-pointer"
};

[SecureContext, Exposed=Window]
interface XRInputSource {
  readonly attribute XRHandedness handedness;
  readonly attribute XRTargetRayMode targetRayMode;
  [SameObject] readonly attribute XRSpace targetRaySpace;
  [SameObject] readonly attribute XRSpace? gripSpace;
  [SameObject] readonly attribute FrozenArray<DOMString> profiles;
  readonly attribute boolean skipRendering;
};

참고: XRInputSource 인터페이스는 WebXR Gamepads Module에 의해 확장되기도 합니다.

handedness 속성은 해당 XR 입력 소스가 어느 손에 할당되어 있는지(있는 경우)를 나타냅니다. 자연스러운 handedness가 없는 입력 소스(예: HMD 장착 컨트롤러)나 handedness를 알 수 없는 경우 이 속성은 반드시 "none"으로 설정해야 합니다.

targetRayMode 속성은 타겟 레이를 생성하는 방식을 설명하며, 필요하다면 애플리케이션이 타겟 레이를 사용자에게 어떻게 보여줄지 알려줍니다.

targetRaySpace 속성은 XRSpace 타입으로, 네이티브 원점이 해당 XRInputSource의 선호 포인팅 레이(보통 -Z 축 방향)의 위치와 방향을 targetRayMode에 따라 트래킹합니다.

targetRayMode"transient-pointer"인 입력 소스의 경우, targetRaySpace는 상호작용 시작 시점의 타겟을 향하는 레이를 나타냅니다. 해당 pose는 이 XRInput의 gripSpace 내에서 정적이어야 합니다.

gripSpace 속성은 XRSpace 타입으로, 가상의 객체가 사용자의 손에 들려 있는 것처럼 렌더링되도록 해야 하는 pose를 트래킹하는 네이티브 원점을 가집니다. 사용자가 막대기를 쥐고 있다면, 이 XRSpace네이티브 원점은 손가락이 말려있는 중심에 위치하며, -Z 축은 막대기 길이 방향으로 엄지 방향을 가리킵니다. X 축은 손등에 수직이며, 오른손 손등은 +X, 왼손 손등은 -X 방향입니다. Y 축은 XZ의 관계로 정해지며, +Y는 대략 사용자의 팔 방향입니다.

skipRendering 속성은 이 입력이 화면에 보이지만 현재 세션에서 추가로 렌더링할 필요가 없음을 나타냅니다. skipRendering 이 true이고 targetRayMode가 "tracked-pointer"라면, 유저 에이전트는 해당 XR 입력 소스가 반드시 사용자에게 표시되도록 해야 합니다.

컨트롤러가 사용자에게 표시되는 예로는 디스플레이와 사용자 사이에 컨트롤러가 있거나, 디스플레이가 투명하거나, 운영체제에서 컨트롤러를 렌더링하는 경우 등이 있습니다.

skipRendering 은 컨트롤러 등 입력 소스를 별도로 렌더링하지 않아도 된다는 힌트입니다. pick ray와 커서는 여전히 렌더링되어야 합니다.

targetRayMode"transient-pointer"인 입력 소스의 경우, gripSpace는 관련된 사용자 제스처가 있다면 그 제스처에, 없다면 사용자가 제어하는 다른 space(예: ViewerSpace, gripSpace, 다른 XRInput의 targetRaySpace 등)로 설정되어야 합니다. 이를 통해 사용자가 여전히 targetRaySpace를 조작할 수 있습니다.

gripSpace 는 입력 소스가 본질적으로 트래킹 불가(예: targetRayMode"gaze" 또는 "screen"인 경우)라면 반드시 null이어야 합니다.

profiles 속성은 입력 소스의 선호 시각 표현과 동작을 모두 나타내는 리스트 형식의 input profile name들입니다.

input profile name은 공백 없는 ASCII 소문자 DOMString으로, 여러 단어는 하이픈(-)으로 결합됩니다. 장치 제조사의 용어를 가능하면 그대로 사용해 서술적 이름을 정해야 하며, 플랫폼에서 USB vendor/product ID 등 적절한 식별자를 제공한다면 사용할 수 있습니다. 단일 장치만 고유하게 식별하는 값(예: 시리얼 넘버)은 사용 금지입니다. input profile name에는 handedness 정보가 포함되면 안 됩니다. 여러 유저 에이전트가 동일 디바이스를 노출할 경우 같은 input profile name을 보고하려고 노력해야 하며, WebXR Input Profiles Registry가 관리 권장 위치입니다.

profiles는 구체성 높은 순서대로 제시됩니다. 첫 번째 이후 input profile name들은 대체 표현(더 일반적이거나 이전 버전 등)을 의미하며, 모든 profile들은 서로의 layout의 superset/subset이어야 합니다.

XRSessionmode"inline"이면, profiles는 반드시 빈 리스트여야 합니다.

유저 에이전트는 상황에 따라 적절한 generic input profile name이나 빈 리스트만 보고할 수 있습니다. 예를 들어 입력 장치를 신뢰성 있게 식별할 수 없거나, 일치하는 프로필이 없거나, 사용 장치 노출을 감추려는 경우입니다.

예를 들어 Samsung HMD Odyssey 컨트롤러는 표준 Windows Mixed Reality 컨트롤러의 변형 디자인으로, 입력 레이아웃이 동일합니다. 따라서 Samsung HMD Odyssey 컨트롤러의 profiles["samsung-odyssey", "microsoft-mixed-reality", "generic-trigger-squeeze-touchpad-thumbstick"]과 같이 될 수 있습니다. 첫 번째 프로필이 가장 구체적으로 외형을 나타내며, 두 번째는 대체, 마지막은 가장 일반적인 타입입니다. (트리거, 스퀴즈 버튼, 터치패드, 썸스틱이 있는 컨트롤러임을 의미)
Valve Index 컨트롤러는 HTC Vive 컨트롤러와 호환되지만 추가 버튼과 축이 있습니다. 이 경우 profiles["valve-index", "htc-vive", "generic-trigger-squeeze-touchpad-thumbstick"]이 될 수 있습니다. 이때 "valve-index"는 "htc-vive"의 superset이며, 외형 차이는 허용된 것으로 간주합니다. 마지막은 마찬가지로 일반 fallback입니다.
(문자열은 예시이며 실제 프로필 이름은 WebXR Input Profiles Registry에서 관리됩니다.)

참고: XRInputSource들은 XRSessioninputSources 배열에서 "라이브"로 유지됩니다. 즉, 그 값들은 인플레이스 갱신됩니다. 그러므로 한 프레임에서 XRInputSource의 속성 참조를 저장하고 이후 프레임에서 비교하는 것은 상태 변화를 감지하는 데 쓸 수 없습니다. 상태를 프레임 간 비교하려면 값을 복사해서 사용해야 합니다.

XR 입력 소스기본 입력 소스(primary input source)일 수 있으며, 이는 기본 동작(primary action)을 지원함을 의미합니다. 기본 동작은 플랫폼별 동작으로, 트리거, 터치패드, 버튼 누르기, 음성 명령, 손 제스처 등입니다. 이 때 selectstart, selectend, select 이벤트가 발생합니다. 플랫폼 가이드에 기본 입력이 정의되어 있다면 그것을, 그렇지 않으면 UA에서 임의로 선택합니다. 장치는 최소 하나의 기본 입력 소스를 지원해야 합니다.

XR 입력 소스트래킹 입력 소스(tracked input source)일 수 있으며, 이는 기본 동작을 지원하지 않는 입력입니다. 주로 pose 데이터를 제공하는 용도입니다. 참고: 트래킹 입력 소스의 예로는 사용자의 다리나 소품에 부착된 트래커, 제스처 인식이 없는 손 트래킹 등이 있습니다.

XR 입력 소스 sourceXRSession session에서 기본 동작을 시작할 때, UA는 반드시 다음 단계를 실행해야 합니다:

  1. frame XRFrame 으로 session관련 realm에서 생성, session session, time을 동작 발생 시간으로 설정합니다.

  2. 태스크 큐입력 소스 이벤트 발생 (이름: selectstart, frame frame, source source)를 추가합니다.

XR 입력 소스 sourceXRSession session에서 기본 동작을 끝낼 때 UA는 반드시 다음 단계를 실행해야 합니다:

  1. frame XRFrame 으로 session관련 realm에서 생성, session session, time을 동작 발생 시간으로 설정합니다.

  2. 태스크 큐에 다음 단계를 추가합니다:

    1. 입력 소스 이벤트 발생 (이름: select, frame frame, source source)

    2. 입력 소스 이벤트 발생 (이름: selectend, frame frame, source source)

XR 입력 소스기본 스퀴즈 동작(primary squeeze action)을 정의할 수 있습니다. 기본 스퀴즈 동작은 플랫폼별 동작으로, grip 트리거 누르기, 손 쥐기 제스처 등이며, squeezestart, squeezeend, squeeze 이벤트가 발생합니다. 플랫폼 가이드에 권장 스퀴즈 동작이 정의되어 있다면 그것을, 그렇지 않으면 UA가 임의로 선택할 수 있습니다.

XR 입력 소스 sourceXRSession session에서 기본 스퀴즈 동작을 시작할 때 UA는 반드시 다음 단계를 실행해야 합니다:

  1. frame XRFrame 으로 session관련 realm에서 생성, session session, time을 동작 발생 시간으로 설정합니다.

  2. 태스크 큐입력 소스 이벤트 발생 (이름: squeezestart, frame frame, source source)를 추가합니다.

XR 입력 소스 sourceXRSession session에서 기본 스퀴즈 동작을 끝낼 때 UA는 반드시 다음 단계를 실행해야 합니다:

  1. frame XRFrame 으로 session관련 realm에서 생성, session session, time을 동작 발생 시간으로 설정합니다.

  2. 태스크 큐에 다음 단계를 추가합니다:

    1. 입력 소스 이벤트 발생 (이름: squeeze, frame frame, source source)

    2. 입력 소스 이벤트 발생 (이름: squeezeend, frame frame, source source)

플랫폼별 동작에 따라 기본 동작 또는 기본 스퀴즈 동작이 중단/취소될 수 있습니다. 예를 들어, XR 입력 소스기본 동작 또는 기본 스퀴즈 동작이 진행 중에 XR 디바이스에서 제거될 수 있습니다.

XR 입력 소스 sourceXRSession session에서 기본 동작이 취소된 경우 UA는 반드시 다음 단계를 실행해야 합니다:

  1. frame XRFrame 으로 session관련 realm에서 생성, session session, time을 동작 발생 시간으로 설정합니다.

  2. 태스크 큐입력 소스 이벤트 발생 (XRInputSourceEvent, 이름: selectend, frame frame, source source)을 추가합니다.

XR 입력 소스 sourceXRSession session에서 기본 스퀴즈 동작이 취소된 경우 UA는 반드시 다음 단계를 실행해야 합니다:

  1. frame XRFrame 으로 session관련 realm에서 생성, session session, time을 동작 발생 시간으로 설정합니다.

  2. 태스크 큐입력 소스 이벤트 발생 (XRInputSourceEvent, 이름: squeezeend, frame frame, source source)을 추가합니다.

10.2. 일시적 입력(Transient input)

일부 XR 디바이스일시적 입력 소스(transient input sources)를 지원할 수 있으며, 이러한 경우 XR 입력 소스일시적 동작(transient action)을 수행하는 동안에만 의미를 가집니다. 즉, 기본 입력 소스기본 동작이나, 트래킹 입력 소스의 디바이스별 보조 동작(auxiliary action)을 의미합니다.

예를 들어, "inline" XRSession에서 마우스, 터치, 또는 스타일러스 입력이 발생하는 경우를 들 수 있습니다. 이 경우 반드시 XRInputSource를 일시적으로 생성해야 하며, targetRayModescreen으로 설정되어야 합니다. 이 입력은 주요 동작으로 간주되며, 주 포인터에 대해, 그리고 주 포인터가 아닌 경우에는 비주요 보조 동작으로 처리됩니다.

또 다른 예로는, 운영체제에서 사용자의 민감한 정보(예: 시선 기반 상호작용)로부터 파생된 입력 의도가 직접적으로 노출될 수 없는 경우가 있습니다. 이 경우 XRInputSource의 일시적 인스턴스가 생성되며, targetRayModetransient-pointer로 설정되어 기본 동작(primary action)으로 처리됩니다.

일시적 입력 소스일시적 동작이 지속되는 동안에만 세션의 활성 XR 입력 소스 목록에 존재합니다.

일시적 입력 소스비일시적 기본 동작 알고리즘 대신 아래 순서로 일시적 동작을 처리합니다:

일시적 입력 소스 sourceXRSession session에서 일시적 동작을 시작할 때 UA는 반드시 다음 단계를 실행해야 합니다:

  1. frame XRFrame 으로 session관련 realm에서 생성, session session, 동작 발생 시간을 기준으로 합니다.

  2. 태스크 큐에 다음 단계를 추가합니다:

    1. 필요한 경우, XR 입력 소스 동작에 의해 생성된 "pointerdown" 이벤트를 발생시킵니다.

    2. XR 입력 소스 추가활성 XR 입력 소스 목록에 수행합니다.

    3. 일시적 동작기본 동작이라면, 입력 소스 이벤트 발생 (이름: selectstart, frame frame, source source)을 실행합니다.

일시적 입력 소스 sourceXRSession session에서 일시적 동작을 끝낼 때 UA는 반드시 다음 단계를 실행해야 합니다:

  1. frame XRFrame 으로 session관련 realm에서 생성, session session, 동작 발생 시간을 기준으로 합니다.

  2. 태스크 큐에 다음 단계를 추가합니다:

    1. 일시적 동작기본 동작이라면, 입력 소스 이벤트 발생 (이름: select, frame frame, source source)을 실행합니다.

    2. 필요한 경우, XR 입력 소스 동작에 의해 생성된 "click" 이벤트를 발생시킵니다.

    3. 일시적 동작기본 동작이라면, 입력 소스 이벤트 발생 (이름: selectend, frame frame, source source)을 실행합니다.

    4. XR 입력 소스 제거활성 XR 입력 소스 목록에서 수행합니다.

    5. 필요한 경우, XR 입력 소스 동작에 의해 생성된 "pointerup" 이벤트를 발생시킵니다.

일시적 입력 소스 sourceXRSession session에서 일시적 동작이 취소된 경우 UA는 반드시 다음 단계를 실행해야 합니다:

  1. frame XRFrame 으로 session관련 realm에서 생성, session session, 동작 발생 시간을 기준으로 합니다.

  2. 태스크 큐에 다음 단계를 추가합니다:

    1. 일시적 동작기본 동작이라면, 입력 소스 이벤트 발생 (이름: selectend, frame frame, source source)을 실행합니다.

    2. XR 입력 소스 제거활성 XR 입력 소스 목록에서 수행합니다.

    3. 필요한 경우, XR 입력 소스 동작에 의해 생성된 "pointerup" 이벤트를 발생시킵니다.

10.3. XRInputSourceArray

XRInputSourceArray리스트 형태의 XRInputSource 들을 나타냅니다. frozen array type 대신, 리스트의 내용이 시간에 따라 변할 수 있는 경우(예: XRSessioninputSources 속성 등)에 사용됩니다.

[SecureContext, Exposed=Window]
interface XRInputSourceArray {
  iterable<XRInputSource>;
  readonly attribute unsigned long length;
  getter XRInputSource(unsigned long index);
};

XRInputSourceArraylength 속성은 XRInputSourceArray에 몇 개의 XRInputSource가 포함되어 있는지 나타냅니다.

XRInputSourceArray인덱스 속성 getter(indexed property getter)는 지정된 인덱스의 XRInputSource를 반환합니다.

11. 레이어(Layers)

참고: 이 명세에서는 XRWebGLLayer 레이어만 정의되어 있지만, 향후 명세 확장에서는 추가적인 레이어 타입과 해당 레이어가 사용하는 이미지 소스가 추가될 수 있습니다.

11.1. XRLayer

[SecureContext, Exposed=Window]
interface XRLayer : EventTarget {};

XRLayerXRWebGLLayer 및 향후 확장에서 도입될 기타 레이어 타입의 기본 클래스입니다.

11.2. XRWebGLLayer

XRWebGLLayer 는 WebGL 프레임버퍼를 제공하여 3D 그래픽의 하드웨어 가속 렌더링이 XR 디바이스에 표시될 수 있도록 하는 레이어입니다.

typedef (WebGLRenderingContext or
         WebGL2RenderingContext) XRWebGLRenderingContext;

dictionary XRWebGLLayerInit {
  boolean antialias = true;
  boolean depth = true;
  boolean stencil = false;
  boolean alpha = true;
  boolean ignoreDepthValues = false;
  double framebufferScaleFactor = 1.0;
};

[SecureContext, Exposed=Window]
interface XRWebGLLayer: XRLayer {
  constructor(XRSession session,
             XRWebGLRenderingContext context,
             optional XRWebGLLayerInit layerInit = {});
  // Attributes
  readonly attribute boolean antialias;
  readonly attribute boolean ignoreDepthValues;
  attribute float? fixedFoveation;

  [SameObject] readonly attribute WebGLFramebuffer? framebuffer;
  readonly attribute unsigned long framebufferWidth;
  readonly attribute unsigned long framebufferHeight;

  // Methods
  XRViewport? getViewport(XRView view);

  // Static Methods
  static double getNativeFramebufferScaleFactor(XRSession session);
};

XRWebGLLayercontext 객체를 가지며, 초기값은 null이고, 이는 WebGLRenderingContext 혹은 WebGL2RenderingContext의 인스턴스입니다.

XRWebGLLayersession을 가지며, 이는 해당 레이어를 생성한 XRSession입니다.

XRWebGLLayer(session, context, layerInit) 생성자가 호출될 때 반드시 다음 단계를 실행해야 합니다:

  1. layer XRWebGLLayer 객체로 session관련 realm에서 생성합니다.

  2. sessionended 값이 true라면 InvalidStateError 를 throw하고 단계를 중단합니다.

  3. context가 손실(lost)된 경우 InvalidStateError 를 throw하고 단계를 중단합니다.

  4. sessionimmersive session이고 contextXR compatible 불리언이 false라면, InvalidStateError 를 throw하고 단계를 중단합니다.

  5. layercontextcontext로 초기화합니다.

  6. layersessionsession으로 초기화합니다.

  7. layerignoreDepthValues 를 다음과 같이 초기화합니다:

    layerInitignoreDepthValues 값이 false이고 XR Compositor가 depth 값을 사용할 경우:

    layerignoreDepthValuesfalse로 초기화합니다.

    그 외의 경우:

    layerignoreDepthValuestrue로 초기화합니다.

  8. layercomposition enabled 불리언을 다음과 같이 초기화합니다:

    sessioninline session인 경우:

    layercomposition enabledfalse로 초기화합니다.

    그 외의 경우:

    layercomposition enabled 불리언을 true로 초기화합니다.

  9. layercomposition enabled 불리언이 true일 때:
    1. layerantialiaslayerInitantialias 값으로 초기화합니다.

    2. scaleFactorlayerInitframebufferScaleFactor로 둡니다.

    3. 여기서 user agent는 필요에 따라 scaleFactor를 조정(예: 2의 제곱수로 맞추기 등)할 수 있습니다.

    4. framebufferSize권장 WebGL 프레임버퍼 해상도에 각 width/height를 scaleFactor로 곱한 값으로 둡니다.

    5. layerframebuffer WebGLFramebuffercontext관련 realm에서, opaque framebufferframebufferSize 크기와 context, sessionsession으로, layerInitdepth, stencil, alpha 값을 사용해 생성합니다.

    6. sessionXR 디바이스에 맞는 자원(예: GPU 메모리 버퍼 등)을 할당·초기화합니다.

    7. 자원 생성에 실패하면 OperationError 를 throw하고 단계를 중단합니다.

    그 외의 경우:
    1. layerantialiaslayercontext실제 context 파라미터 antialias 값으로 초기화합니다.

    2. layerframebuffernull로 초기화합니다.

  10. layer를 반환합니다.

참고: XRWebGLLayercomposition enabled 불리언이 false로 설정된 경우, XRWebGLLayerInit 객체의 모든 값은 무시됩니다. 이미 WebGLRenderingContext의 기본 프레임버퍼가 해당 context의 실제 context 파라미터로 할당되어 있으며, 이는 재정의할 수 없기 때문입니다.

context 속성은 WebGLRenderingContext 로, 이 XRWebGLLayer 가 생성될 때 사용된 context입니다.

XRWebGLLayercomposition enabled 불리언을 가지며, 초기값은 true입니다. false로 설정되면, XRWebGLLayer 는 자체 WebGLFramebuffer를 할당해서는 안 되며, XRWebGLLayer의 모든 framebuffer 관련 속성은 context의 기본 프레임버퍼 속성을 그대로 반영해야 합니다.

framebuffer 속성은 XRWebGLLayer의 인스턴스에 대해 WebGLFramebuffer 인스턴스이며, composition enabledtrue일 때 opaque로 표시되고, 그렇지 않으면 null입니다. framebuffer 의 크기는 XRWebGLLayer 가 생성된 이후 개발자가 변경할 수 없습니다.

opaque framebuffer는 표준 WebGLFramebuffer와 동일하게 동작하지만, 다음과 같은 차이로 기본 프레임버퍼처럼 동작합니다:

참고: User agent는 true로 설정된 depthstencil 값을 반드시 존중해야 하며, 이는 WebGL에서 드로잉 버퍼 생성과 유사한 동작입니다.

opaque framebuffer에 연결된 버퍼는 최초 생성 시 또는 각 XR animation frame 처리 전 아래 표의 값으로 반드시 clear되어야 합니다. 이는 WebGL context의 기본 프레임버퍼와 동일한 동작입니다. opaque framebuffer는 WebGL context의 preserveDrawingBuffer 값과 관계없이 항상 clear됩니다.

버퍼 Clear 값
Color (0, 0, 0, 0)
Depth 1.0
Stencil 0

참고: 구현체는 개발자가 다른 프로세스의 버퍼 내용을 얻지 못한다는 보장을 할 수 있다면, opaque framebuffer의 암시적 clear 동작을 최적화해 생략할 수 있습니다. 예를 들어 개발자가 명시적으로 clear를 수행하면 암시적 clear는 필요 없습니다.

XRWebGLLayeralpha true로 생성하면 framebufferRGBA 색상 포맷의 텍스처로 지원되어야 합니다. XRWebGLLayeralpha false로 생성하면 framebufferRGB 색상 포맷의 텍스처로 지원되어야 합니다.

하지만 XR Compositorframebuffer의 버퍼 픽셀을 SRGB8_ALPHA8 또는 SRGB8 colorFormat으로 처리해야 합니다.

참고: 즉, XR CompositorRGBARGB 텍스처 백킹에 대해 감마 변환을 수행하면 안 됩니다. 그렇지 않으면 최종 렌더링된 픽셀이 너무 밝아져 일반 2D WebGLRenderingContext 렌더링과 일치하지 않습니다.

XRWebGLLayerimmersive sessionbaseLayer로 설정될 때, opaque framebuffer의 내용은 XR animation frame이 완료된 직후 immersive XR 디바이스에 표시됩니다. 단, 이전 XR animation frame 이후 아래 중 하나가 발생한 경우에 한합니다:

opaque framebufferimmersive XR 디바이스에 표시되기 전, UA는 모든 렌더링 연산이 해당 opaque framebuffer에 flush되었는지 보장해야 합니다.

XRWebGLLayertarget framebuffer를 가지며, composition enabledtrueframebuffer, 아니면 context의 기본 프레임버퍼입니다.

framebufferWidthframebufferHeight 속성은 target framebuffer의 attachments의 가로/세로 크기를 반환합니다.

antialias 속성은 target framebuffer가 UA가 선택한 방식으로 안티앨리어싱을 지원하면 true, 아니면 false입니다.

ignoreDepthValues 속성이 true면, XR Compositor는 렌더링 시 depth buffer attachment 값을 사용해서는 안 됩니다. false면 해당 depth buffer 내용이 XR Compositor에 의해 사용되며, 씬의 깊이 정보가 반영되어야 함을 의미합니다.

버퍼에 저장되는 depth 값은 0.0~1.0 사이여야 하며, 0.0depthNear, 1.0depthFar 거리를 의미합니다. 중간 값은 선형 보간됩니다(WebGL 기본 동작, 자세한 내용은 depthRange 함수 참고).

참고: 씬의 depth buffer가 compositor에서 사용 가능하면, 일부 플랫폼에서 reprojection 품질이나 시각적 편안함 개선이 가능합니다.

fixedFoveation 속성은 XR Compositor의 foveation(가변 해상도) 정도를 제어합니다. UA나 디바이스가 이 속성을 지원하지 않으면 get 시 null을 반환하고, set은 no-op이어야 합니다. fixedFoveation 값을 0 미만으로 설정하면 0, 1 초과로 설정하면 1이 됩니다. 0은 최소, 1은 최대 foveation입니다. 해석 방식은 UA에 따라 다를 수 있습니다. 값이 변경되면 다음 XRFrame에 반영됩니다.

참고: fixed foveation은 사용자의 시야 가장자리에서 렌더링 해상도를 줄여 GPU fill 성능 한계를 극복할 수 있는 기술입니다. 전력 소비를 줄이고, 텍스처 해상도를 높일 수 있어 낮은 대비의 배경 이미지에 효과적이지만, 텍스트나 세밀한 이미지는 덜 효과적입니다. 프레임마다 조절해 성능/품질 트레이드오프를 최적화할 수 있습니다.

XRWebGLLayer전체 크기 뷰포트 목록(list of full-sized viewports)을 반드시 가지고, 이는 리스트로 각 view마다 하나의 WebGL viewport를 포함합니다. 여기에 현재 active가 아니지만 세션에서 active가 될 수 있는 secondary view도 포함됩니다. 각 뷰포트의 widthheight는 0보다 커야 하며, target framebuffer 경계를 넘으면 안 됩니다. 뷰포트는 겹치면 안 됩니다. composition enabledfalse면, 전체 크기 뷰포트 목록은 context의 전체 기본 프레임버퍼를 덮는 단일 WebGL viewport만 포함해야 합니다.

XRWebGLLayer뷰포트 객체 목록(list of viewport objects)을 반드시 가지며, 현재 노출된 각 active view마다 하나의 XRViewport를 포함합니다.

getViewport() 는 주어진 XRView 가 이 레이어에 렌더링 시 사용할 XRViewport를 조회합니다.

getViewport(view) 메서드가 XRWebGLLayer layer에서 호출되면, 아래 단계를 반드시 실행해야 합니다:

  1. sessionviewsession으로 둡니다.

  2. framesessionanimation frame으로 둡니다.

  3. sessionlayersession과 다르면 InvalidStateError 를 throw하고 중단합니다.

  4. frameactive 불리언이 falseInvalidStateError 를 throw하고 중단합니다.

  5. viewframeframe과 다르면 InvalidStateError 를 throw하고 중단합니다.

  6. viewport modifiable 플래그가 true이고, viewrequested viewport scalecurrent viewport scale과 다르면:

    1. current viewport scalerequested viewport scale로 설정합니다.

    2. 뷰포트 객체 목록에서 view와 연관된 XRViewport스케일된 뷰포트 얻기 결과로 설정합니다.

  7. viewviewport modifiable 플래그를 false로 설정합니다.

  8. viewport뷰포트 객체 목록에서 view에 해당하는 XRViewport로 둡니다.

  9. viewport를 반환합니다.

참고: viewport modifiable 플래그는 current viewport scale이 변경되지 않아도 false로 설정됩니다. 이렇게 하면 getViewport(view) 호출은 해당 애니메이션 프레임 내에서 항상 일관된 값을 반환하여, 최초 조회된 값이 해당 프레임 동안 고정됩니다. requestViewportScale()getViewport() 이후 호출하면, 요청값은 다음 프레임에서 getViewport()가 다시 호출될 때만 적용됩니다.

XRSession네이티브 WebGL 프레임버퍼 해상도(native WebGL framebuffer resolution)를 식별해야 하며, 이는 XR 디바이스의 실제 픽셀 해상도와 일치하는 WebGL 프레임버퍼의 픽셀 해상도입니다.

XRSession session네이티브 WebGL 프레임버퍼 해상도는 다음 단계로 결정됩니다:

  1. sessionmode"inline"이 아니면, 네이티브 WebGL 프레임버퍼 해상도를 세션의 모든 XRView를 담을 수 있는 프레임버퍼 픽셀과, 물리적 디스플레이 픽셀(최대 배율 영역) 간 1:1 비율로 설정하고 종료합니다. 위와 같이 네이티브 해상도를 구할 방법이 없다면, 권장 WebGL 프레임버퍼 해상도를 사용할 수 있습니다.

  2. sessionmode"inline"이면, 네이티브 WebGL 프레임버퍼 해상도sessionrenderStateoutput canvas의 물리적 디스플레이 픽셀 크기로 설정하고, 캔버스 크기 또는 output canvas가 변경될 때마다 재평가합니다.

또한 XRSession권장 WebGL 프레임버퍼 해상도(recommended WebGL framebuffer resolution)를 식별해야 하며, 이는 모든 세션의 XRView를 포함할 수 있으면서, 평균적인 애플리케이션에 대해 성능과 품질의 균형을 제공하는 WebGL 프레임버퍼 해상도의 최적 추정값을 나타냅니다. 이 값은 네이티브 WebGL 프레임버퍼 해상도보다 작거나, 크거나, 같을 수 있습니다. 새로운 opaque framebuffer 는 이 해상도로 생성되며, 가로와 세로는 XRWebGLLayerInitframebufferScaleFactor 값으로 각각 스케일링됩니다.

참고: user agent는 권장 WebGL 프레임버퍼 해상도를 추정하는 방법을 자유롭게 선택할 수 있습니다. 플랫폼별로 권장 크기를 질의하는 방법이 있다면 사용하는 것이 권장되지만, 필수는 아닙니다. framebufferScaleFactorgetNativeFramebufferScaleFactor() 에서 사용하는 스케일 팩터는 가로와 세로 각각에 적용되므로, 값이 2면 전체 픽셀 수는 4배가 됩니다. 만약 플랫폼이 픽셀 수 기반의 면적 렌더 스케일을 제공한다면, WebXR 스케일 팩터로 변환하려면 제곱근을 사용해야 합니다.

getNativeFramebufferScaleFactor(session) 메서드가 호출될 때 반드시 다음 단계를 실행해야 합니다:

  1. sessionthis로 둡니다.

  2. sessionended 값이 true이면, 0.0을 반환하고 단계를 중단합니다.

  3. session권장 WebGL 프레임버퍼 해상도의 가로, 세로를 각각 곱하면 session네이티브 WebGL 프레임버퍼 해상도가 되는 값을 반환합니다.

11.3. WebGL 컨텍스트 호환성

WebGL 컨텍스트가 몰입형 XR 이미지를 위한 소스로 사용되기 위해서는, 해당 몰입형 XR 디바이스에 대해 호환되는 그래픽 어댑터(compatible graphics adapter)에서 생성되어야 합니다. 호환되는 그래픽 어댑터가 무엇인지는 플랫폼마다 다르지만, 일반적으로 해당 그래픽 어댑터가 몰입형 XR 디바이스에 지연 없이 이미지를 제공할 수 있음을 의미합니다. WebGL 컨텍스트가 이미 호환되는 그래픽 어댑터에서 생성되지 않았다면, 해당 어댑터에서 다시 생성해야 XRWebGLLayer와 함께 사용할 수 있습니다.

참고: XR 플랫폼에 GPU가 하나뿐이라면, 해당 GPU가 플랫폼에서 제공하는 몰입형 XR 디바이스와 호환된다고 안전하게 간주할 수 있으며, 따라서 하드웨어 가속 WebGL 컨텍스트도 모두 호환됩니다. 통합 그래픽과 별도 GPU가 모두 있는 PC에서는 일반적으로 별도 GPU가 호환되는 그래픽 어댑터로 간주됩니다(더 높은 성능을 제공하기 때문). 그래픽 어댑터가 여러 개 설치된 데스크탑 PC에서는 몰입형 XR 디바이스가 물리적으로 연결된 어댑터가 호환되는 그래픽 어댑터로 간주됩니다.

참고: "inline" 세션은 캔버스와 동일한 그래픽 어댑터를 사용하므로 xrCompatible 컨텍스트가 필요하지 않습니다.

partial dictionary WebGLContextAttributes {
    boolean xrCompatible = false;
};

partial interface mixin WebGLRenderingContextBase {
    [NewObject] Promise<undefined> makeXRCompatible();
};

사용자 에이전트가 이 명세를 구현할 때, 반드시 모든 WebGLRenderingContextBase에 대해, 초기값 falseXR 호환(XR compatible) 불리언을 설정해야 합니다. XR 호환 불리언이 true가 되면, 해당 컨텍스트는 현재 몰입형 XR 디바이스에서 요청된 모든 XRSession의 레이어와 함께 사용할 수 있습니다.

참고: 이 플래그는 느린 동기적 동작을 유발하므로 사용이 권장되지 않습니다. 비동기적 해결책으로 makeXRCompatible() 사용을 고려하세요.

XR 호환 불리언은 컨텍스트 생성 시 또는 생성 후에도 설정할 수 있으며, 컨텍스트 손실이 발생할 수 있습니다. 생성 시 XR 호환 불리언을 설정하려면, WebGL 컨텍스트 요청 시 xrCompatible 컨텍스트 속성을 true로 설정해야 합니다. 만약 요청하는 문서의 origin에 "xr-spatial-tracking" permissions policy가 허용되지 않았다면, xrCompatible 는 아무 효과가 없습니다.

xrCompatible 플래그가 WebGLContextAttributes에 대해 true로 설정되면, 사용자 에이전트에게 WebGL 컨텍스트 생성 시 해당 호환되는 그래픽 어댑터를 사용하도록 요청하게 됩니다. 이 과정이 성공하면, 생성된 컨텍스트의 XR 호환 불리언이 true로 설정됩니다. 몰입형 XR 디바이스를 얻기 위해서는 ensure an immersive XR device is selected를 호출해야 합니다.

참고: ensure an immersive XR device is selected병렬로 실행되어야 하므로, 메인 스레드에서 느린 동기 동작을 유발합니다. 사용자 에이전트는 대신 makeXRCompatible() 사용을 요청하는 경고를 콘솔에 출력해야 합니다.

아래 코드는 몰입형 XR 디바이스와 호환되는 WebGL 컨텍스트를 생성한 뒤, 이를 사용해 XRWebGLLayer를 생성합니다.
function onXRSessionStarted(xrSession) {
  const glCanvas = document.createElement("canvas");
  const gl = glCanvas.getContext("webgl", { xrCompatible: true });

  loadWebGLResources();

  xrSession.updateRenderState({ baseLayer: new XRWebGLLayer(xrSession, gl) });
}

컨텍스트 생성 후 XR 호환 불리언을 설정하려면 makeXRCompatible() 메서드를 사용해야 합니다.

참고: 일부 시스템에서는 이 플래그가 고성능 별도 GPU를 활성화하거나, 모든 명령을 온디바이스 GPU로 프록시할 수 있습니다. XR 사용 여부가 결정되지 않은 상황이라면, 몰입형 세션을 시작할 때만 makeXRCompatible() 호출을 권장합니다.

makeXRCompatible() 메서드는 WebGLRenderingContextBase몰입형 XR 디바이스에 대해 호환되는 그래픽 어댑터에서 실행되고 있음을 보장합니다.

이 메서드가 호출되면, 사용자 에이전트는 반드시 다음 단계를 실행해야 합니다:

  1. 요청 문서의 origin이 "xr-spatial-tracking" permissions policy를 사용할 수 없다면, resolve promise를 반환하고 중단한다. XR 권한 정책이 비활성화된 경우, XR 디바이스가 없는 것처럼 동작해야 하며, makeXRCompatible() 은 set-and-forget 방식이어야 하기 때문이다.

  2. promise를 이 WebGLRenderingContextBaseRealm에서 생성된 새 Promise로 둔다.

  3. contextthis로 둔다.

  4. 아래 단계를 병렬로 실행한다:

    1. deviceensure an immersive XR device is selected 결과로 둔다.

    2. contextXR 호환 불리언을 아래와 같이 설정한다:

      만약 contextWebGL 컨텍스트 손실 플래그가 설정되어 있으면:

      작업 큐에 추가하여 contextXR 호환 불리언을 false로 설정하고, promise를 reject하며 InvalidStateError로 반환한다.

      devicenull인 경우:

      작업 큐에 추가하여 contextXR 호환 불리언을 false로 설정하고, promise를 reject하며 InvalidStateError로 반환한다.

      contextXR 호환 불리언이 true인 경우:

      작업 큐에 추가하여 promise를 resolve한다.

      contextdevice에 대해 호환되는 그래픽 어댑터에서 생성된 경우:

      작업 큐에 추가하여 contextXR 호환 불리언을 true로 설정하고 promise를 resolve한다.

      그 외의 경우:

      WebGL 작업 소스에 작업 큐 추가하여 아래 단계 실행:

      1. context를 강제로 손실시킨다.

      2. WebGL 명세에 따라 컨텍스트 손실 처리:

        1. canvascontextcanvas로 둔다.

        2. contextWebGL 컨텍스트 손실 플래그가 설정되어 있으면, 중단한다.

        3. contextWebGL 컨텍스트 손실 플래그를 설정한다.

        4. context에서 생성된 각 WebGLObject 인스턴스의 invalidated 플래그를 설정한다.

        5. "WEBGL_lose_context"를 제외한 모든 확장 기능을 비활성화한다.

        6. WebGL 작업 소스에 작업 큐 추가하여 아래 단계 실행:

          1. WebGL 컨텍스트 이벤트 e를 이름 "webglcontextlost"로 canvas에 발생시키고, statusMessage 는 ""로 설정한다.

          2. ecanceled flag가 설정되지 않았다면, promise를 reject하고 AbortError로 반환하며, 중단한다.

          3. 아래 단계를 병렬로 실행한다.

            1. device에 대해 호환되는 그래픽 어댑터에서 복원 가능한 드로잉 버퍼를 대기한다.

            2. WebGL 작업 소스에 작업 큐 추가하여 아래 단계 실행:

              1. device에 대해 호환되는 그래픽 어댑터에서 컨텍스트를 복원한다.

              2. contextXR 호환 불리언을 true로 설정한다.

              3. promise를 resolve한다.

  5. promise를 반환한다.

또한, WebGL 컨텍스트가 손실될 때는 "webglcontextlost" 이벤트가 발생하기 전에 아래 단계를 실행합니다:

  1. 컨텍스트의 XR 호환 불리언을 false로 설정합니다.

아래 코드는 기존 WebGL 컨텍스트에서 XRWebGLLayer를 생성합니다.
const glCanvas = document.createElement("canvas");
const gl = glCanvas.getContext("webgl");

loadWebGLResources();

glCanvas.addEventListener("webglcontextlost", (event) => {
  // WebGL 컨텍스트를 복원할 수 있음을 나타냅니다.
  event.canceled = true;
});

glCanvas.addEventListener("webglcontextrestored", (event) => {
  // 컨텍스트 손실 후에는 WebGL 리소스를 다시 생성해야 합니다.
  loadWebGLResources();
});

async function onXRSessionStarted(xrSession) {
  // 사용할 캔버스 컨텍스트가 디바이스와 호환되는지 확인합니다.
  // 컨텍스트 손실이 발생할 수 있습니다.
  await gl.makeXRCompatible();
  xrSession.updateRenderState({ baseLayer: new XRWebGLLayer(xrSession, gl) });
}

12. 이벤트(Events)

이 명세서에서 큐에 추가된 모든 작업(tasks queued)작업 소스(task source)는 별도의 명시가 없는 한 XR 작업 소스(XR task source)입니다.

12.1. XRSessionEvent

XRSessionEventXRSession의 상태 변화가 발생했음을 알리기 위해 발생합니다.

[SecureContext, Exposed=Window]
interface XRSessionEvent : Event {
  constructor(DOMString type, XRSessionEventInit eventInitDict);
  [SameObject] readonly attribute XRSession session;
};

dictionary XRSessionEventInit : EventInit {
  required XRSession session;
};

session 속성은 이벤트를 발생시킨 XRSession을 나타냅니다.

12.2. XRInputSourceEvent

XRInputSourceEventXRInputSource의 상태 변화가 발생했음을 알리기 위해 발생합니다.

[SecureContext, Exposed=Window]
interface XRInputSourceEvent : Event {
  constructor(DOMString type, XRInputSourceEventInit eventInitDict);
  [SameObject] readonly attribute XRFrame frame;
  [SameObject] readonly attribute XRInputSource inputSource;
};

dictionary XRInputSourceEventInit : EventInit {
  required XRFrame frame;
  required XRInputSource inputSource;
};

inputSource 속성은 이 이벤트를 발생시킨 XRInputSource를 나타냅니다.

frame 속성은 이벤트가 발생한 시점의 XRFrame을 나타냅니다. 이 값은 과거의 데이터를 나타낼 수 있습니다. getViewerPose()frame에서 호출 시 예외를 발생시켜야 합니다.

사용자 에이전트가 name, XRFrame frame, XRInputSource source로 입력 소스 이벤트를 발생시켜야 할 때 다음 단계를 수행해야 합니다:

  1. XRInputSourceEvent eventtype name, frame frame, inputSource source로 생성한다.

  2. frameactive 불리언을 true로 설정한다.

  3. 프레임 업데이트를 적용한다(frame).

  4. eventframesession에 디스패치한다.

  5. frameactive 불리언을 false로 설정한다.

12.3. XRInputSourcesChangeEvent

XRInputSourcesChangeEventXRSession에서 사용 가능한 활성 XR 입력 소스 목록의 변경을 알리기 위해 발생합니다.

[SecureContext, Exposed=Window]
interface XRInputSourcesChangeEvent : Event {
  constructor(DOMString type, XRInputSourcesChangeEventInit eventInitDict);
  [SameObject] readonly attribute XRSession session;
  [SameObject] readonly attribute FrozenArray<XRInputSource> added;
  [SameObject] readonly attribute FrozenArray<XRInputSource> removed;
};

dictionary XRInputSourcesChangeEventInit : EventInit {
  required XRSession session;
  required sequence<XRInputSource> added;
  required sequence<XRInputSource> removed;

};

session 속성은 이벤트를 발생시킨 XRSession을 나타냅니다.

added 속성은 이벤트 시점에 XRSession에 추가된 XRInputSource목록입니다.

removed 속성은 이벤트 시점에 XRSession에서 제거된 XRInputSource목록입니다.

12.4. XRReferenceSpaceEvent

XRReferenceSpaceEventXRReferenceSpace의 상태 변화가 발생했음을 알리기 위해 발생합니다.

[SecureContext, Exposed=Window]
interface XRReferenceSpaceEvent : Event {
  constructor(DOMString type, XRReferenceSpaceEventInit eventInitDict);
  [SameObject] readonly attribute XRReferenceSpace referenceSpace;
  [SameObject] readonly attribute XRRigidTransform? transform;
};

dictionary XRReferenceSpaceEventInit : EventInit {
  required XRReferenceSpace referenceSpace;
  XRRigidTransform? transform = null;
};

referenceSpace 속성은 이벤트를 발생시킨 XRReferenceSpace를 나타냅니다.

선택적 transform 속성은 이벤트 이후 referenceSpace네이티브 원점(native origin)의 위치와 방향을 이벤트 이전 좌표계에서 설명합니다. XRSystem이 이전과 새로운 좌표계의 차이를 알 수 없는 경우 이 속성은 null일 수 있습니다.

참고: referenceSpace 또는 referenceSpace가 예를 들어 헤드셋이 두 위치 사이에서 벗겨졌다가 다시 착용된 경우 등에서 변경될 수 있습니다. 이런 경우, 경험이 월드-락 콘텐츠에 의존한다면 사용자에게 경고하고 씬을 재설정해야 합니다.

12.5. XRVisibilityMaskChangeEvent

由于视锥体并不总是与矩形显示器精确相交,XRLayer 的整个区域可能不会被显示。该事件将通知体验哪些 XRView 区域被展示给用户。

当用户代理需要告知体验 XRLayer 的显示区域发生变化时,会触发 XRVisibilityMaskChangeEvent 事件。 体验可以选择只绘制该区域,这有助于提升性能。

注意:体验必须在 requestSession 的 promise 解决期间注册该事件,否则事件可能会触发且遮罩信息会丢失。

[SecureContext, Exposed=Window]
interface XRVisibilityMaskChangeEvent : Event {
  constructor(DOMString type, XRVisibilityMaskChangeEventInit eventInitDict);
  [SameObject] readonly attribute XRSession session;
  readonly attribute XREye eye;
  readonly attribute unsigned long index;
  [SameObject] readonly attribute Float32Array vertices;
  [SameObject] readonly attribute Uint32Array indices;
};

dictionary XRVisibilityMaskChangeEventInit : EventInit {
  required XRSession session;
  required XREye eye;
  required unsigned long index;
  required Float32Array vertices;
  required Uint32Array indices;
};

session 属性表示生成该事件的 XRSession

eye 属性表示该遮罩所应用的 XREye

index 属性表示该遮罩应用到的 XRView视图列表 中的偏移量。

vertices 属性是一个 列表,包含 XY 坐标。体验必须假定 Z 坐标为 -1。每组 XYZ 坐标描述一个顶点。如果该数组为空,则应绘制 XRView 的整个区域。

indices 属性是一个 列表,用于描述 vertices 顶点列表中的索引。这些索引将描述该眼睛的 XRView 应绘制的区域。如果该数组为空,则应绘制 XRView 的整个区域。

该区域必须使用 projectionMatrixXRVieweye)和默认 XRRigidTransform 进行绘制。

注意:这意味着该区域不得使用当前 XRViewXRRigidTransformeye)进行绘制。

12.6. 이벤트 타입(Event Types)

사용자 에이전트는 다음과 같은 새로운 이벤트를 반드시 제공합니다. 이벤트의 등록과 발생(fire)은 DOM 이벤트의 일반적인 동작을 따라야 합니다.

사용자 에이전트는 이벤트를 발생시켜야 하며, devicechange 이벤트를 XRSystem 객체에 대해 발생시켜야 합니다. 이는 immersive XR device의 가용성이 변경되었음을 나타내기 위한 것이며, 단 문서의 origin이 "xr-spatial-tracking" permissions policy 사용이 허용되지 않은 경우에는 예외입니다.

사용자 에이전트는 이벤트를 발생시켜야 하며, visibilitychange 이벤트를 XRSessionEvent를 사용하여 XRSession에 대해, 해당 XRSessionvisibility state가 변경될 때마다 발생시켜야 합니다.

사용자 에이전트는 이벤트를 발생시켜야 하며, end 이벤트를 XRSessionEvent를 사용하여 XRSession에 대해, 세션이 애플리케이션 또는 사용자 에이전트에 의해 종료될 때 발생시켜야 합니다.

사용자 에이전트는 이벤트를 발생시켜야 하며, inputsourceschange 이벤트를 XRInputSourcesChangeEvent를 사용하여 XRSession에 대해, 세션의 list of active XR input sources가 변경될 때 발생시켜야 합니다.

사용자 에이전트는 이벤트를 발생시켜야 하며, trackedsourceschange 이벤트를 XRInputSourcesChangeEvent를 사용하여 XRSession에 대해, 세션의 list of active XR tracked sources가 변경될 때 발생시켜야 합니다.

사용자 에이전트는 이벤트를 발생시켜야 하며, selectstart 이벤트를 XRInputSourceEvent를 사용하여 XRSession에 대해, XRInputSource 중 하나가 primary action을 시작할 때 발생시켜야 합니다. 이 이벤트는 해당 타입이어야 합니다.

사용자 에이전트는 이벤트를 발생시켜야 하며, selectend 이벤트를 XRInputSourceEvent를 사용하여 XRSession에 대해, XRInputSource 중 하나가 primary action을 종료하거나, XRInputSourceprimary action을 시작한 뒤 연결이 끊어질 때 발생시켜야 합니다.

사용자 에이전트는 이벤트를 발생시켜야 하며, select 이벤트를 XRInputSourceEvent를 사용하여 XRSession에 대해, XRInputSource 중 하나가 primary action을 완전히 완료했을 때 발생시켜야 합니다.

사용자 에이전트는 이벤트를 발생시켜야 하며, squeezestart 이벤트를 XRInputSourceEvent를 사용하여 XRSession에 대해, XRInputSource 중 하나가 primary squeeze action을 시작할 때 발생시켜야 합니다.

사용자 에이전트는 이벤트를 발생시켜야 하며, squeezeend 이벤트를 XRInputSourceEvent를 사용하여 XRSession에 대해, XRInputSource 중 하나가 primary squeeze action을 종료하거나, XRInputSourceprimary squeeze action을 시작한 뒤 연결이 끊어질 때 발생시켜야 합니다.

사용자 에이전트는 이벤트를 발생시켜야 하며, squeeze 이벤트를 XRInputSourceEvent를 사용하여 XRSession에 대해, XRInputSource 중 하나가 primary squeeze action을 완전히 완료했을 때 발생시켜야 합니다.

사용자 에이전트는 이벤트를 발생시켜야 하며, frameratechange 이벤트를 XRSessionEvent를 사용하여 XRSession에 대해, XR CompositorXRSessioninternal nominal framerate를 변경할 때 발생시켜야 합니다.

사용자 에이전트는 이벤트를 발생시켜야 하며, reset 이벤트를 XRReferenceSpaceEvent를 사용하여 XRReferenceSpace에 대해 발생시켜야 하며, 이는 native origin 또는 effective origin에 불연속성이 발생한 경우(즉, 사용자 환경에 대해 origin의 위치 또는 방향에 중대한 변화가 있을 때)입니다. (예: 사용자가 XR 장치를 재보정한 후, 또는 XR 장치가 추적을 잃었다가 다시 추적을 시작하여 origin을 자동으로 이동시키는 경우) reset 이벤트는 XRBoundedReferenceSpaceboundsGeometry가 변경될 때도 반드시 발생해야 합니다. reset 이벤트는 viewer의 포즈에 불연속성이 발생해도 XRReferenceSpace의 origin 물리적 매핑이 안정적으로 유지되는 한(예: viewer가 같은 트래킹 영역 내에서 잠시 추적을 잃었다가 다시 찾는 경우)에는 발생하면 안 됩니다. 또한, unbounded reference space가 사용자의 근처에서 공간의 안정성을 유지하기 위해 native origin을 조금씩 조정하는 경우에도, 명백한 불연속성이 아니라면 이벤트는 발생하면 안 됩니다. 이 이벤트는 새로운 origin을 사용하는 어떤 XR animation frame보다 먼저 dispatch되어야 합니다. reset 이벤트는 해당 이벤트를 발생시키는 reference space의 모든 offset reference space에도 dispatch되어야 하며, offset XRBoundedReferenceSpaceboundsGeometry 또한 다시 계산되어야 합니다.

참고: 이는 세션이 XRReferenceSpacereset 리스너가 등록되어 있다면 해당 reference space에 강한 참조(strong reference)를 유지해야 함을 의미합니다.

참고: viewer 위치의 갑작스러운 이동(jump)은 애플리케이션이 emulatedPosition 불리언을 관찰함으로써 처리할 수 있습니다. viewer 위치의 점프가 emulatedPositiontrue에서 false로 바뀌는 시점과 일치하면, 이는 viewer가 추적을 다시 획득했고, 새 위치가 이전에 에뮬레이션된 값들로부터의 보정임을 나타냅니다. 사용자가 실제로 움직이지 않고 가상 세계 내에서만 이동할 수 있는 "텔레포테이션" 기능이 없는 경험에서는 일반적으로 이 동작이 애플리케이션이 원하는 동작입니다. 그러나 "텔레포테이션" 기능이 있는 경험에서는 추적 복구 후 viewer의 위치를 다시 원래 위치로 점프시키는 것이 불필요하게 어색할 수 있습니다. 이런 경우, 애플리케이션은 추적이 복구되었을 때, viewer의 현재 가상 위치로부터 경험을 계속 진행시키고, 위치의 갑작스런 점프를 텔레포트 오프셋에 반영할 수 있습니다. 이를 위해 개발자는 getOffsetReferenceSpace()를 호출하여, effective origin을 이전 프레임 이후 viewer 위치가 점프한 만큼 조정한 대체 reference space를 생성할 수 있습니다.

13. 보안, 프라이버시, 그리고 편안함에 대한 고려사항

WebXR 디바이스 API는 강력한 새로운 기능을 제공하며, 이로 인해 사용자 에이전트가 완화해야 하는 몇 가지 고유한 프라이버시, 보안, 그리고 편안함에 대한 위험이 발생합니다.

13.1. 민감한 정보

XR 맥락에서 민감한 정보에는 사용자 설정 데이터(예: 동공 간 거리(IPD))와 XRPose와 같은 센서 기반 데이터가 포함되지만 이에 국한되지 않습니다. 모든 몰입형 세션은 사용자의 포즈가 렌더링에 필요하기 때문에 어느 정도의 민감한 데이터를 노출하게 됩니다. 그러나 경우에 따라 동일한 민감한 정보가 "inline" 세션을 통해서도 노출될 수 있습니다.

13.2. 사용자 의도

사용자 의도란 특정 행동이 사용자의 의도에 따라 이루어졌으며 동의가 있었음을 나타내는 신호입니다.

민감한 정보를 노출하거나 사용자 경험에 중대한 영향을 미치는 행동을 허용하기 전에 사용자 의도를 반드시 확인해야 할 때가 많습니다. 이러한 의도는 여러 방식으로 전달되거나 관찰될 수 있습니다.

참고: 사용자 의도를 판단하는 일반적인 방법은 UI 컨트롤의 일시적 활성화입니다. 보통 "VR 입장" 버튼이 이에 해당합니다. 활성화가 일시적이므로, XR 세션을 요청하는 브라우징 컨텍스트는 UI 컨트롤을 포함하는 컨텍스트의 상위이거나 동일 출처 도메인하위여야 하며, 최근에 해당 브라우징 컨텍스트의 활성 문서였어야 합니다.

13.2.1. 사용자 활성화

일시적 활성화는 일부 시나리오에서 사용자 의도의 표시로 사용될 수 있습니다.

13.2.2. 웹 애플리케이션 실행

일부 환경에서는 페이지가 애플리케이션으로 제공되어 몰입형 콘텐츠 실행을 명시적으로 의도할 수 있습니다. 이 경우 웹 애플리케이션 실행사용자 의도의 표시로 사용될 수 있습니다.

묵시적 동의란 사용자 에이전트가 명시적으로 묻지 않고 사용자의 동의를 판단하는 경우를 말합니다. 예를 들어, 웹 애플리케이션의 설치 상태, 방문 빈도 및 최근성, 또는 사용자가 몰입형 경험에 들어가고자 하는 명확한 신호를 보낸 사용자 에이전트 정의 동작 등이 있습니다. XR 데이터의 민감성을 고려할 때, 묵시적 신호에 의존할 때는 각별한 주의가 필요합니다.

명시적 동의란 사용자 에이전트가 명시적으로 동의를 요청하여 사용자의 동의를 판단하는 경우를 말합니다. 명시적 동의를 받을 때, 사용자 에이전트는 요청하는 내용을 설명하고 사용자가 거부할 수 있는 옵션을 제공합니다. 사용자 동의 요청은 보호되는 기능과 사용자 에이전트의 선택에 따라 다양한 시각적 형태로 제시될 수 있습니다. 웹 애플리케이션의 설치 상태는 설치 시 명시적 동의가 요청된 경우 명시적 동의의 신호로 간주될 수 있습니다.

특정 명시적 동의가 특정 origin에 대해 부여되면, 이 동의는 브라우징 컨텍스트가 종료될 때까지 유지되는 것이 권장됩니다. 사용자 에이전트는 사용자 의도에 대한 묵시적 또는 명시적 신호에 따라 이 동의 기간을 연장하거나 단축할 수 있지만, 특히 묵시적 신호에 의존할 때는 주의가 필요합니다. 예를 들어, 몰입형 콘텐츠 실행을 명시적으로 의도하여 설치된 웹 애플리케이션의 경우 사용자의 동의를 지속하는 것이 적절할 수 있지만, 몰입형 콘텐츠가 부가 기능인 경우에는 그렇지 않을 수 있습니다.

사용자 에이전트가 사용자의 동의를 얼마나 오래 유지하든, 민감한 정보XRSession종료되지 않은 경우에만 노출되어야 합니다.

사용자 에이전트가 기능 사용을 위해 명시적 동의를 요청하게 하는 여러 비-XR API가 있습니다. 만약 활성 몰입형 세션이 있는 동안 사용자 에이전트가 사용자의 동의를 요청해야 한다면, 사용자에게 동의 요청을 표시하기 전에 반드시 세션을 종료해야 합니다. 해당 기능에 대한 사용자의 동의가 활성 몰입형 세션이 생성되기 전에 이미 부여된 경우에는 세션을 종료할 필요가 없습니다.

참고: 이 제한은 모든 사용자 에이전트 간의 동작 일관성을 보장하기 위한 것으로, 사용자 에이전트가 세션 중간 명시적 동의를 어떻게 관리해야 하는지에 대한 합의가 이루어질 때까지 유지됩니다. 장기적인 요구사항은 아닙니다.

13.4. 데이터 조정

경우에 따라, 데이터 조정(예: 쓰로틀링, 양자화, 반올림, 제한 또는 기타 방식으로 XR 디바이스에서 보고되는 데이터를 조작하는 것)을 통해 보안 및 프라이버시 위협을 완화할 수 있습니다. 이는 사용자 의도가 확인된 상황에서도 지문 채취를 방지하기 위해 필요할 수 있습니다. 단, 데이터 조정 완화책은 사용자 불편을 초래하지 않는 상황에서만 사용해야 합니다.

13.4.1. 쓰로틀링

쓰로틀링민감한 정보를 원래보다 낮은 빈도로 보고하는 것입니다. 이 완화책은 사이트가 사용자 의도 추론, 위치 추론, 또는 사용자 프로파일링을 수행하는 능력을 줄일 수 있습니다. 그러나 적절히 사용하지 않으면 쓰로틀링은 사용자 불편을 초래할 위험이 큽니다. 또한 많은 상황에서 완전한 완화책이 되지 못할 수 있습니다.

13.4.2. 반올림, 양자화, 퍼징

반올림, 양자화, 퍼징은 원시 데이터를 개발자에게 반환하기 전에 수정하는 세 가지 완화책입니다. 반올림은 데이터를 표현하는 자릿수를 줄여 정밀도를 낮춥니다. 양자화는 연속적인 데이터를 이산적인 값의 집합으로 제한합니다. 퍼징은 데이터에 약간의 무작위 오류를 도입하는 것입니다. 이 완화책들은 지문 채취를 방지하는 데 유용하며, 특히 사용자에게 눈에 띄는 불편을 주지 않는 경우에 효과적입니다.

13.4.3. 제한

제한은 데이터가 특정 범위 내에 있을 때만 보고하는 것입니다. 예를 들어, 사용자가 승인된 위치에서 특정 거리 이상 벗어났을 때 위치 포즈 데이터 보고를 제한하는 것이 가능합니다. 이 완화책을 적용할 때는 사용자 경험이 부정적으로 영향을 받지 않도록 주의해야 합니다. 범위 끝에서 '하드 스톱'을 피하는 것이 바람직하며, 이는 사용자 경험에 방해가 될 수 있기 때문입니다.

13.5. 보호되는 기능

API에서 노출되는 민감한 정보는 위협 프로필과 이에 대한 보호가 필요한 범주로 나눌 수 있습니다.

13.5.1. 몰입성(Immersiveness)

사용자는 몰입형 세션이 생성되는 시점을 직접 제어할 수 있어야 합니다. 그 이유는 세션 생성이 사용자의 기기에 침습적인 변화를 일으키기 때문입니다. 예를 들어, 몰입형 세션을 시작하면 XR 디바이스 센서가 활성화되고, 디바이스의 디스플레이 접근 권한을 가져오며, 몰입형 콘텐츠를 표시하기 시작해 다른 애플리케이션의 XR 하드웨어 접근을 종료시킬 수 있습니다. 또한 일부 시스템에서는 상당한 전력 또는 성능 오버헤드가 발생하거나, 상태 트레이 또는 스토어프론트가 실행될 수 있습니다.

특정 global object에 대해 몰입형 세션 요청이 허용되는지 판단하려면, 사용자 에이전트는 다음 단계를 반드시 수행해야 합니다:

  1. 요청이 global object일시적 활성화 상태이거나 웹 애플리케이션 실행 중이 아닐 때 이루어진 경우, false를 반환합니다.

  2. 몰입형 세션 시작에 대한 사용자 의도명시적 동의 또는 묵시적 동의로 명확히 파악되지 않은 경우, false를 반환합니다.

  3. true를 반환합니다.

"inline" 세션을 시작하는 경우에는 동일한 요구사항이 자동으로 적용되지는 않지만, 세션의 요청된 기능에 따라 추가 요구사항이 적용될 수 있습니다.

특정 global object에 대해 인라인 세션 요청이 허용되는지 판단하려면, 사용자 에이전트는 다음 단계를 반드시 수행해야 합니다:

  1. 세션 요청에 필수 기능 또는 선택 기능이 포함되어 있고, 요청이 global object일시적 활성화 상태이거나 웹 애플리케이션 실행 중이 아닐 때 이루어진 경우, false를 반환합니다.

  2. global objectWindow가 아닌 경우, false를 반환합니다.

  3. true를 반환합니다.

13.5.2. 포즈(Poses)

센서 데이터 기반일 때, XRPoseXRViewerPose 는 입력 스니핑, 시선 추적, 지문 채취 등 다양한 방식으로 악용될 수 있는 민감한 정보를 노출합니다.

XRSession session포즈를 보고할 수 있는지 판단하려면, 사용자 에이전트는 다음 단계를 반드시 수행해야 합니다:

  1. session관련 글로벌 객체현재 글로벌 객체가 아니라면, false를 반환합니다.

  2. sessionvisibilityState 값이 "hidden"이면, false를 반환합니다.

  3. 포즈 데이터를 반환할 수 있는지 다음과 같이 판단합니다:

    포즈 데이터가 사용자 에이전트에 의해 지문 채취가 불가능한 센서 데이터임이 알려진 경우

    true를 반환합니다.

    데이터 조정이 지문 채취 또는 프로파일링 방지를 위해 센서 데이터에 적용되는 경우

    true를 반환합니다.

    사용자 의도명시적 동의 또는 묵시적 동의로 명확히 파악된 경우

    true를 반환합니다.

    그 외의 경우

    false를 반환합니다.

참고: 포즈가 지문 채취가 불가능한 데이터임을 사용자 에이전트가 어떻게 판단하는지는 사용자 에이전트의 재량에 맡깁니다.

XRViewerPoseXRPose의 주요 차이점은 XRView 정보가 포함된다는 점입니다. 둘 이상의 view가 존재하고 이들 간의 물리적 관계가 사용자가 설정할 수 있는 경우, 이 view들 간의 관계는 사용자를 프로파일링하거나 지문 채취(fingerprinting)가 가능하므로 민감 정보로 간주됩니다.

만약 XRView들 간의 관계가 XR 디바이스를 고유하게 식별할 수 있다면, 사용자 에이전트는 지문 채취를 방지하기 위해 반드시 XRView 데이터를 익명화해야 합니다. 익명화 방법은 사용자 에이전트의 재량에 따릅니다.

참고: 또한, XRView들 간의 관계가 사용자가 설정한 동공 거리(IPD)에 영향을 받는 경우, 사용자 에이전트는 세션 생성 시 XRView 데이터를 보고하기 전에 명시적 동의를 요구하는 것이 강력히 권장됩니다.

13.5.3. Reference spaces

사용되는 reference space에 따라 여러 유형의 민감한 정보가 애플리케이션에 노출될 수 있습니다.

이로 인해 다양한 reference space 유형은 노출되는 민감한 정보가 안전하게 처리되도록 생성에 제한이 있습니다:

대부분의 reference space는 해당 공간 사용에 대한 사용자 의도명시적 동의 또는 묵시적 동의로 명확히 파악되어야 합니다. 자세한 내용은 기능 요구사항 표를 참고하세요.

"local", "local-floor", "bounded-floor" reference space들이 서로 연관될 수 있는 경우, 반드시 공통 native origin을 공유해야 합니다. 이 제한은 "unbounded" reference space 생성이 제한된 경우에만 적용됩니다.

두 공간 spacebaseSpace 간에 포즈 제한이 필요한지 판단하려면, 사용자 에이전트는 다음 단계를 반드시 수행해야 합니다:

  1. space 또는 baseSpace 중 하나가 XRBoundedReferenceSpace 이고, 다른 공간의 native originnative bounds geometry에서 사용자 에이전트가 정한 합리적인 거리보다 더 멀리 떨어져 있다면, true를 반환합니다.

  2. space 또는 baseSpace 중 하나가 XRReferenceSpace 이고, type"local" 또는 "local-floor"이며, 두 공간의 native origin 간 거리가 사용자 에이전트가 정한 합리적인 거리보다 크면, true를 반환합니다.

  3. false를 반환합니다.

참고: 문서 가시성 요구사항은 [DEVICE-ORIENTATION]에 기반합니다.

참고: "local" 또는 "local-floor" reference space 기준으로 보고되는 포즈는 15미터 이내로 XRReferenceSpacenative origin에서 제한하는 것이 권장됩니다.

참고: XRBoundedReferenceSpace 기준으로 보고되는 포즈는 native bounds geometry에서 1미터 이내로 제한하는 것이 권장됩니다.

13.6. 신뢰된 환경(Trusted Environment)

신뢰된 UI(Trusted UI)란 사용자 에이전트가 제공하며 사용자는 상호작용할 수 있지만, 페이지에서는 상호작용할 수 없는 인터페이스를 의미합니다. 사용자 에이전트는 신뢰된 UI를 반드시 지원해야 합니다.

신뢰된 UI는 다음과 같은 속성을 반드시 가져야 합니다:

일반적으로 신뢰된 UI를 지원하려는 사용자 에이전트에는 두 가지 선택지가 있습니다. 하나는 신뢰된 몰입형 UI(trusted immersive UI)로, 몰입형 모드를 종료하지 않고 제공되는 신뢰된 UI입니다. 신뢰된 몰입형 UI를 구현하는 것은 XRWebGLLayer 버퍼가 XR 디바이스 디스플레이 전체를 채우고, 사용자 에이전트가 자체적으로 픽셀을 "예약"하지 않기 때문에 어려울 수 있습니다. 사용자 에이전트는 신뢰된 몰입형 UI를 반드시 지원할 필요는 없으며, 대신 몰입형 모드를 일시 중지/종료하고 비몰입형 신뢰된 UI를 사용자에게 표시할 수 있습니다.

참고: 신뢰된 UI의 예시는 다음과 같습니다:

입력 정보(머리 자세, 입력 자세 등)를 읽을 수 있는 기능은 신뢰 UI의 무결성에 위험을 초래할 수 있습니다. 페이지가 사용자가 신뢰 UI와 상호작용하는 동안(예: 키보드 입력 추정 포함) 내린 선택을 염탐하기 위해 이 정보를 사용할 수 있기 때문입니다. 이 위험을 방지하기 위해 사용자 에이전트는 사용자가 신뢰 UI(몰입형 또는 비몰입형, 예: URL 바 또는 시스템 다이얼로그 등)와 상호작용할 때 모든 XRSessionvisibility state"hidden" 또는 "visible-blurred"로 설정해야 합니다. 또한 악의적인 페이지가 다른 페이지의 입력을 감시하지 못하도록, 사용자 에이전트는 현재 포커스된 영역XRSession을 생성한 문서에 속하지 않는 경우, 해당 XRSessionvisibility state"hidden"으로 설정해야 합니다.

특정 신뢰된 UI 인스턴스에 대해 "hidden""visible-blurred" 중 어떤 값을 사용할지 선택할 때, 사용자 에이전트는 머리 포즈 정보가 보안 위험이 되는지 반드시 고려해야 합니다. 예를 들어, 신뢰된 UI에서 텍스트 입력(특히 비밀번호 입력)이 포함된 경우, 사용자가 입력하는 동안 머리 포즈를 통해 입력한 텍스트가 유출될 수 있습니다. 이런 경우, 사용자 에이전트는 시선 추적 관련 정보 노출도 중단해야 합니다.

사용자 에이전트는 권한 프롬프트를 표시할 때 반드시 신뢰된 UI를 사용해야 합니다.

가상 환경이 사용자의 머리 움직임을 낮은 지연과 높은 프레임률로 일관되게 추적하지 못하면, 사용자가 방향 감각을 잃거나 신체적으로 불편함을 느낄 수 있습니다. 페이지가 항상 일관되고 성능 좋은 콘텐츠를 제공하도록 강제할 수 없으므로, 사용자 에이전트는 반드시 추적 가능한 신뢰된 환경과 페이지 콘텐츠와 비동기적으로 동작하는 XR Compositor를 제공해야 합니다. 컴포지터는 신뢰된 콘텐츠와 신뢰되지 않은 콘텐츠를 합성하는 역할을 합니다. 만약 콘텐츠가 성능이 부족하거나 프레임을 제출하지 않거나 예기치 않게 종료되더라도, 사용자 에이전트는 반응성 있는 신뢰된 UI를 계속 표시할 수 있어야 합니다.

또한, 페이지 콘텐츠는 성능과 무관하게 사용자를 불편하게 만들 수 있습니다. 잘못 적용된 트래킹, 깜빡이는 색상, 불쾌감·공포·위협을 주는 콘텐츠 등은 사용자가 XR 경험을 빠르게 종료하고 싶게 만들 수 있습니다. 이런 경우 XR 디바이스를 제거하는 것이 항상 빠르거나 실용적이지 않을 수 있으므로, 사용자 에이전트는 반드시 예약된 하드웨어 버튼을 누르거나 제스처를 수행하는 등 WebXR 콘텐츠를 빠져나와 사용자 에이전트의 신뢰된 UI를 표시할 수 있는 조치를 제공해야 합니다.

13.7. 컨텍스트 격리(Context Isolation)

신뢰된 UI는 반드시 페이지에서 사용하는 렌더링 컨텍스트(예: WebGL 렌더링 컨텍스트 등)와는 독립적으로, 상태가 격리된 렌더링 컨텍스트에서 그려져야 합니다. 이는 페이지가 신뢰된 UI의 컨텍스트 상태를 손상시켜 추적 환경을 제대로 렌더링하지 못하게 하거나, 신뢰된 UI의 이미지를 캡처해 개인 정보가 유출되는 것을 방지하기 위함입니다.

또한, CORS 관련 취약점을 방지하기 위해 각 브라우징 컨텍스트는 API에서 반환되는 객체(예: XRSession 등)의 새로운 인스턴스를 보게 됩니다. context와 같은 속성이 한 XRWebGLLayer에서 설정되어도, relevant realm이 다른 XRWebGLLayer에서는 읽을 수 없어야 하며, relevant realm동일 출처가 아닌 경우에도 마찬가지입니다. 이와 유사하게, API에서 호출되는 메서드는 다른 브라우징 컨텍스트에 관찰 가능한 상태 변화를 일으켜서는 안 됩니다. 예를 들어, 시스템 수준의 방향 재설정 메서드는 노출되지 않으며, 악의적인 페이지가 이를 반복 호출해 다른 페이지의 트래킹을 방해할 수 없도록 해야 합니다. 단, 사용자 제스처나 시스템 메뉴로 트리거된 시스템 수준의 방향 재설정은 사용자 에이전트가 반드시 존중해야 합니다.

참고: 이는 한 브라우징 컨텍스트가 몰입형 모드에 진입하거나, 디바이스 잠금을 획득하거나, devicechange 이벤트를 다른 브라우징 컨텍스트에 발생시키는 등, 상태 변화에는 적용되지 않습니다.

13.8. 지문 채취(Fingerprinting)

이 API는 사용자가 사용할 수 있는 하드웨어와 그 기능을 기술하므로, 필연적으로 지문 채취에 활용될 수 있는 추가 표면을 제공합니다. 이를 완전히 방지하는 것은 불가능하지만, 사용자 에이전트는 문제를 완화하기 위한 조치를 취해야 합니다. 본 명세는 사용 가능한 하드웨어 보고를 한 번에 하나의 디바이스로 제한하여, 여러 헤드셋이 연결된 드문 경우를 지문 채취 신호로 사용하는 것을 방지합니다. 또한, 보고되는 디바이스는 문자열 식별자를 제공하지 않으며, XRSession이 생성되기 전까지는 디바이스의 기능에 대한 정보도 거의 노출하지 않습니다. XRSession 생성 시 민감한 정보가 노출될 때는 추가 보호가 필요합니다.

13.8.1. isSessionSupported()의 지문 채취 고려사항

isSessionSupported() 는 사용자 활성화 없이 호출될 수 있으므로, 지문 채취 벡터로 사용될 수 있습니다.

"xr-session-supported" 강력한 기능(powerful feature)isSessionSupported() API 접근을 제한합니다.

"xr-session-supported" 권한 관련 알고리즘과 타입은 다음과 같이 정의됩니다:

permission descriptor type
dictionary XRSessionSupportedPermissionDescriptor: PermissionDescriptor {
  XRSessionMode mode;
};

name for XRPermissionDescriptor is "xr-session-supported"입니다.

13.8.2. "xr-session-supported" 자동 부여 시 고려사항

웹에서 프라이버시와 개인화는 종종 상충합니다. 이 절에서는 그 균형점을 어디에 둘 수 있는지, 그리고 사용자 에이전트가 isSessionSupported() 를 통해 사이트에 브라우저의 WebXR 기능을 프라이버시 저하 없이 알릴 수 있는 시점에 대한 지침을 제공합니다.

"xr-session-supported"는 아래 기준에 따라 일부 시스템에서 자동으로 부여될 수 있습니다. 이는 더 나은 사용자 경험을 제공하고 권한 피로(permission fatigue)를 완화할 수 있습니다.

여러 사용자 에이전트가 동일한 userAgentappVersion을 보고한다면, 이 집합은 user agent string으로 구분 불가(indistinguishable by user agent string)하다고 합니다. 이런 집합은 보통 브라우저 버전과 실행 중인 플랫폼/디바이스로 식별되지만, 연결된 외부 디바이스 상태로는 구분할 수 없습니다. user agent string으로 구분 불가 개념을 활용해 지문 채취 위험을 적절히 평가할 수 있습니다.

일부 user agent string으로 구분 불가 사용자 에이전트는 특정 XRSessionMode의 세션을 절대 지원하지 않음(never support)으로 간주합니다. 예: 모바일 AR을 지원하지 않는 것으로 알려진 휴대폰 모델에서 실행되는 사용자 에이전트 등 이런 경우, isSessionSupported() 가 항상 해당 XRSessionMode 를 지원하지 않는다고 보고해도 지문 채취 위험이 거의 없습니다. 왜냐하면 모든 해당 디바이스가 일관된 값을 보고하고, 디바이스 종류와 모델은 userAgent 등 다른 방법으로도 추론할 수 있기 때문입니다. 따라서 이런 시스템에서는 사용자 에이전트가 해당 XRSessionMode에 대해 "xr-session-supported"를 자동으로 거부해야 합니다.

다른 user agent string으로 구분 불가 사용자 에이전트는 특정 XRSessionMode의 세션을 대체로 지원함(usually support)으로 간주합니다. 예: VR 헤드셋 내에서만 실행되는 WebXR 지원 사용자 에이전트는, 사용자가 명시적으로 차단하지 않는 한 "immersive-vr" 세션을 지원할 가능성이 높음 이런 경우, 해당 XRSessionMode 를 지원하지 않는다고 보고하는 것이 정확하더라도, 오히려 사용자를 더 고유하게 식별할 수 있는 정보를 제공할 수 있습니다. 따라서 항상 해당 XRSessionMode 가 사용 가능하다고 보고하고, requestSession() 이 실패하도록 하는 것이 프라이버시 보호에 더 적합하며, 사용자가 혼란을 겪을 가능성도 낮습니다. 이런 시스템에서는 해당 XRSessionMode에 대해 "xr-session-supported"를 자동으로 부여해야 합니다.

XR 기능의 가용성이 매우 다양한(예: XR 주변기기를 지원하는 데스크톱 시스템 등) user agent string으로 구분 불가 사용자 에이전트는 지문 채취 위험이 가장 높습니다. 이런 디바이스의 사용자 에이전트는 isSessionSupported() API가 추가적인 지문 채취 정보를 제공하지 않도록 "xr-session-supported"를 자동으로 부여해서는 안 됩니다.

참고: 이런 경우에 사용할 수 있는 허용 가능한 접근법은 다음과 같습니다:
  • isSessionSupported() 호출 시 명시적 동의(권한 프롬프트 캐시 등 포함)를 항상 판단한다.

  • "xr-session-supported"를 자동으로 부여하되, isSessionSupported() 가 실제 XR 하드웨어/소프트웨어가 없어도 항상 true를 반환하도록 한다. 이 경우 사용성은 떨어질 수 있으나, XR 콘텐츠를 볼 수 없는 사용자에게도 페이지가 XR 콘텐츠를 광고하게 됨.

  • isSessionSupported() 가 적절한 하드웨어가 있을 때 명시적 동의를 요청하고, 하드웨어가 없을 때는 적당히 무작위로 지연 후 false를 반환한다. 이때 콘텐츠는 XR 하드웨어가 연결되지 않은 경우와 사용자가 명시적 동의를 거부한 경우를 구분할 수 없어야 한다.

어떤 방식을 선택하든, 명시적 동의 없이 XR 하드웨어에 대한 추가 정보를 노출해서는 안 됩니다.

14. 통합(Integrations)

14.1. 권한 정책(Permissions Policy)

이 명세는 정책 제어 기능(policy-controlled feature)을 정의합니다. 이 기능은 공간 추적(spatial tracking)이 필요한 XRSessionrequestSession()을 통해 반환될 수 있는지, 그리고 공간 추적이 필요한 세션 모드 지원 여부가 isSessionSupported() 또는 devicechange 이벤트로 표시될 수 있는지 제어합니다. 이 기능은 navigator.xr 객체에 적용됩니다.

이 기능의 식별자는 "xr-spatial-tracking"입니다.

이 기능의 기본 허용 목록(default allowlist)["self"]입니다.

참고: 문서의 origin"xr-spatial-tracking" permissions policy를 사용할 수 없는 경우, 모든 몰입형 세션이 차단됩니다. 모든 몰입형 세션은 공간 추적을 필요로 하기 때문입니다. 인라인 세션은 여전히 허용되지만, "viewer" XRReferenceSpace만 사용할 수 있도록 제한됩니다.

14.2. Permissions API 통합

[permissions] API는 웹사이트가 사용자에게 권한을 요청하고, 어떤 권한이 부여되었는지 질의할 수 있는 일관된 방법을 제공합니다.

"xr" 강력한 기능(powerful feature)의 권한 관련 알고리즘과 타입은 다음과 같이 정의됩니다:

permission descriptor type
dictionary XRPermissionDescriptor: PermissionDescriptor {
  XRSessionMode mode;
  sequence<DOMString> requiredFeatures;
  sequence<DOMString> optionalFeatures;
};

name for XRPermissionDescriptor"xr"입니다.

permission result type
[Exposed=Window]
interface XRPermissionStatus: PermissionStatus {
  attribute FrozenArray<DOMString> granted;
};
permission query algorithm
"xr" 권한을 XRPermissionDescriptor descriptorXRPermissionStatus status로 질의하려면, UA는 다음 단계를 반드시 수행해야 합니다:
  1. statusstatedescriptorpermission state로 설정합니다.

  2. statusstate"denied"이면, statusgranted 를 빈 FrozenArray로 설정하고, 이 단계를 중단합니다.

  3. result요청된 기능 해석 결과로 설정합니다. 인자로는 descriptorrequiredFeatures, optionalFeatures, mode를 사용합니다.

  4. resultnull이면 다음을 수행합니다:

    1. statusgranted 를 빈 FrozenArray로 설정합니다.

    2. statusstate"denied"로 설정합니다.

    3. 이 단계를 중단합니다.

  5. result의 필드 (consentRequired, consentOptional, granted)를 가져옵니다.

  6. statusgrantedgranted로 설정합니다.

  7. consentRequiredconsentOptional이 모두 비어 있다면, statusstate"granted"로 설정하고 이 단계를 중단합니다.

  8. statusstate"prompt"로 설정합니다.

permission request algorithm
"xr" 권한 요청XRPermissionDescriptor descriptorXRPermissionStatus status로 수행하려면, UA는 다음 단계를 반드시 수행해야 합니다:
  1. statusgranted 를 빈 FrozenArray로 설정합니다.

  2. requiredFeaturesdescriptorrequiredFeatures로 설정합니다.

  3. optionalFeaturesdescriptoroptionalFeatures로 설정합니다.

  4. device현재 디바이스 획득 결과로 설정합니다. 인자로는 mode, requiredFeatures, optionalFeatures를 사용합니다.

  5. result요청된 기능 해석 결과로 설정합니다. 인자로는 requiredFeatures, optionalFeatures, mode를 사용합니다.

  6. resultnull이면 다음을 수행합니다:

    1. statusstate"denied"로 설정합니다.

    2. 이 단계를 중단합니다.

  7. result의 필드 (consentRequired, consentOptional, granted)를 가져옵니다.

  8. 이 시점에서 사용자 에이전트는 consentRequiredconsentOptional에 포함된 기능 사용에 대해 사용자의 권한을 요청할 수 있습니다. 이 프롬프트 결과는 해당 기능 활성화에 대한 사용자 의도 신호 판단에 포함되어야 합니다.

  9. consentRequired의 각 feature에 대해 다음을 수행합니다:

    1. 이 시점에서 사용자 에이전트는 feature 사용에 대해 사용자의 권한을 요청할 수 있습니다. 이 프롬프트 결과는 feature 활성화에 대한 사용자 의도 신호 판단에 포함되어야 합니다.

    2. feature 활성화에 대한 사용자 의도 신호가 명확하지 않으면, statusstate"denied"로 설정하고 이 단계를 중단합니다.

    3. featuregranted에 없다면, featuregranted에 추가합니다.

  10. consentOptional의 각 feature에 대해 다음을 수행합니다:

    1. 이 시점에서 사용자 에이전트는 feature 사용에 대해 사용자의 권한을 요청할 수 있습니다. 이 프롬프트 결과는 feature 활성화에 대한 사용자 의도 신호 판단에 포함되어야 합니다.

    2. feature 활성화에 대한 사용자 의도 신호가 명확하지 않으면, 다음 항목으로 넘어갑니다.

    3. featuregranted에 없다면, featuregranted에 추가합니다.

  11. statusgrantedgranted로 설정합니다.

  12. granted의 모든 요소를 devicegranted features 집합에 추가합니다. (해당 mode에 대해)

  13. statusstate"granted"로 설정합니다.

참고: 사용자 에이전트는 사용자 의도 신호를 판단할 때, 요청된 모든 기능에 대한 권한 프롬프트를 한 번에 묶어서 보여줄 수도 있고, 하나씩 순차적으로 보여줄 수도 있습니다.

참고: 웹 애플리케이션의 사용자 의도를 판단할 때, 사용자 에이전트는 반드시 해당 앱이 사용자가 명시적으로 웹 애플리케이션으로 실행했는지 확인해야 하며, 단순히 origin이 설치된 웹 애플리케이션과 일치하는지만 확인해서는 안 됩니다.

요청된 기능 해석(resolve the requested features)requiredFeaturesoptionalFeatures, XRSessionMode mode에 대해 수행하려면, 사용자 에이전트는 다음 단계를 반드시 수행해야 합니다:

  1. consentRequired를 비어 있는 리스트 DOMString로 설정한다.

  2. consentOptional를 비어 있는 리스트 DOMString로 설정한다.

  3. granted를 비어 있는 리스트 DOMString로 설정한다.

  4. devicemode, requiredFeatures, optionalFeatures에 대해 현재 디바이스 얻기의 결과로 설정한다.

  5. previouslyEnableddevice허용된 기능 집합(set of granted features)mode에 해당하는 것으로 설정한다.

  6. devicenull이거나 device지원 모드 목록(list of supported modes)mode가 포함되어 있지 않으면 다음 단계를 실행한다:

    1. 튜플(tuple) (consentRequired, consentOptional, granted)을 반환한다.

  7. mode와 연관된 기본 기능(default features) 테이블에 있는 모든 기능 기술자(feature descriptor)granted에 이미 존재하지 않는 경우 추가한다.

  8. requiredFeatures의 각 feature에 대해 다음 단계를 수행한다:

    1. featurenull이면 다음 항목으로 continue한다.

    2. feature가 올바른 기능 기술자(feature descriptor)가 아니면 null을 반환한다.

    3. feature가 이미 granted에 있으면 다음 항목으로 continue한다.

    4. 요청 문서의 originfeature requirements 테이블에 따라 feature에 필요한 권한 정책(permissions policy) 사용이 허용되지 않으면 null을 반환한다.

    5. sessionXR 디바이스feature가 설명하는 기능을 지원할 수 없거나 사용자 에이전트가 그 기능을 거부한 경우 null을 반환한다.

    6. feature가 설명하는 기능이 명시적 동의(explicit consent)를 필요로 하며 featurepreviouslyEnabled에 없다면 consentRequired에 추가한다.

    7. 그 외에는 featuregranted에 추가한다.

  9. optionalFeatures의 각 feature에 대해 다음 단계를 수행한다:

    1. featurenull이면 다음 항목으로 continue한다.

    2. feature가 올바른 기능 기술자(feature descriptor)가 아니면 다음 항목으로 continue한다.

    3. feature가 이미 granted에 있으면 다음 항목으로 continue한다.

    4. 요청 문서의 origin이 feature requirements 테이블에 따라 feature에 필요한 권한 정책(permissions policy) 사용이 허용되지 않으면 다음 항목으로 continue한다.

    5. sessionXR 디바이스feature가 설명하는 기능을 지원할 수 없거나 사용자 에이전트가 그 기능을 거부한 경우 다음 항목으로 continue한다.

    6. feature가 설명하는 기능이 명시적 동의(explicit consent)를 필요로 하며 featurepreviouslyEnabled에 없다면 consentOptional에 추가한다.

    7. 그 외에는 featuregranted에 추가한다.

  10. 튜플(tuple) (|consentRequired|, |consentOptional|, |granted|)을 반환한다.

변경 사항

2022년 3월 31일자 Candidate Recommendation Snapshot 이후 변경 사항

2020년 7월 24일자 Working Draft 이후 변경 사항

2019년 10월 10일자 Working Draft 이후 변경 사항

신규 기능:

변경 사항:

2019년 2월 5일자 First Public Working Draft 이후 변경 사항

신규 기능:

제거된 기능:

변경 사항:

15. 감사의 글

WebXR Device API 명세에 기여해주신 다음 분들께 감사드립니다:

그리고 이 모든 여정을 시작하게 해주신 Vladimir Vukicevic (Unity)께 특별히 감사드립니다!

적합성(Conformance)

문서 규약(Document conventions)

적합성 요구사항은 설명적 단언과 RFC 2119 용어의 조합으로 표현됩니다. 본 문서의 규범적 부분에서 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL”과 같은 주요 단어들은 RFC 2119에 명시된 대로 해석되어야 합니다. 다만, 가독성을 위해 이 단어들은 본 명세에서 모두 대문자로 표기되지 않습니다.

이 명세의 모든 텍스트는 명시적으로 비규범적임을 표시한 섹션, 예시, 참고를 제외하고는 규범적입니다. [RFC2119]

이 명세의 예시는 “for example”이라는 문구로 시작하거나, class="example"로 구분되어 규범적 텍스트와 구별됩니다. 예시는 다음과 같습니다:

이것은 정보 제공용 예시입니다.

참고(Informative note)는 “Note”라는 단어로 시작하며, class="note"로 규범적 텍스트와 구분됩니다. 예시는 다음과 같습니다:

참고, 이것은 정보 제공용 참고입니다.

적합한 알고리즘(Conformant Algorithms)

알고리즘의 일부로 명령문 형태로 표현된 요구사항(예: "선행 공백 문자를 모두 제거한다" 또는 "false를 반환하고 이 단계를 중단한다")는 알고리즘을 소개하는 데 사용된 주요 단어("must", "should", "may" 등)의 의미로 해석되어야 합니다.

알고리즘 또는 구체적 단계로 표현된 적합성 요구사항은 최종 결과가 동등하다면 어떤 방식으로든 구현할 수 있습니다. 특히, 본 명세에 정의된 알고리즘은 이해하기 쉽도록 작성된 것이며, 성능을 고려한 것이 아닙니다. 구현자들은 최적화를 권장합니다.

색인

이 명세서에서 정의된 용어

참조에 의해 정의된 용어

참조

규범적 참조

[DOM]
Anne van Kesteren. DOM 표준. Living Standard. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript 언어 명세서. URL: https://tc39.es/ecma262/multipage/
[GEOMETRY-1]
Simon Pieters; Chris Harrelson. Geometry Interfaces Module Level 1. 2018년 12월 4일. CR. URL: https://www.w3.org/TR/geometry-1/
[HR-TIME-3]
Yoav Weiss. 고해상도 시간. 2024년 11월 7일. WD. URL: https://www.w3.org/TR/hr-time-3/
[HTML]
Anne van Kesteren; 외. HTML 표준. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 표준. Living Standard. URL: https://infra.spec.whatwg.org/
[PERMISSIONS]
Marcos Caceres; Mike Taylor. Permissions. 2025년 9월 26일. WD. URL: https://www.w3.org/TR/permissions/
[PERMISSIONS-POLICY-1]
Ian Clelland. Permissions Policy. 2025년 8월 6일. WD. URL: https://www.w3.org/TR/permissions-policy-1/
[POINTEREVENTS]
Jacob Rossi; Matt Brubeck. 포인터 이벤트. 2019년 4월 4일. REC. URL: https://www.w3.org/TR/pointerevents/
[POINTERLOCK]
Vincent Scheib. 포인터 잠금. 2016년 10월 27일. REC. URL: https://www.w3.org/TR/pointerlock/
[REQUESTIDLECALLBACK]
Scott Haseley. requestIdleCallback(). 2025년 5월 21일. WD. URL: https://www.w3.org/TR/requestidlecallback/
[RFC2119]
S. Bradner. RFC에서 요구 수준을 나타내는 키워드. 1997년 3월. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBGL-2]
Dean Jackson; Jeff Gilbert. WebGL 2.0 명세서. 2017년 8월 12일. URL: https://www.khronos.org/registry/webgl/specs/latest/2.0/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 표준. Living Standard. URL: https://webidl.spec.whatwg.org/
[WEBXRLAYERS-1]
Rik Cabanier. WebXR Layers API Level 1. 2025년 9월 12일. WD. URL: https://www.w3.org/TR/webxrlayers-1/

비규범적 참조

[DEVICE-ORIENTATION]
Reilly Grant; Marcos Caceres. 디바이스 방향 및 모션(Device Orientation and Motion). 2025년 2월 12일. CRD. URL: https://www.w3.org/TR/orientation-event/
[WEBXR-AR-MODULE-1]
Brandon Jones; Manish Goregaokar; Rik Cabanier. WebXR 증강현실 모듈 - 레벨 1(WebXR Augmented Reality Module - Level 1). 2025년 4월 25일. CRD. URL: https://www.w3.org/TR/webxr-ar-module-1/

IDL 색인

partial interface Navigator {
  [SecureContext, SameObject] readonly attribute XRSystem xr;
};

[SecureContext, Exposed=Window] interface XRSystem : EventTarget {
  // Methods
  Promise<boolean> isSessionSupported(XRSessionMode mode);
  [NewObject] Promise<XRSession> requestSession(XRSessionMode mode, optional XRSessionInit options = {});

  // Events
  attribute EventHandler ondevicechange;
};

enum XRSessionMode {
  "inline",
  "immersive-vr",
  "immersive-ar"
};

dictionary XRSessionInit {
  sequence<DOMString> requiredFeatures;
  sequence<DOMString> optionalFeatures;
};

enum XRVisibilityState {
  "visible",
  "visible-blurred",
  "hidden",
};

[SecureContext, Exposed=Window] interface XRSession : EventTarget {
  // Attributes
  readonly attribute XRVisibilityState visibilityState;
  readonly attribute float? frameRate;
  readonly attribute Float32Array? supportedFrameRates;
  [SameObject] readonly attribute XRRenderState renderState;
  [SameObject] readonly attribute XRInputSourceArray inputSources;
  [SameObject] readonly attribute XRInputSourceArray trackedSources;
  readonly attribute FrozenArray<DOMString> enabledFeatures;
  readonly attribute boolean isSystemKeyboardSupported;

  // Methods
  undefined updateRenderState(optional XRRenderStateInit state = {});
  Promise<undefined> updateTargetFrameRate(float rate);
  [NewObject] Promise<XRReferenceSpace> requestReferenceSpace(XRReferenceSpaceType type);

  unsigned long requestAnimationFrame(XRFrameRequestCallback callback);
  undefined cancelAnimationFrame(unsigned long handle);

  Promise<undefined> end();

  // Events
  attribute EventHandler onend;
  attribute EventHandler oninputsourceschange;
  attribute EventHandler onselect;
  attribute EventHandler onselectstart;
  attribute EventHandler onselectend;
  attribute EventHandler onsqueeze;
  attribute EventHandler onsqueezestart;
  attribute EventHandler onsqueezeend;
  attribute EventHandler onvisibilitychange;
  attribute EventHandler onframeratechange;
};

dictionary XRRenderStateInit {
  double depthNear;
  double depthFar;
  boolean passthroughFullyObscured;
  double inlineVerticalFieldOfView;
  XRWebGLLayer? baseLayer;
  sequence<XRLayer>? layers;
};

[SecureContext, Exposed=Window] interface XRRenderState {
  readonly attribute double depthNear;
  readonly attribute double depthFar;
  readonly attribute boolean? passthroughFullyObscured;
  readonly attribute double? inlineVerticalFieldOfView;
  readonly attribute XRWebGLLayer? baseLayer;
};

callback XRFrameRequestCallback = undefined (DOMHighResTimeStamp time, XRFrame frame);

[SecureContext, Exposed=Window] interface XRFrame {
  [SameObject] readonly attribute XRSession session;
  readonly attribute DOMHighResTimeStamp predictedDisplayTime;

  XRViewerPose? getViewerPose(XRReferenceSpace referenceSpace);
  XRPose? getPose(XRSpace space, XRSpace baseSpace);
};

[SecureContext, Exposed=Window] interface XRSpace : EventTarget {

};

enum XRReferenceSpaceType {
  "viewer",
  "local",
  "local-floor",
  "bounded-floor",
  "unbounded"
};

[SecureContext, Exposed=Window]
interface XRReferenceSpace : XRSpace {
  [NewObject] XRReferenceSpace getOffsetReferenceSpace(XRRigidTransform originOffset);

  attribute EventHandler onreset;
};

[SecureContext, Exposed=Window]
interface XRBoundedReferenceSpace : XRReferenceSpace {
  readonly attribute FrozenArray<DOMPointReadOnly> boundsGeometry;
};

[SecureContext, Exposed=Window] interface mixin XRViewGeometry {
  readonly attribute Float32Array projectionMatrix;
  [SameObject] readonly attribute XRRigidTransform transform;
};

enum XREye {
  "none",
  "left",
  "right"
};

[SecureContext, Exposed=Window] interface XRView {
  readonly attribute XREye eye;
  readonly attribute unsigned long index;
  readonly attribute double? recommendedViewportScale;

  undefined requestViewportScale(double? scale);
};

XRView includes XRViewGeometry;

[SecureContext, Exposed=Window] interface XRViewport {
  readonly attribute long x;
  readonly attribute long y;
  readonly attribute long width;
  readonly attribute long height;
};

[SecureContext, Exposed=Window]
interface XRRigidTransform {
  constructor(optional DOMPointInit position = {}, optional DOMPointInit orientation = {});
  [SameObject] readonly attribute DOMPointReadOnly position;
  [SameObject] readonly attribute DOMPointReadOnly orientation;
  readonly attribute Float32Array matrix;
  [SameObject] readonly attribute XRRigidTransform inverse;
};

[SecureContext, Exposed=Window] interface XRPose {
  [SameObject] readonly attribute XRRigidTransform transform;
  [SameObject] readonly attribute DOMPointReadOnly? linearVelocity;
  [SameObject] readonly attribute DOMPointReadOnly? angularVelocity;

  readonly attribute boolean emulatedPosition;
};

[SecureContext, Exposed=Window] interface XRViewerPose : XRPose {
  [SameObject] readonly attribute FrozenArray<XRView> views;
};

enum XRHandedness {
  "none",
  "left",
  "right"
};

enum XRTargetRayMode {
  "gaze",
  "tracked-pointer",
  "screen",
  "transient-pointer"
};

[SecureContext, Exposed=Window]
interface XRInputSource {
  readonly attribute XRHandedness handedness;
  readonly attribute XRTargetRayMode targetRayMode;
  [SameObject] readonly attribute XRSpace targetRaySpace;
  [SameObject] readonly attribute XRSpace? gripSpace;
  [SameObject] readonly attribute FrozenArray<DOMString> profiles;
  readonly attribute boolean skipRendering;
};

[SecureContext, Exposed=Window]
interface XRInputSourceArray {
  iterable<XRInputSource>;
  readonly attribute unsigned long length;
  getter XRInputSource(unsigned long index);
};

[SecureContext, Exposed=Window]
interface XRLayer : EventTarget {};


typedef (WebGLRenderingContext or
         WebGL2RenderingContext) XRWebGLRenderingContext;

dictionary XRWebGLLayerInit {
  boolean antialias = true;
  boolean depth = true;
  boolean stencil = false;
  boolean alpha = true;
  boolean ignoreDepthValues = false;
  double framebufferScaleFactor = 1.0;
};

[SecureContext, Exposed=Window]
interface XRWebGLLayer: XRLayer {
  constructor(XRSession session,
             XRWebGLRenderingContext context,
             optional XRWebGLLayerInit layerInit = {});
  // Attributes
  readonly attribute boolean antialias;
  readonly attribute boolean ignoreDepthValues;
  attribute float? fixedFoveation;

  [SameObject] readonly attribute WebGLFramebuffer? framebuffer;
  readonly attribute unsigned long framebufferWidth;
  readonly attribute unsigned long framebufferHeight;

  // Methods
  XRViewport? getViewport(XRView view);

  // Static Methods
  static double getNativeFramebufferScaleFactor(XRSession session);
};

partial dictionary WebGLContextAttributes {
    boolean xrCompatible = false;
};

partial interface mixin WebGLRenderingContextBase {
    [NewObject] Promise<undefined> makeXRCompatible();
};

[SecureContext, Exposed=Window]
interface XRSessionEvent : Event {
  constructor(DOMString type, XRSessionEventInit eventInitDict);
  [SameObject] readonly attribute XRSession session;
};

dictionary XRSessionEventInit : EventInit {
  required XRSession session;
};

[SecureContext, Exposed=Window]
interface XRInputSourceEvent : Event {
  constructor(DOMString type, XRInputSourceEventInit eventInitDict);
  [SameObject] readonly attribute XRFrame frame;
  [SameObject] readonly attribute XRInputSource inputSource;
};

dictionary XRInputSourceEventInit : EventInit {
  required XRFrame frame;
  required XRInputSource inputSource;
};

[SecureContext, Exposed=Window]
interface XRInputSourcesChangeEvent : Event {
  constructor(DOMString type, XRInputSourcesChangeEventInit eventInitDict);
  [SameObject] readonly attribute XRSession session;
  [SameObject] readonly attribute FrozenArray<XRInputSource> added;
  [SameObject] readonly attribute FrozenArray<XRInputSource> removed;
};

dictionary XRInputSourcesChangeEventInit : EventInit {
  required XRSession session;
  required sequence<XRInputSource> added;
  required sequence<XRInputSource> removed;

};

[SecureContext, Exposed=Window]
interface XRReferenceSpaceEvent : Event {
  constructor(DOMString type, XRReferenceSpaceEventInit eventInitDict);
  [SameObject] readonly attribute XRReferenceSpace referenceSpace;
  [SameObject] readonly attribute XRRigidTransform? transform;
};

dictionary XRReferenceSpaceEventInit : EventInit {
  required XRReferenceSpace referenceSpace;
  XRRigidTransform? transform = null;
};

[SecureContext, Exposed=Window]
interface XRVisibilityMaskChangeEvent : Event {
  constructor(DOMString type, XRVisibilityMaskChangeEventInit eventInitDict);
  [SameObject] readonly attribute XRSession session;
  readonly attribute XREye eye;
  readonly attribute unsigned long index;
  [SameObject] readonly attribute Float32Array vertices;
  [SameObject] readonly attribute Uint32Array indices;
};

dictionary XRVisibilityMaskChangeEventInit : EventInit {
  required XRSession session;
  required XREye eye;
  required unsigned long index;
  required Float32Array vertices;
  required Uint32Array indices;
};

dictionary XRSessionSupportedPermissionDescriptor: PermissionDescriptor {
  XRSessionMode mode;
};

dictionary XRPermissionDescriptor: PermissionDescriptor {
  XRSessionMode mode;
  sequence<DOMString> requiredFeatures;
  sequence<DOMString> optionalFeatures;
};

[Exposed=Window]
interface XRPermissionStatus: PermissionStatus {
  attribute FrozenArray<DOMString> granted;
};

MDN

Navigator/xr

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

WebGLRenderingContext/makeXRCompatible

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?

WebGLRenderingContext/makeXRCompatible

In only one current engine.

FirefoxNoneSafariNoneChrome79+
OperaNoneEdge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera MobileNone
MDN

XRBoundedReferenceSpace/boundsGeometry

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRBoundedReferenceSpace

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRFrame/getPose

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRFrame/getViewerPose

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRFrame/session

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRFrame

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSource/gripSpace

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSource/handedness

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSource/profiles

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSource/targetRayMode

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSource/targetRaySpace

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSource

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourceArray/length

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourceArray

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourceEvent/XRInputSourceEvent

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourceEvent/frame

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourceEvent/inputSource

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourceEvent

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourcesChangeEvent/XRInputSourcesChangeEvent

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourcesChangeEvent/added

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourcesChangeEvent/removed

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourcesChangeEvent/session

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRInputSourcesChangeEvent

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRLayer

In only one current engine.

FirefoxNoneSafariNoneChrome84+
Opera?Edge84+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

XRPose/angularVelocity

In no current engines.

FirefoxNoneSafariNoneChromeNone
Opera?EdgeNone
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRPose/emulatedPosition

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRPose/linearVelocity

In no current engines.

FirefoxNoneSafariNoneChromeNone
Opera?EdgeNone
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRPose/transform

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRPose

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRReferenceSpace/getOffsetReferenceSpace

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRReferenceSpace/reset_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRReferenceSpace/reset_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRReferenceSpace

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRReferenceSpaceEvent/XRReferenceSpaceEvent

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRReferenceSpaceEvent/referenceSpace

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRReferenceSpaceEvent/transform

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRReferenceSpaceEvent

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRRenderState/baseLayer

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRRenderState/depthFar

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRRenderState/depthNear

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRRenderState/inlineVerticalFieldOfView

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRRenderState

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRRigidTransform/XRRigidTransform

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRRigidTransform/inverse

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRRigidTransform/matrix

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRRigidTransform/orientation

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRRigidTransform/position

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRRigidTransform

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/cancelAnimationFrame

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/end

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/end_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/end_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/inputSources

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/inputsourceschange_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/inputsourceschange_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/renderState

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/requestAnimationFrame

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/requestReferenceSpace

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/select_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/select_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/selectend_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/selectend_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/selectstart_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/selectstart_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/squeeze_event

In only one current engine.

FirefoxNoneSafariNoneChrome83+
Opera?Edge83+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSession/squeeze_event

In only one current engine.

FirefoxNoneSafariNoneChrome83+
Opera?Edge83+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSession/squeezeend_event

In only one current engine.

FirefoxNoneSafariNoneChrome83+
Opera?Edge83+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSession/squeezeend_event

In only one current engine.

FirefoxNoneSafariNoneChrome83+
Opera?Edge83+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSession/squeezestart_event

In only one current engine.

FirefoxNoneSafariNoneChrome83+
Opera?Edge83+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSession/squeezestart_event

In only one current engine.

FirefoxNoneSafariNoneChrome83+
Opera?Edge83+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSession/updateRenderState

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/visibilitychange_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/visibilitychange_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession/visibilityState

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSession

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSessionEvent/XRSessionEvent

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSessionEvent/session

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSessionEvent

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSpace

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRSystem/devicechange_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSystem/devicechange_event

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSystem/isSessionSupported

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSystem/requestSession

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRSystem

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRView/eye

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRView/recommendedViewportScale

In only one current engine.

FirefoxNoneSafariNoneChrome90+
Opera?Edge90+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRView/requestViewportScale

In only one current engine.

FirefoxNoneSafariNoneChrome90+
Opera?Edge90+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

XRView

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRViewerPose/views

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRViewerPose

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRViewport/height

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRViewport/width

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRViewport/x

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRViewport/y

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRViewport

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer/XRWebGLLayer

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer/antialias

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer/fixedFoveation

In no current engines.

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

XRWebGLLayer/framebuffer

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer/framebufferHeight

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer/framebufferWidth

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer/getNativeFramebufferScaleFactor_static

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer/getViewport

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer/ignoreDepthValues

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

XRWebGLLayer

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet11.2+Opera Mobile?
MDN

Headers/Feature-Policy/xr-spatial-tracking

In only one current engine.

FirefoxNoneSafariNoneChrome79+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera MobileNone

Headers/Permissions-Policy/xr-spatial-tracking

In only one current engine.

FirefoxNoneSafariNoneChrome88+
Opera?Edge88+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera MobileNone