Tworzenie aplikacji do rzeczywistości rozszerzonej (AR) przy użyciu interfejsu WebXR Device API

1. Zanim zaczniesz

W tym ćwiczeniu w Codelabs znajdziesz przykład tworzenia aplikacji internetowej AR. Do renderowania modeli 3D, które wyglądają, jakby istniały w rzeczywistym świecie, używa się JavaScriptu.

Korzystasz z interfejsu WebXR Device API, który łączy funkcje rzeczywistości rozszerzonej i wirtualnej (VR). Skupisz się na rozszerzeniach AR interfejsu WebXR Device API, aby utworzyć prostą aplikację AR działającą w interaktywnej sieci.

Co to jest AR?

AR to termin zwykle używany do opisywania mieszania grafiki generowanej komputerowo ze światem rzeczywistym. W przypadku AR na telefonie oznacza to przekonujące nakładanie grafiki komputerowej na przekaz z kamery na żywo. Aby ten efekt pozostał realistyczny podczas przemieszczania się telefonu, urządzenie z AR musi rozpoznawać otoczenie i określać swoją pozycję (położenie i orientację) w przestrzeni 3D. Może to obejmować wykrywanie powierzchni i szacowanie oświetlenia otoczenia.

Po wprowadzeniu przez Google ARCore i przez Apple ARKit rozszerzona rzeczywistość stała się powszechnie stosowana w aplikacjach, np. w filtrach do selfie czy grach opartych na AR.

Co utworzysz

W tym ćwiczeniu w Codelabs utworzysz aplikację internetową, która umieszcza model w rzeczywistym świecie za pomocą rzeczywistości rozszerzonej. Twoja aplikacja będzie:

  1. Używanie czujników urządzenia docelowego do określania i śledzenia jego pozycji i orientacji w świecie
  2. Renderowanie modelu 3D nałożonego na obraz z kamery na żywo
  3. Przeprowadzanie testów trafień w celu umieszczania obiektów na wykrytych powierzchniach w prawdziwym świecie

Czego się nauczysz

  • Jak korzystać z WebXR Device API
  • Jak skonfigurować podstawową scenę AR
  • Jak znaleźć powierzchnię za pomocą testów trafień AR
  • Jak wczytać i wyrenderować model 3D zsynchronizowany z przekazem z kamery w świecie rzeczywistym
  • Renderowanie cieni na podstawie modelu 3D

Te ćwiczenia z programowania dotyczą interfejsów AR API. Nieistotne koncepcje i bloki kodu zostały zamaskowane. Można je znaleźć w odpowiednim kodzie repozytorium.

Czego potrzebujesz

Kliknij Wypróbuj na urządzeniu AR, aby wypróbować pierwszy krok tego samouczka. Jeśli pojawi się strona z komunikatem „Twoja przeglądarka nie ma funkcji AR”, sprawdź, czy na urządzeniu z Androidem są zainstalowane Usługi Google Play dla AR.

2. Konfigurowanie środowiska programistycznego

Pobieranie kodu

  1. Kliknij ten link, aby pobrać na stację roboczą cały kod do tego laboratorium:

  1. Rozpakuj pobrany plik ZIP. Spowoduje to rozpakowanie folderu głównego (ar-with-webxr-master), który zawiera katalogi z kilkoma etapami tego samouczka oraz wszystkie potrzebne zasoby.

Foldery step-03step-04 zawierają oczekiwany stan końcowy trzeciego i czwartego kroku tego ćwiczenia oraz wynik final. Są one podane w celach informacyjnych.

Całą pracę związaną z kodowaniem wykonujesz w katalogu work.

Instalowanie serwera WWW

  1. Możesz używać własnego serwera WWW. Jeśli nie masz jeszcze skonfigurowanego serwera, w tej sekcji znajdziesz szczegółowe informacje o konfigurowaniu serwera Web Server for Chrome.
     Jeśli nie masz jeszcze tej aplikacji zainstalowanej na stacji roboczej, możesz ją zainstalować z Chrome Web Store.

  1. Po zainstalowaniu aplikacji Web Server for Chrome otwórz chrome://apps i kliknij ikonę serwera WWW:

Ikona serwera WWW

Następnie zobaczysz to okno dialogowe, w którym możesz skonfigurować lokalny serwer WWW:

Konfigurowanie serwera WWW Chrome

  1. Kliknij wybierz folder i wybierz folder ar-with-webxr-master. Dzięki temu możesz wyświetlać swoje prace w trakcie tworzenia za pomocą adresu URL wyróżnionego w oknie serwera internetowego (w sekcji Adresy URL serwera internetowego).
  2. W sekcji Opcje (wymaga ponownego uruchomienia) zaznacz pole wyboru Automatycznie wyświetlaj plik index.html.
  3. Przełącz Serwer WWW na Zatrzymaj, a potem z powrotem na Uruchomiono.Ponowne uruchamianie serwera WWW Chrome
  4. Sprawdź, czy wyświetla się co najmniej 1 adres URL serwera internetowego: http://127.0.0.1:8887 – domyślny adres URL hosta lokalnego.

Skonfiguruj przekierowanie portów

Skonfiguruj urządzenie AR tak, aby po otwarciu na nim adresu localhost:8887 uzyskiwał dostęp do tego samego portu na stacji roboczej.

  1. Na stacji roboczej programisty otwórz chrome://inspect i kliknij Przekazywanie portów...: chrome://inspect
  2. W oknie Ustawienia przekierowania portów przekieruj port 8887 na localhost:8887.
  3. Zaznacz pole wyboru Włącz przekierowanie portów:

Skonfiguruj przekierowanie portów

Sprawdzanie konfiguracji

Sprawdź połączenie:

  1. Podłącz urządzenie AR do stacji roboczej za pomocą kabla USB.
  2. Na urządzeniu AR w Chrome wpisz http://localhost:8887 na pasku adresu. Urządzenie AR powinno przekazać to żądanie do serwera WWW stacji roboczej dewelopera. Powinien pojawić się katalog plików.
  3. Na urządzeniu AR kliknij step-03, aby załadować plik step-03/index.html w przeglądarce.

Powinna się wyświetlić strona z przyciskiem Włącz rzeczywistość rozszerzoną.

Jeśli jednak zobaczysz stronę błędu Nieobsługiwana przeglądarka, Twoje urządzenie prawdopodobnie nie jest zgodne.

Obsługa ARCore

ARCore nie jest obsługiwany

Połączenie z serwerem WWW powinno teraz działać na urządzeniu AR.

  1. Kliknij Rozpocznij rzeczywistość rozszerzoną. Może pojawić się prośba o zainstalowanie ARCore.

Prośba o zainstalowanie ARCore

Gdy po raz pierwszy uruchomisz aplikację AR, zobaczysz prośbę o przyznanie uprawnień do korzystania z aparatu.

Chrome prosi o uprawnienia dostępu do aparatuOkno uprawnień

Gdy wszystko będzie gotowe, zobaczysz scenę z kostkami nałożoną na przekaz z kamery. Rozpoznawanie sceny poprawia się w miarę analizowania przez aparat większej części świata, więc poruszanie się może pomóc w ustabilizowaniu obrazu.

3. Konfigurowanie WebXR

W tym kroku dowiesz się, jak skonfigurować sesję WebXR i podstawową scenę AR. Strona HTML jest dostarczana ze stylami CSS i kodem JavaScript, który umożliwia korzystanie z podstawowych funkcji AR. Usprawnia to proces konfiguracji, dzięki czemu można skupić się na funkcjach AR.

strona HTML,

Do tworzenia środowiska AR na tradycyjnej stronie internetowej używasz istniejących technologii internetowych. W tym przypadku używasz obszaru renderowania na pełnym ekranie, więc plik HTML nie musi być zbyt złożony.

Funkcje AR wymagają gestu użytkownika, aby je uruchomić, dlatego istnieją komponenty Material Design do wyświetlania przycisku Uruchom AR i komunikatu o nieobsługiwanej przeglądarce.

Plik index.html, który znajduje się już w katalogu work, powinien wyglądać mniej więcej tak: Jest to podzbiór rzeczywistej zawartości. Nie kopiuj tego kodu do pliku.

<!-- Don't copy this code into your file! -->
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Building an augmented reality application with the WebXR Device API</title>
    <link rel="stylesheet" href="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css">
    <script src="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.js"></script>

    <!-- three.js -->
    <script src="https://unpkg.com/three@0.123.0/build/three.js"></script>
    <script src="https://unpkg.com/three@0.123.0/examples/js/loaders/GLTFLoader.js"></script>

    <script src="../shared/utils.js"></script>
    <script src="app.js"></script>
  </head>
  <body>
  <!-- Information about AR removed for brevity. -->

  <!-- Starting an immersive WebXR session requires user interaction. Start the WebXR experience with a simple button. -->
  <a onclick="activateXR()" class="mdc-button mdc-button--raised mdc-button--accent">
    Start augmented reality
  </a>

</body>
</html>

Otwórz kluczowy kod JavaScript

Punkt początkowy aplikacji znajduje się w regionie app.js. Ten plik zawiera kod początkowy do skonfigurowania środowiska AR.

Katalog roboczy zawiera już kod aplikacji (app.js).

Sprawdzanie obsługi WebXR i AR

Zanim użytkownik zacznie korzystać z AR, sprawdź, czy istnieje navigator.xr i czy są dostępne niezbędne funkcje XR. Obiekt navigator.xr jest punktem wejścia interfejsu WebXR Device API, więc powinien istnieć, jeśli urządzenie jest zgodne. Sprawdź też, czy obsługiwany jest tryb sesji "immersive-ar".

Jeśli wszystko jest w porządku, kliknięcie przycisku Wejdź w rzeczywistość rozszerzoną spowoduje próbę utworzenia sesji XR. W przeciwnym razie wywoływana jest funkcja onNoXRDevice() (w shared/utils.js), która wyświetla komunikat o braku obsługi AR.

Ten kod jest już obecny w app.js, więc nie trzeba wprowadzać żadnych zmian.

(async function() {
  if (navigator.xr && await navigator.xr.isSessionSupported("immersive-ar")) {
    document.getElementById("enter-ar").addEventListener("click", activateXR)
  } else {
    onNoXRDevice();
  }
})();

Poproś o XRSession

Gdy klikniesz Enter augmented Reality (Wejdź w rzeczywistość rozszerzoną), kod wywoła funkcję activateXR(). Spowoduje to uruchomienie AR.

  1. Znajdź funkcję activateXR() w pliku app.js. Pominięto część kodu:
activateXR = async () => {
  // Initialize a WebXR session using "immersive-ar".
  this.xrSession = /* TODO */;

  // Omitted for brevity
}

Punktem wejścia do WebXR jest XRSystem.requestSession(). Użyj trybu immersive-ar, aby wyświetlać wyrenderowane treści w środowisku rzeczywistym.

  1. Zainicjuj this.xrSession w trybie "immersive-ar":
activateXR = async () => {
  // Initialize a WebXR session using "immersive-ar".
  this.xrSession = await navigator.xr.requestSession("immersive-ar");

  // ...
}

Zainicjuj XRReferenceSpace

XRReferenceSpace opisuje system współrzędnych używany w przypadku obiektów w świecie wirtualnym. Tryb 'local' najlepiej sprawdza się w przypadku środowisk AR, w których przestrzeń odniesienia ma punkt początkowy w pobliżu użytkownika i zapewnia stabilne śledzenie.

Zainicjuj this.localReferenceSpaceonSessionStarted() za pomocą tego kodu:

this.localReferenceSpace = await this.xrSession.requestReferenceSpace("local");

Definiowanie pętli animacji

  1. Użyj XRSession requestAnimationFrame, aby rozpocząć pętlę renderowania podobną do window.requestAnimationFrame.

W każdej klatce wywoływana jest funkcja onXRFrame z sygnaturą czasową i obiektem XRFrame.

  1. Dokończ wdrażanie usługi onXRFrame. Gdy klatka zostanie narysowana, dodaj do kolejki następne żądanie:
// Queue up the next draw request.
this.xrSession.requestAnimationFrame(this.onXRFrame);
  1. Dodaj kod, aby skonfigurować środowisko graficzne. Dodaj na dole onXRFrame:
// Bind the graphics framebuffer to the baseLayer's framebuffer.
const framebuffer = this.xrSession.renderState.baseLayer.framebuffer;
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebuffer);
this.renderer.setFramebuffer(framebuffer);
  1. Aby określić pozę widza, użyj XRFrame.getViewerPose(). Ten XRViewerPose opisuje położenie i orientację urządzenia w przestrzeni. Zawiera też tablicę XRView, która opisuje każdy punkt widzenia, z którego scena powinna być renderowana, aby prawidłowo wyświetlać się na bieżącym urządzeniu. W przypadku stereoskopowej rzeczywistości wirtualnej są 2 widoki (po jednym na każde oko), a urządzenia AR mają tylko 1 widok.
    Informacje w pose.views są najczęściej używane do konfigurowania macierzy widoku i macierzy projekcji wirtualnej kamery. Wpływa to na sposób rozmieszczenia sceny w 3D. Po skonfigurowaniu kamery można renderować scenę.
  2. Dodaj na dole onXRFrame:
// Retrieve the pose of the device.
// XRFrame.getViewerPose can return null while the session attempts to establish tracking.
const pose = frame.getViewerPose(this.localReferenceSpace);
if (pose) {
  // In mobile AR, we only have one view.
  const view = pose.views[0];

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

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

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

Przetestuj

Uruchom aplikację. Na urządzeniu deweloperskim otwórz work/index.html. Powinien pojawić się przekaz z kamery z kostkami unoszącymi się w przestrzeni, których perspektywa zmienia się wraz z ruchem urządzenia. Śledzenie staje się dokładniejsze, gdy się poruszasz, więc sprawdź, co najlepiej sprawdza się w Twoim przypadku i na Twoim urządzeniu.

Jeśli masz problemy z uruchomieniem aplikacji, zapoznaj się z sekcjami WprowadzenieKonfigurowanie środowiska programistycznego.

4. Dodawanie siatki celowniczej

Po skonfigurowaniu podstawowej sceny AR możesz zacząć wchodzić w interakcje z rzeczywistym światem za pomocą testu pozycji wskaźnika. W tej sekcji zaprogramujesz test pozycji wskaźnika i użyjesz go do znalezienia powierzchni w rzeczywistym świecie.

Informacje o teście pozycji wskaźnika

Test pozycji wskaźnika to zwykle sposób na wyznaczenie prostej linii z punktu w przestrzeni w określonym kierunku i sprawdzenie, czy przecina ona jakieś interesujące obiekty. W tym przykładzie kierujesz urządzenie na miejsce w rzeczywistym świecie. Wyobraź sobie promień światła wychodzący z aparatu urządzenia i kierujący się prosto w fizyczny świat przed nim.

Interfejs WebXR Device API informuje, czy promień przecinał jakieś obiekty w rzeczywistym świecie, co jest określane przez podstawowe funkcje AR i rozumienie świata.

Wyjaśnienie dotyczące testu pozycji wskaźnika

Poproś o XRSession z dodatkowymi funkcjami

Aby przeprowadzić testy trafień, podczas wysyłania żądania XRSession wymagane są dodatkowe funkcje.

  1. app.js znajdź navigator.xr.requestSession.
  2. Dodaj funkcje "hit-test""dom-overlay" jako requiredFeatures w ten sposób:
this.xrSession = await navigator.xr.requestSession("immersive-ar", {
  requiredFeatures: ["hit-test", "dom-overlay"]
});
  1. Skonfiguruj nakładkę DOM. Nałóż element document.body na obraz z kamery AR w ten sposób:
this.xrSession = await navigator.xr.requestSession("immersive-ar", {
  requiredFeatures: ["hit-test", "dom-overlay"],
  domOverlay: { root: document.body }
});

Dodawanie prompta dotyczącego ruchu

ARCore działa najlepiej, gdy ma wystarczającą wiedzę o otoczeniu. Jest to możliwe dzięki procesowi jednoczesnej lokalizacji i mapowania (SLAM), w którym do obliczania zmiany lokalizacji i charakterystyki środowiska używane są wizualnie odrębne punkty charakterystyczne.

Użyj "dom-overlay" z poprzedniego kroku, aby wyświetlić prompt ruchu na strumieniu z kamery.

Dodaj <div> do index.html o identyfikatorze stabilization. Ten <div> wyświetla animację przedstawiającą stan stabilizacji i zachęca użytkowników do poruszania się z urządzeniem, aby usprawnić proces SLAM. Wyświetla się, gdy użytkownik korzysta z AR, i jest ukryty, gdy siatka celownicza znajdzie powierzchnię. Jest to kontrolowane przez klasy <body>.

  <div id="stabilization"></div>

</body>
</html>

Dodawanie siatki celowniczej

Użyj siatki celowniczej, aby wskazać miejsce, na które jest skierowany widok urządzenia.

  1. app.js zastąp wywołanie DemoUtils.createCubeScene()setupThreeJs() pustym ciągiem Three.Scene().
setupThreeJs() {
  // ...

  // this.scene = DemoUtils.createCubeScene();
  this.scene = DemoUtils.createLitScene();
}
  1. Wypełnij nową scenę obiektem reprezentującym punkt kolizji. Podana Reticle klasa obsługuje wczytywanie modelu siatki celowniczej w shared/utils.js.
  2. Dodaj Reticle do sceny w setupThreeJs():
setupThreeJs() {
  // ...

  // this.scene = DemoUtils.createCubeScene();
  this.scene = DemoUtils.createLitScene();
  this.reticle = new Reticle();
  this.scene.add(this.reticle);
}

Aby przeprowadzić test pozycji wskaźnika, użyj nowego elementu XRReferenceSpace. Ta przestrzeń odniesienia wskazuje nowy układ współrzędnych z perspektywy widza, aby utworzyć promień, który jest zgodny z kierunkiem oglądania. Ten układ współrzędnych jest używany w XRSession.requestHitTestSource(), które może obliczać testy trafień.

  1. Dodaj do pliku onSessionStarted() w app.js te informacje:
async onSessionStarted() {
  // ...

  // Setup an XRReferenceSpace using the "local" coordinate system.
  this.localReferenceSpace = await this.xrSession.requestReferenceSpace("local");

  // Add these lines:
  // Create another XRReferenceSpace that has the viewer as the origin.
  this.viewerSpace = await this.xrSession.requestReferenceSpace("viewer");
  // Perform hit testing using the viewer as origin.
  this.hitTestSource = await this.xrSession.requestHitTestSource({ space: this.viewerSpace });

  // ...
}
  1. Użyj tego hitTestSource, aby przeprowadzać test pozycji wskaźnika w każdej klatce:
    • Jeśli test pozycji wskaźnika nie przyniesie żadnych wyników, oznacza to, że ARCore nie miał wystarczająco dużo czasu, aby poznać otoczenie. W takim przypadku poproś użytkownika o przesunięcie urządzenia za pomocą stabilizacji <div>.
    • Jeśli pojawią się wyniki, przesuń celownik w to miejsce.
  2. Zmodyfikuj onXRFrame, aby przesunąć siatkę celowniczą:
onXRFrame = (time, frame) => {
  // ... some code omitted ...
  this.camera.updateMatrixWorld(true);

  // Add the following:
  const hitTestResults = frame.getHitTestResults(this.hitTestSource);

  if (!this.stabilized && hitTestResults.length > 0) {
    this.stabilized = true;
    document.body.classList.add("stabilized");
  }
  if (hitTestResults.length > 0) {
    const hitPose = hitTestResults[0].getPose(this.localReferenceSpace);

    // update the reticle position
    this.reticle.visible = true;
    this.reticle.position.set(hitPose.transform.position.x, hitPose.transform.position.y, hitPose.transform.position.z)
    this.reticle.updateMatrixWorld(true);
  }
  // More code omitted.
}

Dodawanie działania po dotknięciu ekranu

XRSession może emitować zdarzenia na podstawie interakcji użytkownika za pomocą zdarzenia select, które reprezentuje główną akcję. W WebXR na urządzeniach mobilnych główną czynnością jest kliknięcie ekranu.

  1. Dodaj detektor zdarzeń select u dołu pliku onSessionStarted:
this.xrSession.addEventListener("select", this.onSelect);

W tym przykładzie dotknięcie ekranu powoduje umieszczenie słonecznika w celowniku.

  1. Utwórz implementację interfejsu onSelect w klasie App:
onSelect = () => {
  if (window.sunflower) {
    const clone = window.sunflower.clone();
    clone.position.copy(this.reticle.position);
    this.scene.add(clone);
  }
}

Testowanie aplikacji

Utworzono siatkę celowniczą, którą można wycelować za pomocą urządzenia, korzystając z testów trafień. Po kliknięciu ekranu powinna pojawić się możliwość umieszczenia słonecznika w miejscu wskazanym przez siatkę celowniczą.

  1. Po uruchomieniu aplikacji powinien być widoczny celownik śledzący powierzchnię podłogi. Jeśli nie, powoli rozejrzyj się dookoła, poruszając telefonem.
  2. Gdy zobaczysz siatkę celowniczą, kliknij ją. Na górze powinien znajdować się słonecznik. Może być konieczne poruszanie się, aby platforma AR mogła lepiej wykrywać powierzchnie w świecie rzeczywistym. Słabe oświetlenie i powierzchnie bez cech charakterystycznych obniżają jakość rozpoznawania sceny i zwiększają prawdopodobieństwo, że nie zostanie znaleziony żaden wynik. Jeśli napotkasz problemy, zapoznaj się z kodem step-04/app.js, aby zobaczyć działający przykład tego kroku.

5. Dodawanie cieni

Tworzenie realistycznej sceny obejmuje elementy takie jak odpowiednie oświetlenie i cienie na obiektach cyfrowych, które zwiększają realizm i immersję w scenie.

Oświetlenie i cienie są obsługiwane przez three.js. Możesz określić, które światła mają rzucać cienie, które materiały mają je odbierać i renderować oraz które siatki mogą rzucać cienie. Scena tej aplikacji zawiera światło, które rzuca cień, oraz płaską powierzchnię do renderowania tylko cieni.

  1. Włącz cienie na three.js WebGLRenderer. Po utworzeniu renderera ustaw te wartości w jego polu shadowMap:
setupThreeJs() {
  ...
  this.renderer = new THREE.WebGLRenderer(...);
  ...
  this.renderer.shadowMap.enabled = true;
  this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  ...
}

Przykładowa scena utworzona w DemoUtils.createLitScene() zawiera obiekt o nazwie shadowMesh, czyli płaską, poziomą powierzchnię, która renderuje tylko cienie. Początkowo ta powierzchnia ma pozycję Y wynoszącą 10 000 jednostek. Po umieszczeniu słonecznika przesuń ikonę shadowMesh na wysokość powierzchni w rzeczywistym świecie, tak aby cień kwiatu był renderowany na ziemi.

  1. W onSelect po dodaniu clone do sceny dodaj kod, aby zmienić położenie płaszczyzny cienia:
onSelect = () => {
  if (window.sunflower) {
    const clone = window.sunflower.clone();
    clone.position.copy(this.reticle.position);
    this.scene.add(clone);

    const shadowMesh = this.scene.children.find(c => c.name === "shadowMesh");
    shadowMesh.position.y = clone.position.y;
  }
}

Przetestuj

Po umieszczeniu słonecznika powinien być widoczny jego cień. Jeśli napotkasz problemy, zapoznaj się z kodem final/app.js, aby zobaczyć działający przykład tego kroku.

6. Dodatkowe materiały

Gratulacje! To już koniec tego ćwiczenia z zakresu AR z użyciem WebXR.

Więcej informacji