Создайте иммерсивный сеанс AR с помощью WebXR

На этой странице вы узнаете, как создать простое иммерсивное AR-приложение с использованием WebXR.

Для начала вам понадобится среда разработки, совместимая с 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>

Инициализировать Three.js

При нажатии кнопки «Пуск» ничего особенного не произойдет. Чтобы настроить 3D-среду, вы можете использовать библиотеку рендеринга для отображения сцены.

В этом примере вы будете использовать three.js — библиотеку 3D-рендеринга JavaScript, которая предоставляет средство рендеринга WebGL. Three.js обрабатывает рендеринг, камеры и графики сцен, упрощая отображение 3D-контента в Интернете.

Создать сцену

3D-среда обычно моделируется как сцена. Создайте THREE.Scene , содержащий элементы AR. Следующий код позволяет вам посмотреть на неосвещенный цветной прямоугольник в 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);

Настройте рендеринг с помощью Three.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-опыта, поскольку опорное пространство находится рядом со зрителем и стабильно отслеживается.

Чтобы создать XRSession и XRReferenceSpace , добавьте этот код в конец функции 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);

Запустить Привет 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> вашего документа добавьте GLTFLoader Three.js .

<!-- 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) => {

Создайте источник проверки попадания

Чтобы вычислить пересечения с объектами реального мира, создайте XRHitTestSource с помощью XRSession.requestHitTestSource() . Луч, используемый для проверки попадания, имеет в качестве начала опорное пространство 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.getHitTestResults возвращает массив XRHitTestResult и предоставляет пересечения с реальной геометрией. Используйте эти пересечения, чтобы расположить прицельную сетку в каждом кадре.

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 определит окружающую среду, прицельная марка должна появиться на реальных поверхностях. Коснитесь экрана, чтобы разместить подсолнух, который можно рассмотреть со всех сторон.

Следующие шаги