몰입형 웹에 오신 것을 환영합니다.

몰입형 웹은 브라우저를 통해 호스팅되는 가상 세계를 의미합니다. 이 모든 가상 현실 경험은 브라우저나 VR 지원 헤드셋에 표시됩니다.

Joe Medley
Joe Medley

몰입형 웹은 브라우저를 통해 호스팅되는 가상 세계 경험을 의미합니다. 여기에는 Google의 Daydream, Oculus Rift, 삼성 Gear VR, HTC Vive, Windows Mixed Reality 헤드셋과 같은 브라우저 또는 VR 지원 헤드셋에 표시되는 전체 가상 현실 (VR) 경험은 물론 AR 지원 휴대기기용으로 개발된 증강 현실 환경이 포함됩니다.

몰입형 경험을 설명하기 위해 두 가지 용어를 사용하지만, 이 두 용어는 완전한 현실에서 완전한 몰입형 VR 환경까지의 스펙트럼으로 간주해야 하며, 그 사이에 다양한 수준의 AR이 있습니다.

몰입형 환경의 예는 다음과 같습니다.

  • 몰입형 360° 동영상
  • 몰입감 넘치는 환경에서 제공되는 전통적인 2D (또는 3D) 동영상
  • 데이터 시각화
  • 홈쇼핑
  • 아트
  • 누구도 떠올리지 못한 멋진 음악

거기에 어떻게 가나요?

이 몰입형 웹은 이제 거의 1년 동안 배아 형태로 사용할 수 있게 되었습니다. 이는 Chrome 62부터 오리진 트라이얼에서 제공된 WebVR 1.1 API를 통해 이루어졌습니다. 이 API는 Firefox 및 Edge와 Safari용 polyfill에서도 지원됩니다.

하지만 이제 다음 문제로 넘어갈 차례입니다.

오리진 트라이얼은 2018년 7월 24일에 종료되었으며 WebXR Device API와 새로운 오리진 트라이얼로 사양이 대체되었습니다.

WebVR 1.1은 어떻게 되었나요?

WebVR 1.1에서 많은 것을 배웠지만 시간이 지남에 따라 개발자가 빌드하려는 애플리케이션 유형을 지원하려면 몇 가지 주요 변경사항이 필요하다는 사실이 명확해졌습니다. 여기서 다룬 전체 교훈을 설명하기에는 너무 길지만 API가 명시적으로 기본 JavaScript 스레드에 연결되어 있는 문제, 개발자가 명백히 잘못된 구성을 설정할 수 있는 너무 많은 기회, 매직 창이 의도된 기능이 아닌 부작용이 되는 것과 같은 일반적인 용도가 포함되어 있습니다. 매직 창은 헤드셋 없이 몰입형 콘텐츠를 보는 기법으로, 앱이 기기의 방향 센서를 기반으로 단일 뷰를 렌더링합니다.

새로운 디자인은 더 간단한 구현과 대규모 성능 개선을 용이하게 합니다. 이와 동시에 AR 및 기타 사용 사례가 새롭게 떠오르면서 향후 이러한 사용 사례를 지원할 수 있도록 API를 확장하는 것이 중요해졌습니다.

WebXR Device API는 이러한 확장된 사용 사례를 염두에 두고 설계되고 명명되었으며 더 나은 경로를 제공합니다. WebVR 구현자는 WebXR Device API로의 이전을 약정했습니다.

WebXR Device API란 무엇인가요?

이전의 WebVR 사양과 마찬가지로 WebXR Device API는 Google, Microsoft, Mozilla 등의 기여자가 있는 Immersive Web Community Group의 제품입니다. XR의 'X는 몰입형 환경 스펙트럼의 모든 것을 나타내는 일종의 대수적 변수로 고안되었습니다. 앞서 언급한 오리진 트라이얼polyfill을 통해 사용할 수 있습니다.

Chrome 67 베타 기간 중에 이 문서가 처음 게시되었을 때는 VR 기능만 사용 설정되었습니다. Chrome 69에서 증강 현실을 만나보세요. 자세한 내용은 웹용 증강 현실을 참고하세요.

이 새로운 API에는 이 같은 글에서 다룰 수 있는 것보다 더 많은 내용이 있습니다. WebXR 샘플을 이해하는 데 충분한 정보를 제공해 드리겠습니다. 자세한 내용은 원래 설명몰입형 웹 얼리 어답터 가이드를 참고하세요. 오리진 트라이얼이 진행됨에 따라 후자를 확장할 예정입니다. 언제든지 문제를 제기하거나 pull 요청을 제출하세요.

이 문서에서는 XR 세션의 시작, 중지, 실행과 입력 처리에 관한 몇 가지 기본사항에 관해 설명합니다.

AR/VR 콘텐츠를 화면에 그리는 방법은 다루지 않겠습니다. WebXR Device API는 이미지 렌더링 기능을 제공하지 않습니다. 광고주가 직접 결정합니다. WebGL API를 사용하여 그리기를 수행합니다. 정말 야심 찬 일이라면 그렇게 할 수 있습니다. 하지만 프레임워크를 사용하는 것이 좋습니다. 몰입형 웹 샘플은 Cottontail이라는 데모 전용 웹 샘플을 사용합니다. Three.js는 5월부터 WebXR을 지원했습니다. A-Frame에 대해서는 아무것도 듣지 못했습니다.

앱 시작 및 실행

기본 프로세스는 다음과 같습니다.

  1. XR 기기를 요청합니다.
  2. 사용 가능한 경우 XR 세션을 요청합니다. 사용자가 헤드셋에 휴대전화를 착용하도록 하려면 몰입형 세션이라고 하며 이 세션에 들어가려면 사용자 동작이 필요합니다.
  3. 세션을 사용하여 초당 60개의 이미지 프레임을 제공하는 렌더링 루프를 실행합니다. 각 프레임에서 화면에 적절한 콘텐츠를 그립니다.
  4. 사용자가 종료할 때까지 렌더링 루프를 실행합니다.
  5. XR 세션을 종료합니다.

좀 더 자세히 살펴보고 몇 가지 코드를 포함시켜 보겠습니다. 지금 보여드리는 것은 앱을 실행할 수 없습니다. 그러나 다시 한번 말씀드리자면 이것은 단지 이해하기 위한 것입니다.

XR 기기 요청

여기에서는 표준 특성 감지 코드를 알아볼 수 있습니다. 이를 checkForXR()와 같은 함수로 래핑할 수 있습니다.

몰입형 세션을 사용하지 않는 경우 기능 광고 및 사용자 동작 가져오기를 건너뛰고 바로 세션 요청으로 진행할 수 있습니다. 몰입형 세션은 헤드셋이 필요한 세션입니다. 비몰입형 세션은 단순히 기기 화면에 콘텐츠를 표시합니다. 전자는 가상 현실 또는 증강 현실이라고 할 때 대부분의 사람들이 떠올리는 것입니다. 후자를 '매직 창'이라고도 합니다.

if (navigator.xr) {
    navigator.xr.requestDevice()
    .then(xrDevice => {
    // Advertise the AR/VR functionality to get a user gesture.
    })
    .catch(err => {
    if (err.name === 'NotFoundError') {
        // No XRDevices available.
        console.error('No XR devices available:', err);
    } else {
        // An error occurred while requesting an XRDevice.
        console.error('Requesting XR device failed:', err);
    }
    })
} else{
    console.log("This browser does not support the WebXR API.");
}

XR 세션 요청

이제 기기와 사용자 동작이 있으므로 세션을 가져올 차례입니다. 세션을 만들려면 브라우저에 그릴 캔버스가 필요합니다.

xrPresentationContext = htmlCanvasElement.getContext('xrpresent');
let sessionOptions = {
    // The immersive option is optional for non-immersive sessions; the value
    //   defaults to false.
    immersive: false,
    outputContext: xrPresentationContext
}
xrDevice.requestSession(sessionOptions)
.then(xrSession => {
    // Use a WebGL context as a base layer.
    xrSession.baseLayer = new XRWebGLLayer(session, gl);
    // Start the render loop
})

렌더링 루프 실행

이 단계의 코드는 약간의 해제가 필요합니다. 이 문제를 풀기 위해 몇 마디를 던지려고 해요. 최종 코드를 미리 보려면 바로 이동하여 빠르게 살펴본 다음 돌아와서 전체 설명을 확인하세요. 추론하지 못할 수도 있는 부분이 꽤 많습니다.

렌더링 루프의 기본 프로세스는 다음과 같습니다.

  1. 애니메이션 프레임을 요청합니다.
  2. 기기 위치를 쿼리합니다.
  3. 기기의 위치를 기준으로 기기의 위치에 콘텐츠를 그립니다.
  4. 입력 기기에 필요한 작업을 실행합니다.
  5. 사용자가 종료할 때까지 1초에 60회 반복합니다.

프레젠테이션 프레임 요청

'프레임'이라는 단어는 Web XR 맥락에서 여러 의미를 갖습니다. 첫 번째 요소는 좌표계의 원점이 계산되는 위치와 기기가 이동할 때 해당 원점에 어떤 일이 일어나는지 정의하는 참조 프레임입니다. (사용자가 움직일 때 뷰가 동일하게 유지되나요, 아니면 실제와 같이 이동하나요?)

두 번째 프레임 유형은 XRFrame 객체로 표시되는 프레젠테이션 프레임입니다. 이 객체에는 AR/VR 장면의 단일 프레임을 기기에 렌더링하는 데 필요한 정보가 포함되어 있습니다. 이는 requestAnimationFrame()를 호출하여 프레젠테이션 프레임을 가져오기 때문에 약간 혼란스러울 수 있습니다. 이렇게 하면 window.requestAnimationFrame()와 호환됩니다.

더 자세히 설명하기 전에 몇 가지 코드를 제공하겠습니다. 아래 샘플은 렌더링 루프가 시작 및 유지되는 방법을 보여줍니다. 워드 프레임의 이중적인 용도에 주목하세요. requestAnimationFrame()의 재귀 호출을 확인합니다. 이 함수는 1초에 60회 호출됩니다.

xrSession.requestFrameOfReference('eye-level')
.then(xrFrameOfRef => {
    xrSession.requestAnimationFrame(onFrame(time, xrFrame) {
    // The time argument is for future use and not implemented at this time.
    // Process the frame.
    xrFrame.session.requestAnimationFrame(onFrame);
    }
});

자세

화면에 무언가를 그리기 전에 디스플레이 기기가 가리키는 위치를 알아야 하며 화면에 액세스할 수 있어야 합니다. 일반적으로 AR/VR에서 사물의 위치와 방향을 포즈라고 합니다. 뷰어와 입력 장치 모두 포즈가 있습니다. 입력 장치는 나중에 다루겠습니다. 뷰어 및 입력 기기 포즈는 모두 열 상위 순서로 Float32Array에 저장된 4x4 행렬로 정의됩니다. 현재 애니메이션 프레임 객체에서 XRFrame.getDevicePose()를 호출하여 뷰어의 포즈를 가져옵니다. 항상 포즈가 돌아왔는지 테스트하세요. 문제가 발생했다면 화면에 그리려고 하지 않을 겁니다.

let pose = xrFrame.getDevicePose(xrFrameOfRef);
if (pose) {
    // Draw something to the screen.
}

조회수

포즈를 확인했다면 이제 무언가를 그려 볼 차례입니다. 그리는 객체를 뷰 (XRView)라고 합니다. 여기에서 세션 유형이 중요합니다. 뷰는 XRFrame 객체에서 배열로 검색됩니다. 비몰입형 세션인 경우 배열에 하나의 뷰가 있습니다. 몰입형 세션에 있는 경우 배열에는 각 눈에 하나씩 두 개가 있습니다.

for (let view of xrFrame.views) {
    // Draw something to the screen.
}

이는 WebXR과 다른 몰입형 시스템 간의 중요한 차이점입니다. 하나의 뷰를 반복하는 것이 무의미해 보일 수 있지만, 이렇게 하면 다양한 기기에서 단일 렌더링 경로를 사용할 수 있습니다.

전체 렌더링 루프

이 모든 것을 합치면 아래의 코드가 생성됩니다. 입력 장치의 자리표시자를 남겼습니다. 이 부분은 이후 섹션에서 다룰 것입니다.

xrSession.requestFrameOfReference('eye-level')
.then(xrFrameOfRef => {
    xrSession.requestAnimationFrame(onFrame(time, xrFrame) {
    // The time argument is for future use and not implemented at this time.
    let pose = xrFrame.getDevicePose(xrFrameOfRef);
    if (pose) {
        for (let view of xrFrame.views) {
        // Draw something to the screen.
        }
    }
    // Input device code will go here.
    frame.session.requestAnimationFrame(onFrame);
    }
}

XR 세션 종료

XR 세션은 XRSession.end() 호출을 통해 자체 코드에 의해 종료되는 등 여러 가지 이유로 종료될 수 있습니다. 다른 원인으로는 헤드셋 연결이 끊어지거나 다른 애플리케이션이 헤드셋을 제어하는 경우 등이 있습니다. 따라서 제대로 작동하는 애플리케이션은 종료 이벤트를 모니터링하고 종료 이벤트가 발생하면 세션 및 렌더기 객체를 삭제해야 합니다. 일단 종료되면 XR 세션은 재개할 수 없습니다.

xrDevice.requestSession(sessionOptions)
.then(xrSession => {
    // Create a WebGL layer and initialize the render loop.
    xrSession.addEventListener('end', onSessionEnd);
});

// Restore the page to normal after immersive access has been released.
function onSessionEnd() {
    xrSession = null;

    // Ending the session stops executing callbacks passed to the XRSession's
    // requestAnimationFrame(). To continue rendering, use the window's
    // requestAnimationFrame() function.
    window.requestAnimationFrame(onDrawFrame);
}

상호작용은 어떻게 작동하나요?

이제 애플리케이션의 전체 기간과 마찬가지로 AR 또는 VR에서 객체와 상호작용하는 방법을 보여드리겠습니다.

WebXR Device API는 사용자 입력에 '포인트 앤 클릭' 접근 방식을 사용합니다. 이 접근 방식을 사용하면 모든 입력 소스에 입력 기기가 가리키는 위치를 나타내는 포인터 레이와 무언가가 선택되었을 때를 나타내는 이벤트가 정의됩니다. 앱이 포인터 광선을 그리고 그 포인터가 가리키는 곳을 표시합니다. 사용자가 입력 기기를 클릭하면 select, selectStart, selectEnd 등의 이벤트가 실행됩니다. 앱은 클릭된 항목을 파악하고 적절하게 응답합니다.

입력 장치 및 포인터 광선

사용자에게 포인터 광선은 컨트롤러와 사용자가 가리키는 대상 사이의 희미한 선에 불과합니다. 그러나 앱은 그것을 그려야 합니다. 즉, 입력 기기의 포즈를 가져와 해당 위치에서 AR/VR 공간의 객체에 선을 그립니다. 이 과정은 대략 다음과 같습니다.

let inputSources = xrSession.getInputSources();
for (let xrInputSource of inputSources) {
    let inputPose = frame.getInputPose(inputSource, xrFrameOfRef);
    if (!inputPose) {
    continue;
    }
    if (inputPose.gripMatrix) {
    // Render a virtual version of the input device
    //   at the correct position and orientation.
    }
    if (inputPose.pointerMatrix) {
    // Draw a ray from the gripMatrix to the pointerMatrix.
    }
}

이 이미지는 몰입형 웹 커뮤니티 그룹의 입력 추적 샘플의 요약 버전입니다. 프레임 렌더링과 마찬가지로 포인터 광선과 기기를 그리는 것은 개발자의 몫입니다. 앞서 언급했듯이 이 코드는 렌더링 루프의 일부로 실행되어야 합니다.

가상 공간에서 항목 선택

단순히 AR/VR에서 사물을 가리키는 것만으로는 소용이 없습니다. 유용한 작업을 하려면 사용자가 항목을 선택할 수 있어야 합니다. WebXR Device API는 사용자 상호작용에 응답하기 위한 세 가지 이벤트(select, selectStart, selectEnd)를 제공합니다. 제가 예상하지 못한 특이한 점이 있습니다. 단지 입력 장치가 클릭되었다는 것뿐입니다. 환경에서 어떤 항목을 클릭했는지는 알려주지 않습니다. 이벤트 핸들러는 XRSession 객체에 추가되며, 사용 가능한 상태가 되는 즉시 추가해야 합니다.

xrDevice.requestSession(sessionOptions)
.then(xrSession => {
    // Create a WebGL layer and initialize the render loop.
    xrSession.addEventListener('selectstart', onSelectStart);
    xrSession.addEventListener('selectend', onSelectEnd);
    xrSession.addEventListener('select', onSelect);
});

자세한 내용을 원하는 경우를 위해 이 코드는 입력 선택 예를 기반으로 합니다.

클릭된 항목을 파악하려면 포즈를 사용해 보세요. (놀랍나요? 그렇게 생각하지 않았습니다.) 자세한 내용은 앱 또는 사용 중인 프레임워크에 한정되며 이 도움말의 범위를 벗어납니다. Cottontail의 접근 방식은 입력 선택 예에 있습니다.

function onSelect(ev) {
    let inputPose = ev.frame.getInputPose(ev.inputSource, xrFrameOfRef);
    if (!inputPose) {
    return;
    }
    if (inputPose.pointerMatrix) {
    // Figure out what was clicked and respond.
    }
}

결론: 향후 계획

앞서 말씀드린 바와 같이 Chrome 69 (2018년 6월 카나리아 출시 예정)에서 증강 현실이 필요합니다. 그럼에도 불구하고, 여러분도 지금까지 준비한 방법을 시도해 보실 것을 권합니다. 개선을 위해서는 의견이 필요합니다. ChromeStatus.com의 WebXR Hit Test를 통해 진행 상황을 확인하세요. WebXR 앵커를 따라 자세 추적을 개선할 수도 있습니다.