יצירת פעילות 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

לא הרבה יקרה כשלוחצים על לחצן ההתחלה. כדי להגדיר סביבה תלת-ממדית, אפשר להשתמש בספריית רינדור כדי להציג סצנה.

בדוגמה הזו נשתמש ב-three.js, ספריית עיבוד תלת-ממד ב-JavaScript שמספקת מעבד WebGL. Three.js מטפל ברינדור, במצלמות ובתרשים סצנות, וכך מקל על הצגת תוכן תלת-ממדי באינטרנט.

יצירת סצנה

סביבת 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() מתזמן קריאה חוזרת (callback) שתתבצע כשהדפדפן יהיה מוכן לצייר פריים.

במהלך הקריאה החוזרת (callback) של מסגרת האנימציה, צריך לבצע קריאה ל-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 יבין את הסביבה, כוורת הראייה אמורה להופיע על משטחים בעולם האמיתי. מקישים על המסך כדי להציב חמנית שאפשר לראות מכל הכיוונים.

השלבים הבאים