WebXR을 사용하여 몰입형 AR 세션 만들기

이 페이지에서는 WebXR을 사용하여 간단한 몰입형 AR 애플리케이션을 만드는 과정을 안내합니다.

시작하려면 WebXR 호환 개발 환경이 필요합니다.

HTML 페이지 만들기

세션을 시작하려면 WebXR이 사용자 상호작용이 필요합니다. activateXR()를 호출하는 버튼을 만듭니다. 페이지를 로드하면 사용자는 이 버튼을 사용하여 AR 환경을 시작할 수 있습니다.

index.html라는 새 파일을 만들고 다음 HTML 코드를 추가합니다.

<!doctype html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <title>Hello WebXR!</title>

  <!-- three.js -->
  <script src="https://unpkg.com/three@0.126.0/build/three.js"></script>
</head>
<body>

<!-- Starting an immersive WebXR session requires user interaction.
    We start this one with a simple button. -->
<button onclick="activateXR()">Start Hello WebXR</button>
<script>
async function activateXR() {
  // Add a canvas element and initialize a WebGL context that is compatible with WebXR.
  const canvas = document.createElement("canvas");
  document.body.appendChild(canvas);
  const gl = canvas.getContext("webgl", {xrCompatible: true});

  // To be continued in upcoming steps.
}
</script>
</body>
</html>

third.js 초기화

시작 버튼을 누를 때 아무 변화가 없습니다. 3D 환경을 설정하려면 렌더링 라이브러리를 사용하여 장면을 표시할 수 있습니다.

이 예에서는 WebGL 렌더기를 제공하는 JavaScript 3D 렌더링 라이브러리인 three.js를 사용합니다. Three.js는 렌더링, 카메라, 장면 그래프를 처리하여 웹에 3D 콘텐츠를 더 쉽게 표시할 수 있도록 합니다.

장면 만들기

3D 환경은 일반적으로 장면으로 모델링됩니다. AR 요소가 포함된 THREE.Scene를 만듭니다. 다음 코드를 사용하면 AR로 조명이 없는 색상 상자를 볼 수 있습니다.

다음 코드를 activateXR() 함수 하단에 추가합니다.

const scene = new THREE.Scene();

// The cube will have a different color on each side.
const materials = [
  new THREE.MeshBasicMaterial({color: 0xff0000}),
  new THREE.MeshBasicMaterial({color: 0x0000ff}),
  new THREE.MeshBasicMaterial({color: 0x00ff00}),
  new THREE.MeshBasicMaterial({color: 0xff00ff}),
  new THREE.MeshBasicMaterial({color: 0x00ffff}),
  new THREE.MeshBasicMaterial({color: 0xffff00})
];

// Create the cube and add it to the demo scene.
const cube = new THREE.Mesh(new THREE.BoxBufferGeometry(0.2, 0.2, 0.2), materials);
cube.position.set(1, 1, 1);
scene.add(cube);

third.js를 사용하여 렌더링 설정

AR로 이 장면을 보려면 렌더러카메라가 필요합니다. 렌더기는 WebGL을 사용하여 화면에 장면을 그립니다. 카메라는 장면이 표시되는 표시 영역을 설명합니다.

다음 코드를 activateXR() 함수 하단에 추가합니다.

// Set up the WebGLRenderer, which handles rendering to the session's base layer.
const renderer = new THREE.WebGLRenderer({
  alpha: true,
  preserveDrawingBuffer: true,
  canvas: canvas,
  context: gl
});
renderer.autoClear = false;

// The API directly updates the camera matrices.
// Disable matrix auto updates so three.js doesn't attempt
// to handle the matrices independently.
const camera = new THREE.PerspectiveCamera();
camera.matrixAutoUpdate = false;

XRSession 만들기

WebXR의 진입점은 XRSystem.requestSession()을 통해 이루어집니다. immersive-ar 모드를 사용하면 렌더링된 콘텐츠를 실제 환경에서 볼 수 있습니다.

XRReferenceSpace은 가상 세계 내의 객체에 사용되는 좌표계를 설명합니다. 'local' 모드는 뷰어 근처에 원점이 있고 안정적인 추적이 가능한 참조 공간이 있는 AR 환경에 가장 적합합니다.

XRSessionXRReferenceSpace를 만들려면 다음 코드를 activateXR() 함수 하단에 추가합니다.

// Initialize a WebXR session using "immersive-ar".
const session = await navigator.xr.requestSession("immersive-ar");
session.updateRenderState({
  baseLayer: new XRWebGLLayer(session, gl)
});

// A 'local' reference space has a native origin that is located
// near the viewer's position at the time the session was created.
const referenceSpace = await session.requestReferenceSpace('local');

장면 렌더링

이제 장면을 렌더링할 수 있습니다. XRSession.requestAnimationFrame()는 브라우저가 프레임을 그릴 준비가 되었을 때 실행되는 콜백을 예약합니다.

애니메이션 프레임 콜백 중에 XRFrame.getViewerPose()를 호출하여 로컬 좌표 공간을 기준으로 뷰어의 포즈를 가져옵니다. 장면 내 카메라를 업데이트하는 데 사용되며, 렌더기가 업데이트된 카메라를 사용하여 장면을 그리기 전에 사용자가 가상 세계를 보는 방식을 변경합니다.

다음 코드를 activateXR() 함수 하단에 추가합니다.

// Create a render loop that allows us to draw on the AR view.
const onXRFrame = (time, frame) => {
  // Queue up the next draw request.
  session.requestAnimationFrame(onXRFrame);

  // Bind the graphics framebuffer to the baseLayer's framebuffer
  gl.bindFramebuffer(gl.FRAMEBUFFER, session.renderState.baseLayer.framebuffer)

  // Retrieve the pose of the device.
  // XRFrame.getViewerPose can return null while the session attempts to establish tracking.
  const pose = frame.getViewerPose(referenceSpace);
  if (pose) {
    // In mobile AR, we only have one view.
    const view = pose.views[0];

    const viewport = session.renderState.baseLayer.getViewport(view);
    renderer.setSize(viewport.width, viewport.height)

    // Use the view's transform matrix and projection matrix to configure the THREE.camera.
    camera.matrix.fromArray(view.transform.matrix)
    camera.projectionMatrix.fromArray(view.projectionMatrix);
    camera.updateMatrixWorld(true);

    // Render the scene with THREE.WebGLRenderer.
    renderer.render(scene, camera)
  }
}
session.requestAnimationFrame(onXRFrame);

Hello WebXR 실행

기기에서 WebXR 파일로 이동합니다. 사방에서 색깔 큐브가 보여야 합니다.

히트 테스트 추가

AR 세계와 상호작용하는 일반적인 방법은 광선과 실제 도형 사이의 교차점을 찾는 히트 테스트를 사용하는 것입니다. Hello WebXR에서는 조회 테스트를 사용하여 가상 세계에 해바라기를 배치합니다.

데모 큐브 삭제

점등되지 않은 큐브를 제거하고 다음과 같이 조명이 포함된 장면으로 교체합니다.

const scene = new THREE.Scene();

const directionalLight = new THREE.DirectionalLight(0xffffff, 0.3);
directionalLight.position.set(10, 15, 10);
scene.add(directionalLight);

hit-test 기능 사용

조회 테스트 기능을 초기화하려면 hit-test 기능을 사용하여 세션을 요청합니다. 이전 requestSession() 프래그먼트를 찾아 hit-test를 추가합니다.

const session = await navigator.xr.requestSession("immersive-ar", {requiredFeatures: ['hit-test']});

모델 로더 추가

현재 장면에는 색상이 있는 큐브만 포함되어 있습니다. 더 흥미로운 환경을 만들려면 GLTF 모델을 로드할 수 있는 모델 로더를 추가하세요.

문서의 <head> 태그에 three.js' GLTFLoader를 추가합니다.

<!-- three.js -->
<script src="https://unpkg.com/three@0.126.0/build/three.js"></script>

<script src="https://unpkg.com/three@0.126.0/examples/js/loaders/GLTFLoader.js"></script>

GLTF 모델 로드

이전 단계의 모델 로더를 사용하여 웹에서 타겟팅 레티클과 해바라기를 로드합니다.

onXRFrame 위에 다음 코드를 추가합니다.

const loader = new THREE.GLTFLoader();
let reticle;
loader.load("https://immersive-web.github.io/webxr-samples/media/gltf/reticle/reticle.gltf", function(gltf) {
  reticle = gltf.scene;
  reticle.visible = false;
  scene.add(reticle);
})

let flower;
loader.load("https://immersive-web.github.io/webxr-samples/media/gltf/sunflower/sunflower.gltf", function(gltf) {
  flower = gltf.scene;
});

// Create a render loop that allows us to draw on the AR view.
const onXRFrame = (time, frame) => {

히트 테스트 소스 만들기

실제 객체와의 교집합을 계산하려면 XRSession.requestHitTestSource()를 사용하여 XRHitTestSource를 만듭니다. 히트 테스트에 사용되는 레이의 원점은 viewer 참조 공간이며, 이는 히트 테스트가 표시 영역의 중앙에서 실행됨을 의미합니다.

히트 테스트 소스를 만들려면 local 참조 공간을 만든 후 다음 코드를 추가합니다.

// A 'local' reference space has a native origin that is located
// near the viewer's position at the time the session was created.
const referenceSpace = await session.requestReferenceSpace('local');

// Create another XRReferenceSpace that has the viewer as the origin.
const viewerSpace = await session.requestReferenceSpace('viewer');
// Perform hit testing using the viewer as origin.
const hitTestSource = await session.requestHitTestSource({ space: viewerSpace });

타겟팅 레티클 그리기

해바라기가 배치될 위치를 명확하게 하려면 장면에 타겟팅 레티클을 추가합니다. 이 레티클은 해바라기가 고정될 위치를 나타내는 실제 표면에 붙어 있는 것처럼 보입니다.

XRFrame.getHitTestResultsXRHitTestResult의 배열을 반환하고 실제 도형의 교집합을 노출합니다. 이러한 교차점을 사용하여 모든 프레임에 타겟팅 레티클을 배치합니다.

camera.projectionMatrix.fromArray(view.projectionMatrix);
camera.updateMatrixWorld(true);

const hitTestResults = frame.getHitTestResults(hitTestSource);
if (hitTestResults.length > 0 && reticle) {
  const hitPose = hitTestResults[0].getPose(referenceSpace);
  reticle.visible = true;
  reticle.position.set(hitPose.transform.position.x, hitPose.transform.position.y, hitPose.transform.position.z)
  reticle.updateMatrixWorld(true);
}

탭하여 상호작용 추가

XRSession는 사용자가 기본 작업을 완료하면 select 이벤트를 수신합니다. AR 세션에서는 화면을 탭하는 것에 해당합니다.

초기화 중에 다음 코드를 추가하여 사용자가 화면을 탭할 때 새 해바라기가 표시되도록 합니다.

let flower;
loader.load("https://immersive-web.github.io/webxr-samples/media/gltf/sunflower/sunflower.gltf", function(gltf) {
  flower = gltf.scene;
});

session.addEventListener("select", (event) => {
  if (flower) {
    const clone = flower.clone();
    clone.position.copy(reticle.position);
    scene.add(clone);
  }
});

히트 테스트 테스트

휴대기기를 사용하여 페이지로 이동합니다. WebXR이 환경을 이해한 후 레티클이 실제 표면에 표시되어야 합니다. 화면을 탭하여 해바라기를 배치하세요. 이 해바라기는 모든 측면에서 볼 수 있습니다.

다음 단계