증강 현실: 이미 알고 계신가요?

이미 WebXR Device API를 사용해 본 적이 있다면 거의 완료된 것입니다.

Joe Medley
Joe Medley

WebXR Device API는 지난 가을에 Chrome 79에 출시되었습니다. 당시 언급했듯이 Chrome의 API 구현은 아직 진행 중입니다. Chrome은 일부 작업이 완료되었음을 알려드립니다. Chrome 81에는 두 가지 새로운 기능이 추가되었습니다.

이 문서에서는 증강 현실을 다룹니다. 이미 WebXR Device API를 사용해 본 적이 있다면 알아야 할 새로운 내용이 거의 없다는 점에 안심하세요. WebXR 세션을 시작하는 방법도 거의 같습니다. 프레임 루프를 실행하는 방법도 거의 같습니다. 차이점은 증강 현실에 콘텐츠를 적절하게 표시할 수 있는 구성에 있습니다. WebXR의 기본 개념에 익숙하지 않다면 WebXR Device API에 관한 이전 게시물을 읽어보거나 적어도 거기에서 다루는 주제를 숙지해야 합니다. 세션을 요청하고 들어가는 방법과 프레임 루프를 실행하는 방법을 알아야 합니다.

히트 테스트에 관한 자세한 내용은 실제 뷰에서 가상 객체 배치 관련 도움말을 참고하세요. 이 도움말의 코드는 몰입형 웹 작업 그룹의 WebXR Device API 샘플에 있는 몰입형 AR 세션 샘플(데모소스)을 기반으로 합니다.

코딩을 시작하기 전에 몰입형 AR 세션 샘플을 한 번 이상 사용해야 합니다. Chrome 81 이상을 실행하는 최신 Android 휴대전화가 필요합니다.

어디에 유용한가요?

증강 현실은 많은 기존 또는 신규 웹페이지에서 브라우저를 나가지 않고도 AR 사용 사례를 구현할 수 있도록 하는 유용한 추가 기능이 될 것입니다. 예를 들어 교육 사이트에서 학습하는 데 도움이 되며, 잠재적 구매자가 쇼핑하는 동안 집에 있는 물건을 시각화할 수 있습니다.

두 번째 사용 사례를 고려하세요. 실제 장면에 가상 물체의 실물 크기 표현을 배치하는 시뮬레이션을 생각해 보세요. 배치되면 이미지는 선택한 노출 영역에 머물고 실제 항목이 노출 영역에 있는 것처럼 표시되며 사용자가 이미지를 둘러보거나 이미지에 더 가까이 또는 더 멀리 이동할 수 있습니다. 이를 통해 시청자는 2차원 이미지로 파악할 수 있는 것보다 객체를 더 깊이 이해할 수 있습니다.

제 자신도 조금 앞서고 있어요. 설명한 작업을 실제로 실행하려면 AR 기능과 표면을 감지하는 수단이 필요합니다. 이 도움말에서는 전자를 다룹니다. WebXR 히트 테스트 API에 관한 함께 제공되는 도움말(위 링크 참고)에서는 후자를 다룹니다.

세션 요청

세션을 요청하는 방법은 이전에 본 것과 매우 유사합니다. 먼저 xr.isSessionSupported()를 호출하여 원하는 세션 유형을 현재 기기에서 사용할 수 있는지 확인합니다. 이전과 같이 'immersive-vr'를 요청하는 대신 'immersive-ar'를 요청합니다.

if (navigator.xr) {
  const supported = await navigator.xr.isSessionSupported('immersive-ar');
  if (supported) {
    xrButton.addEventListener('click', onButtonClicked);
    xrButton.textContent = 'Enter AR';
    xrButton.enabled = supported; // supported is Boolean
  }
}

이전과 마찬가지로 이렇게 하면 'AR 들어가기' 버튼이 사용 설정됩니다. 사용자가 클릭하면 xr.requestSession()를 호출하고 'immersive-ar'도 전달합니다.

let xrSession = null;
function onButtonClicked() {
  if (!xrSession) {
    navigator.xr.requestSession('immersive-ar')
    .then((session) => {
      xrSession = session;
      xrSession.isImmersive = true;
      xrButton.textContent = 'Exit AR';
      onSessionStarted(xrSession);
    });
  } else {
    xrSession.end();
  }
}

편의 속성

마지막 코드 샘플에서 두 줄을 강조 표시했음을 눈치채셨을 것입니다. XRSession 객체에 isImmersive라는 속성이 있는 것처럼 보입니다. 이 속성은 내가 직접 만든 편의 속성이므로 사양의 일부가 아닙니다. 나중에 시청자에게 표시할 항목을 결정하는 데 사용합니다. 이 속성이 API의 일부가 아닌 이유는 무엇인가요? 앱에서 이 속성을 다르게 추적해야 할 수 있으므로 사양 작성자는 API를 깔끔하게 유지하기로 결정했습니다.

세션 시작

이전 도움말에서 onSessionStarted()의 형태를 떠올려 보세요.

function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);

  let canvas = document.createElement('canvas');
  gl = canvas.getContext('webgl', { xrCompatible: true });

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

  xrSession.requestReferenceSpace('local-floor')
  .then((refSpace) => {
    xrRefSpace = refSpace;
    xrSession.requestAnimationFrame(onXRFrame);
  });
}

증강 현실 렌더링을 고려하여 몇 가지 항목을 추가해야 합니다. 배경을 사용 중지합니다. 먼저 배경이 필요한지 확인합니다. 여기에서 편의 속성을 처음 사용합니다.

function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);

  if (session.isImmersive) {
    removeBackground();
  }

  let canvas = document.createElement('canvas');
  gl = canvas.getContext('webgl', { xrCompatible: true });

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

  refSpaceType = xrSession.isImmersive ? 'local' : 'viewer';
  xrSession.requestReferenceSpace(refSpaceType).then((refSpace) => {
    xrSession.requestAnimationFrame(onXRFrame);
  });

}

참조 공간

이전 도움말에서는 참조 스페이스를 간략히 살펴봤습니다. 여기서 설명하는 샘플은 두 가지를 사용하므로 이제 그 생략을 정정해야 합니다.

참조 공간은 가상 세계와 사용자의 물리적 환경 간의 관계를 설명합니다. 이 작업은 다음과 같은 방식으로 이루어집니다.

  • 가상 세계의 위치를 표현하는 데 사용되는 좌표계의 원점을 지정합니다.
  • 사용자가 해당 좌표계 내에서 이동할 것으로 예상되는지 지정합니다.
  • 해당 좌표계에 사전 설정된 경계가 있는지 여부입니다. 여기에 표시된 예시는 사전 설정된 경계가 있는 좌표계를 사용하지 않습니다.

모든 참조 공간에서 X 좌표는 왼쪽과 오른쪽을, Y는 위와 아래를, Z는 앞뒤를 나타냅니다. 양수 값은 각각 오른쪽, 위쪽, 뒤쪽을 나타냅니다.

XRFrame.getViewerPose()에서 반환하는 좌표는 요청된 참조 공간 유형에 따라 다릅니다. 프레임 루프에 관해 자세히 알아보겠습니다. 지금은 증강 현실에 적합한 참조 유형을 선택해야 합니다. 여기서도 편의 속성을 사용합니다.

let refSpaceType
function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);

  if (session.isImmersive) {
    removeBackground();
  }

  let canvas = document.createElement('canvas');
  gl = canvas.getContext('webgl', { xrCompatible: true });

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

  refSpaceType = xrSession.isImmersive ? 'local' : 'viewer';
  xrSession.requestReferenceSpace(refSpaceType).then((refSpace) => {
    xrSession.requestAnimationFrame(onXRFrame);
  });
}

몰입형 AR 세션 샘플을 방문했다면 처음에는 장면이 정적이고 증강 현실이 전혀 아닌 것을 확인할 수 있습니다. 손가락을 드래그하고 스와이프하여 장면을 이동할 수 있습니다. 'AR 시작'을 클릭하면 배경이 사라지고 기기를 움직여 장면을 이동할 수 있습니다. 모드는 서로 다른 참조 공간 유형을 사용합니다. 위에서 강조 표시된 텍스트는 선택 방법을 보여줍니다. 다음 참조 유형을 사용합니다.

local - 원점은 세션 생성 시 시청자의 위치에 있습니다. 즉, 이 경험에 꼭 잘 정의된 하한선이 있는 것은 아니며 출발지의 정확한 위치는 플랫폼에 따라 다를 수 있습니다. 공간에 미리 설정된 경계는 없지만 회전 외에는 움직이지 않고 콘텐츠를 볼 수 있습니다. Google의 AR 예시에서 볼 수 있듯이 공간 내에서 약간의 움직임이 가능할 수 있습니다.

viewer - 페이지에 인라인으로 표시되는 콘텐츠에 가장 자주 사용되는 이 공백은 시청 기기를 따릅니다. getViewerPose에 전달되면 추적을 제공하지 않으므로 애플리케이션에서 XRReferenceSpace.getOffsetReferenceSpace()로 수정하지 않는 한 항상 원점에 있는 포즈를 보고합니다. 샘플에서는 이를 사용하여 카메라의 터치 기반 화면 이동을 사용 설정합니다.

프레임 루프 실행

개념적으로는 이전 도움말에서 설명한 VR 세션에서 한 것과 전혀 달라지지 않습니다. 참조 공간 유형을 XRFrame.getViewerPose()에 전달합니다. 반환된 XRViewerPose는 현재 참조 공간 유형에 해당합니다. viewer를 기본값으로 사용하면 AR 또는 VR에 대한 사용자 동의를 요청하기 전에 페이지에 콘텐츠 미리보기를 표시할 수 있습니다. 이는 중요한 점을 보여줍니다. 인라인 콘텐츠는 몰입형 콘텐츠와 동일한 프레임 루프를 사용하므로 유지해야 하는 코드의 양이 줄어듭니다.

function onXRFrame(hrTime, xrFrame) {
  let xrSession = xrFrame.session;
  xrSession.requestAnimationFrame(onXRFrame);
  let xrViewerPose = xrFrame.getViewerPose(refSpaceType);
  if (xrViewerPose) {
    // Render based on the pose.
  }
}

결론

이 도움말 시리즈에서는 웹에서 몰입형 콘텐츠를 구현하는 기본사항만 다룹니다. 더 많은 기능과 사용 사례는 Immersive Web Working Group의 WebXR Device API 샘플에서 제공합니다. 또한 표면을 감지하고 실제 카메라 뷰에 가상 항목을 배치하는 API를 설명하는 조회 테스트 도움말도 게시했습니다. 이 자료와 함께 web.dev 블로그에서 내년에도 더 많은 도움을 드릴 수 있기를 바랍니다.

사진: Unsplash데이비드 그랑모긴