3D-Karten mit WebGL Overlay View erstellen

1. Hinweis

In diesem Codelab lernen Sie, wie Sie mit den WebGL-gestützten Funktionen der Maps JavaScript API die Vektorkarte in drei Dimensionen steuern und rendern können.

Finale 3D-Markierung

Vorbereitung

Dieses Codelab setzt voraus, dass du Grundkenntnisse in JavaScript und der Maps JavaScript API hast. Im Grundlagen-Codelab zur Maps JS API finden Sie im Artikel Karte zu Ihrer Website hinzufügen (JavaScript).

Lerninhalte

  • Generieren einer Karten-ID mit der Vektorkarte für aktiviertes JavaScript
  • Karte mit programmatischer Neigung und Rotation steuern
  • Rendering von 3D-Objekten auf der Karte mit WebGLOverlayView und Three.js
  • Kamerabewegungen mit moveCamera animieren.

Voraussetzungen

  • Google Cloud Platform-Konto mit aktivierter Abrechnung
  • Ein Google Maps Platform API-Schlüssel mit aktivierter Maps JavaScript API
  • Grundkenntnisse in JavaScript, HTML und CSS
  • Texteditor oder IDE deiner Wahl
  • Node.js

2. Einrichten

Für den nachfolgenden Aktivierungsschritt musst du die Maps JavaScript API aktivieren.

Google Maps Platform einrichten

Wenn Sie noch kein Google Cloud Platform-Konto und kein Projekt mit aktivierter Abrechnung haben, lesen Sie den Leitfaden Erste Schritte mit der Google Maps Platform, um ein Rechnungskonto und ein Projekt zu erstellen.

  1. Klicken Sie in der Cloud Console auf das Drop-down-Menü für Projekte und wählen Sie das Projekt aus, das Sie für dieses Codelab verwenden möchten.

  1. Aktivieren Sie im Google Cloud Marketplace die für dieses Codelab erforderlichen Google Maps Platform APIs und SDKs. Folgen Sie dazu der Anleitung in diesem Video oder dieser Dokumentation.
  2. Generieren Sie in der Cloud Console auf der Seite Anmeldedaten einen API-Schlüssel. Folgen Sie der Anleitung in diesem Video oder dieser Dokumentation. Für alle Anfragen an die Google Maps Platform ist ein API-Schlüssel erforderlich.

Node.js-Einrichtung

Wenn Sie dies noch nicht getan haben, rufen Sie https://searchads.org/ auf, um die Node.js-Laufzeit auf Ihren Computer herunterzuladen und zu installieren.

Node.js enthält den npm-Paketmanager, den Sie zum Installieren von Abhängigkeiten für dieses Codelab benötigen.

Projektstartvorlage herunterladen

Bevor Sie mit diesem Codelab beginnen, müssen Sie Folgendes tun, um die Startprojektvorlage sowie den vollständigen Lösungscode herunterzuladen:

  1. Laden Sie das GitHub-Repository für dieses Codelab unter https://github.com/googlecodelabs/maps-platform-101-webgl/ herunter. Das Starter-Projekt befindet sich im Verzeichnis /starter und enthält die grundlegende Dateistruktur, die Sie für das Codelab benötigen. Alles, was du brauchst, findest du im Verzeichnis /starter/src.
  2. Führen Sie nach dem Herunterladen des Starter-Projekts npm install im Verzeichnis /starter aus. Dadurch werden alle erforderlichen Abhängigkeiten installiert, die in package.json aufgeführt sind.
  3. Führen Sie nach der Installation der Abhängigkeiten npm start im Verzeichnis aus.

Das Starter-Projekt wurde so eingerichtet, dass Sie webpack-dev-server verwenden, der den von Ihnen lokal geschriebenen Code kompiliert und ausführt. Wenn Sie Codeänderungen vornehmen, wird Ihre App von webpack-dev-server automatisch im Browser neu geladen.

Wenn Sie möchten, dass der vollständige Lösungscode ausgeführt wird, führen Sie die oben genannten Einrichtungsschritte im Verzeichnis /solution aus.

Eigenen API-Schlüssel hinzufügen

Die Starter-App enthält den gesamten Code, der zum Laden der Karte mit dem JS API-Ladegerät erforderlich ist. Sie müssen also nur Ihren API-Schlüssel und die Karten-ID angeben. Der JS API Loader ist eine einfache Bibliothek, die die herkömmliche Methode zum Laden der Maps JS API in die HTML-Vorlage mit einem script-Tag abstrahiert. Dadurch können Sie alles in JavaScript-Code verarbeiten.

So fügen Sie den API-Schlüssel im Startprojekt hinzu:

  1. Öffnen Sie app.js.
  2. Lege im apiOptions-Objekt deinen API-Schlüssel als Wert von apiOptions.apiKey fest.

3. Karten-ID generieren und verwenden

Um die WebGL-basierten Funktionen der Maps JavaScript API zu verwenden, benötigst du eine Karten-ID mit aktivierter Vektorkarte.

Karten-ID generieren

Karten-ID-Generierung

  1. Gehen Sie in der Google Cloud Console zu „Google Maps Platform' > Map Management'.
  2. Klicken Sie auf NEUE KARTEN-ID ERSTELLEN'.
  3. Geben Sie in das Feld „Kartenname&name“ einen Namen für die Karten-ID ein.
  4. Wählen Sie im Drop-down-Menü „Kartentyp'“ die Option „JavaScript'“ aus. „JavaScript-Optionen“ wird angezeigt.
  5. Aktivieren Sie unter „JavaScript-Optionen“ das Optionsfeld „Vector'“ und „Neigung“ sowie das Kästchen „Rotation'“.
  6. Optional: Gib in das Feld „Beschreibung“ eine Beschreibung für deinen API-Schlüssel ein.
  7. Klicken Sie auf die Schaltfläche „Weiter“. Die Seite „Karten-ID-Details“ wird angezeigt.

    Seite mit Kartendetails
  8. Kopieren Sie die Karten-ID. Diese Datei benötigen Sie im nächsten Schritt, um die Karte zu laden.

Karten-ID verwenden

Zum Laden der Vektorkarte müssen Sie in den Optionen eine Karten-ID als Eigenschaft angeben, wenn Sie die Karte instanziieren. Sie können auch beim Laden der Maps JavaScript API dieselbe Karten-ID angeben.

So laden Sie die Karte mit Ihrer Karten-ID:

  1. Lege deine Karten-ID als Wert für mapOptions.mapId fest.

    Wenn Sie die Karten-ID beim Instanziieren der Karte angeben, wird der Google Maps Platform mitgeteilt, welche Ihrer Karten für eine bestimmte Instanz geladen werden sollen. Sie können dieselbe Karten-ID dann für mehrere Apps und Aufrufe einer App verwenden.
    const mapOptions = {
      "tilt": 0,
      "heading": 0,
      "zoom": 18,
      "center": { lat: 35.6594945, lng: 139.6999859 },
      "mapId": "YOUR_MAP_ID"
    };
    

Überprüfen Sie die in Ihrem Browser ausgeführte App. Die Vektorkarte mit aktivierter Neigungs- und Rotation sollte geladen werden. Um zu prüfen, ob Neigung und Rotation aktiviert sind, halten Sie die Umschalttaste gedrückt und ziehen Sie entweder mit der Maus oder verwenden Sie die Pfeiltasten auf der Tastatur.

Wenn die Karte nicht geladen wird, überprüfe, ob du einen gültigen API-Schlüssel in apiOptions angegeben hast. Wenn die Karte nicht geneigt und gedreht wird, sehen Sie nach, ob in apiOptions und mapOptions eine Karten-ID mit Neigung und Drehung angegeben ist.

Geneigte Karte

Die Datei app.js sollte jetzt so aussehen:

    import { Loader } from '@googlemaps/js-api-loader';

    const apiOptions = {
      "apiKey": 'YOUR_API_KEY',
    };

    const mapOptions = {
      "tilt": 0,
      "heading": 0,
      "zoom": 18,
      "center": { lat: 35.6594945, lng: 139.6999859 },
      "mapId": "YOUR_MAP_ID"
    }

    async function initMap() {
      const mapDiv = document.getElementById("map");
      const apiLoader = new Loader(apiOptions);
      await apiLoader.load();
      return new google.maps.Map(mapDiv, mapOptions);
    }

    function initWebGLOverlayView (map) {
      let scene, renderer, camera, loader;
      // WebGLOverlayView code goes here
    }

    (async () => {
      const map = await initMap();
    })();

4. WebGLOverlayView implementieren

WebGLOverlayView ermöglicht dir direkten Zugriff auf denselben WebGL-Renderingkontext, mit dem die Vektorbasiskarte gerendert wird. Sie können also mithilfe von WebGL sowie beliebten WebGL-basierten Grafikbibliotheken 2D- und 3D-Objekte direkt auf der Karte rendern.

WebGLOverlayView stellt fünf Webhooks für den Lebenszyklus des WebGL-Renderingkontexts der Karte bereit, die du verwenden kannst. Hier findest du eine kurze Beschreibung der einzelnen Webhooks und deren Bedeutung:

  • onAdd(): Wird aufgerufen, wenn das Overlay zur Karte hinzugefügt wird, indem setMap auf einer WebGLOverlayView-Instanz aufgerufen wird. Hier sollten Sie alle Aufgaben im Zusammenhang mit WebGL durchführen, die keinen direkten Zugriff auf den WebGL-Kontext erfordern.
  • onContextRestored(): Wird aufgerufen, wenn der WebGL-Kontext verfügbar ist, aber bevor etwas gerendert wird. Hier sollten Sie Objekte initialisieren, den Status binden und alle anderen Aktionen ausführen, die Zugriff auf den WebGL-Kontext benötigen, aber außerhalb des onDraw()-Aufrufs ausgeführt werden können. So kannst du alles einrichten, was du brauchst, ohne den Overhead für das tatsächliche Rendern der Karte zu erhöhen, was bereits GPU-aufwendig ist.
  • onDraw(): Wird einmal pro Frame aufgerufen, sobald WebGL mit dem Rendern der Karte und anderen angeforderten Elementen beginnt. Du solltest so wenig wie möglich in onDraw() tun, um Leistungsprobleme beim Rendern der Karte zu vermeiden.
  • onContextLost(): Wird aufgerufen, wenn der WebGL-Renderingkontext aus irgendeinem Grund verloren geht.
  • onRemove(): Wird aufgerufen, wenn das Overlay aus der Karte entfernt wird, indem setMap(null) in einer WebGLOverlayView-Instanz aufgerufen wird.

In diesem Schritt erstellen Sie eine Instanz von WebGLOverlayView und implementieren die drei zugehörigen Lebenszyklus-Webhooks onAdd, onContextRestored und onDraw. Damit alles übersichtlich und übersichtlich ist, wird der gesamte Code für das Overlay mit der initWebGLOverlayView()-Funktion verarbeitet. Diese befindet sich in der Startvorlage für dieses Codelab.

  1. Erstelle eine WebGLOverlayView()-Instanz.

    Das Overlay wird von der Maps JS API in google.maps.WebGLOverlayView bereitgestellt. Erstellen Sie als Erstes eine Instanz, indem Sie initWebGLOverlayView() Folgendes hinzufügen:
    const webGLOverlayView = new google.maps.WebGLOverlayView();
    
  2. Lebenszyklus-Webhooks implementieren.

    Wenn Sie den Lebenszyklus-Webhooks implementieren möchten, hängen Sie Folgendes an initWebGLOverlayView() an:
    webGLOverlayView.onAdd = () => {};
    webGLOverlayView.onContextRestored = ({gl}) => {};
    webGLOverlayView.onDraw = ({gl, coordinateTransformer}) => {};
    
  3. Overlay-Instanz zur Karte hinzufügen

    Rufe setMap() auf der Overlay-Instanz auf und übergib die Karte. Hänge dazu Folgendes an initWebGLOverlayView() an:
    webGLOverlayView.setMap(map)
    
  4. Rufen Sie einfach initWebGLOverlayView an.

    Im letzten Schritt wird initWebGLOverlayView() ausgeführt, indem Folgendes zur sofort aufgerufenen Funktion unten in app.js hinzugefügt wird:
    initWebGLOverlayView(map);
    

Die Funktion initWebGLOverlayView und die sofort aufgerufene Funktion sollten nun so aussehen:

    async function initWebGLOverlayView (map) {
      let scene, renderer, camera, loader;
      const webGLOverlayView = new google.maps.WebGLOverlayView();

      webGLOverlayView.onAdd = () => {}
      webGLOverlayView.onContextRestored = ({gl}) => {}
      webGLOverlayView.onDraw = ({gl, coordinateTransformer}) => {}
      webGLOverlayView.setMap(map);
    }

    (async () => {
      const map = await initMap();
      initWebGLOverlayView(map);
    })();

Das ist alles, was du brauchst, um WebGLOverlayView zu implementieren. Als Nächstes richtest du alles ein, um ein 3D-Objekt auf der Karte mit Three.js zu rendern.

5. Two.js-Szene einrichten

Die Verwendung von WebGL kann kompliziert sein, weil Sie alle Aspekte jedes Objekts manuell und dann einige definieren müssen. Für dieses Codelab verwenden Sie am besten Three.js, eine beliebte Grafikbibliothek mit einer vereinfachten Abstraktionsebene über WebGL. Three.js bietet zahlreiche praktische Funktionen, die von der Erstellung eines WebGL-Renderers bis hin zum Zeichnen gängiger 2D- und 3D-Objektformen sowie für die Steuerung von Kameras, Objekttransformationen und vielem mehr reichen.

In Three.js gibt es drei grundlegende Objekttypen, die zum Anzeigen aller Elemente erforderlich sind:

  • Ambiente: Ein &Container, in dem alle Objekte, Lichtquellen, Texturen usw. gerendert und angezeigt werden.
  • Kamera: Eine Kamera, die den Blickwinkel der Szene darstellt. Es sind mehrere Kameratypen verfügbar. Eine oder mehrere Kameras können gleichzeitig aufgenommen werden.
  • Renderer: Dieser Renderer verarbeitet alle Elemente in einer Szene. In Three.js wird WebGLRenderer am häufigsten verwendet. Einige sind jedoch auch als Fallbacks verfügbar, falls der Client WebGL nicht unterstützt.

In diesem Schritt laden Sie alle erforderlichen Abhängigkeiten für Three.js und richten eine grundlegende Szene ein.

  1. Laden Sie drei.js

    Sie benötigen zwei Abhängigkeiten für dieses Codelab: die Three.js-Bibliothek und den GLTF-Ladeprogramm, eine Klasse, mit der 3D-Objekte im GL Trasmission Format (gLTF) geladen werden können. Three.js bietet spezielle Load-Balancer für viele 3D-Objektformate, aber die Verwendung von gLTF wird empfohlen.

    Im Code unten wird die gesamte Three.js-Bibliothek importiert. In einer Produktions-App würden Sie wahrscheinlich nur die benötigten Kurse importieren, aber für dieses Codelab können Sie die gesamte Bibliothek importieren, um den Vorgang zu vereinfachen. Beachten Sie auch, dass der GLTF-Ladegerät nicht in der Standardbibliothek enthalten ist und aus einem separaten Pfad in der Abhängigkeit importiert werden muss. Dies ist der Pfad, auf den Sie auf alle Load-Balancer zugreifen können, die von Three.js bereitgestellt werden.

    Fügen Sie oben in app.js Folgendes ein, um Three.js und die GLTF-Ladefunktion zu importieren:
    import * as THREE from 'three';
    import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js';
    
  2. Erstellen Sie eine 3.js-Szene.

    Um eine Szene zu erstellen, musst du die Three.js-Klasse Scene instanziieren, indem du Folgendes an den Webhook onAdd anhängt:
    scene = new THREE.Scene();
    
  3. Fügen Sie das Sichtfeld der Kamera hinzu.

    Wie bereits erwähnt, stellt die Kamera die Perspektive der Szene dar und bestimmt, wie Three.js mit dem visuellen Rendering von Objekten in einer Szene umgeht. Ohne Kamera ist die Szene praktisch nicht sichtbar. Das bedeutet, dass Objekte nicht angezeigt werden, weil sie nicht gerendert werden.

    Three.js bietet eine Vielzahl verschiedener Kameras, die beeinflussen, wie Renderer Objekte in Bezug auf Perspektive und Tiefe verarbeitet. In dieser Szene verwenden Sie PerspectiveCamera, den am häufigsten verwendeten Kameratyp in Three.js. Damit wird die Sicht des menschlichen Auges emuliert. Das bedeutet, dass Objekte, die weiter von der Kamera entfernt sind, kleiner dargestellt werden als Objekte, die sich näher befinden, und einen Punkt mit einem Fluchtpunkt.

    Um eine perspektivische Kamera in eine Szene aufzunehmen, hängen Sie Folgendes an den Hook onAdd an:
    camera = new THREE.PerspectiveCamera();
    
    Mit PerspectiveCamera können Sie auch die Attribute konfigurieren, aus denen der Blickwinkel besteht, einschließlich der Nah- und Fernebenen, des Seitenverhältnisses und des Sichtfelds (Fokus). Insgesamt bilden diese Attribute den sogenannten Frustum, also ein wichtiges Konzept für die 3D-Darstellung, aber nicht im Rahmen dieses Codelabs. Die Standardkonfiguration für PerspectiveCamera reicht aus.
  4. Fügen Sie Lichtquellen hinzu.

    Objekte, die in einer Three.js-Szene gerendert werden, sind standardmäßig schwarz, unabhängig von den angewendeten Texturen. Das liegt daran, dass eine Three.js-Szene die Umgebung von Objekten imitiert, bei der die Farbe von einem Licht reflektiert wird, das von einem Objekt reflektiert wird. Kurz gesagt: Kein Licht, keine Farbe.

    Three.js bietet verschiedene Lampen, für die zwei Lampen verwendet werden:

  5. AmbientLight: Stellt eine diffuse Lichtquelle bereit, die alle Objekte in der Sahne aus allen Perspektiven gleichmäßig beleuchtet. Dadurch erhält die Szene einen Grundlichtlicht, damit die Texturen aller Objekte gut sichtbar sind.
  6. DirectionalLight: Zeigt an, dass ein Licht aus einer Richtung im Bild kommt. Anders als bei der Positionierung eines Positionslichts in der realen Welt sind die Lichtstrahlen, die von DirectionalLight ausstrahlen, alle parallel und nicht verstreut und verstreut, wenn sie sich weiter von der Lichtquelle entfernen.

    Farbe und Intensität der einzelnen Lichtquellen lassen sich so konfigurieren, dass zusammengefasste Lichteffekte entstehen. Im folgenden Code beispielsweise liefert das Umgebungslicht ein weiches weißes Licht für die gesamte Szene. Das gerichtete Licht hingegen stellt ein sekundäres Licht bereit, das Gegenstände nach unten abwinkelt. Im Hinblick auf die Richtungsrichtung ist der Winkel mit position.set(x, y ,z) festgelegt, wobei jeder Wert relativ zur entsprechenden Achse ist. Beispielsweise wird position.set(0,1,0)das Licht direkt über dem Bild auf der y-Achse so platziert, dass es direkt nach unten zeigt.

    Um die Lichtquellen der Szene hinzuzufügen, hängen Sie Folgendes an den Webhook onAdd an:
    const ambientLight = new THREE.AmbientLight( 0xffffff, 0.75 );
    scene.add(ambientLight);
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.25);
    directionalLight.position.set(0.5, -1, 0.5);
    scene.add(directionalLight);
    

Ihr onAdd-Webhook sollte jetzt so aussehen:

    webGLOverlayView.onAdd = () => {
      scene = new THREE.Scene();
      camera = new THREE.PerspectiveCamera();
      const ambientLight = new THREE.AmbientLight( 0xffffff, 0.75 );
      scene.add(ambientLight);
      const directionalLight = new THREE.DirectionalLight(0xffffff, 0.25);
      directionalLight.position.set(0.5, -1, 0.5);
      scene.add(directionalLight);
    }

Ihre Szene ist jetzt bereit zum Rendern. Als Nächstes konfigurieren Sie den WebGL-Renderer und rendern die Szene.

6. Szene rendern

Zeit zum Rendern der Szene. Bis dahin wurden alle mit Three.js erstellten Elemente im Code initialisiert. Sie sind jedoch im Wesentlichen nicht vorhanden, da sie noch nicht in den WebGL-Renderingkontext gerendert wurden. WebGL rendert 2D- und 3D-Inhalte im Browser mit der Canvas API. Wenn Sie die Canvas API bereits verwendet haben, sind Sie wahrscheinlich mit der context eines HTML-Canvas vertraut, in dem alle Elemente gerendert werden. Es handelt sich möglicherweise um eine Schnittstelle, die den OpenGL-Grafik-Rendering-Kontext über die WebGLRenderingContext API im Browser anzeigt.

Für einen einfacheren Umgang mit dem WebGL-Renderer bietet Three.js WebGLRenderer. Mit diesem Wrapper lässt sich der WebGL-Renderingkontext relativ einfach konfigurieren, sodass Three.js Szenen im Browser rendern kann. Für die Karte reicht es jedoch nicht aus, nur die Three.js-Szene im Browser neben der Karte zu rendern. Three.js muss in denselben Rendering-Kontext wie die Karte gerendert werden, damit sowohl die Karte als auch alle Objekte aus der Three.js-Szene im selben Weltraum gerendert werden. Hierdurch kann der Renderer Interaktionen zwischen Objekten auf der Karte und Objekten im Bild bearbeiten, z. B. durch einen Okklusion. Das ist eine tolle Methode, mit der Objekte, die sich hinter ihnen befinden, ausgeblendet werden.

Klingt ziemlich kompliziert, oder? Glücklicherweise ist Three.js wieder verfügbar.

  1. Richte den WebGL-Renderer ein.

    Wenn Sie eine neue Instanz des Three.js-WebGLRenderers erstellen, können Sie dort den WebGL-Renderingkontext angeben, in den die Szene gerendert werden soll. Erinnern Sie sich noch an das Argument gl, das an den Hook onContextRestored übergeben wird? Dieses gl-Objekt ist der WebGL-Renderingkontext der Karte. Du musst nur den Kontext, seinen Canvas und seine Attribute für die WebGLRenderer-Instanz bereitstellen. Alle sind über das gl-Objekt verfügbar. In diesem Code wird außerdem die autoClear-Eigenschaft des Renderers auf false gesetzt, damit die Renderer die Ausgabe nicht in jedem Frame löschen.

    Hängen Sie zum Konfigurieren des Renderers Folgendes an den Webhook onContextRestored an:
    renderer = new THREE.WebGLRenderer({
      canvas: gl.canvas,
      context: gl,
      ...gl.getContextAttributes(),
    });
    renderer.autoClear = false;
    
  2. Rendern Sie die Szene.

    Sobald der Renderer konfiguriert ist, rufe requestRedraw auf der WebGLOverlayView-Instanz auf, um dem Overlay mitzuteilen, dass beim nächsten Frame eine Neuzeichnung erforderlich ist. Rufe dann beim Renderer render auf und übergebe die Three.js-Szene und -Kamera zum Rendern. Löschen Sie zuletzt den Status des WebGL-Renderingkontexts. Dies ist ein wichtiger Schritt zur Vermeidung von GL-Statuskonflikten, da die Verwendung von WebGL Overlay View auf einem gemeinsamen GL-Status basiert. Wenn der Zustand nicht am Ende jedes Zeichenaufrufs zurückgesetzt wird, kann der Renderer durch GL-Statuskonflikte fehlschlagen.

    Fügen Sie dazu Folgendes an den Hook onDraw an, damit er in jedem Frame ausgeführt wird:
    webGLOverlayView.requestRedraw();
    renderer.render(scene, camera);
    renderer.resetState();
    

Ihre onContextRestored- und onDraw-Webhooks sollten jetzt so aussehen:

    webGLOverlayView.onContextRestored = ({gl}) => {
      renderer = new THREE.WebGLRenderer({
        canvas: gl.canvas,
        context: gl,
        ...gl.getContextAttributes(),
      });

      renderer.autoClear = false;
    }

    webGLOverlayView.onDraw = ({gl, transformer}) => {
      webGLOverlayView.requestRedraw();
      renderer.render(scene, camera);
      renderer.resetState();
    }

7. 3D-Modell auf der Karte rendern

Ok, du kannst alle Komponenten verwenden. Du hast WebGl Overlay View eingerichtet und eine Three.js-Szene erstellt, aber es gibt ein Problem: da ist nichts. Als Nächstes kommt es auf das Rendering eines 3D-Objekts in der Szene an. Verwenden Sie dazu den zuvor importierten GLTF-Lademodus.

3D-Modelle sind in vielen verschiedenen Formaten verfügbar. Für Three.js ist das gLTF-Format aufgrund seiner Größe und der Laufzeitleistung das bevorzugte Format. In diesem Codelab wird in /src/pin.gltf bereits ein Modell für das Rendering bereitgestellt.

  1. Erstellen Sie eine Modellloader-Instanz.

    Folgender Text an onAdd anhängen:
    loader = new GLTFLoader();
    
  2. Ein 3D-Modell laden

    Modellladegeräte sind asynchron und führen einen Callback aus, sobald das Modell vollständig geladen ist. Hängen Sie zum Laden von pin.gltf Folgendes an onAdd an:
    const source = "pin.gltf";
    loader.load(
      source,
      gltf => {}
    );
    
  3. Fügen Sie das Modell zur Szene hinzu.

    Jetzt kannst du das Modell der Szene hinzufügen, indem du Folgendes an den loader-Callback anfügst. Beachten Sie, dass gltf.scene hinzugefügt wird, nicht gltf:
    scene.add(gltf.scene);
    
  4. Matrix für die Kameraprojektion konfigurieren

    Schließlich müssen Sie noch etwas tun, damit das Modell auf der Karte richtig gerendert wird. Dazu müssen Sie die Projektionsmatrix der Kamera in der Three.js-Szene festlegen. Die Projektionsmatrix wird als Three.js-Matrix4-Array angegeben. Sie definiert einen Punkt in dreidimensionalen Bereichen sowie Transformationen wie Rotationen, Scheren, Skalierung und mehr.

    Im Fall von WebGLOverlayView wird die Projektionsmatrix verwendet, um dem Renderer mitzuteilen, wo und wie die Three.js-Szene im Verhältnis zur Basiskarte gerendert werden soll. Aber es gibt ein Problem. Orte auf der Karte werden als Koordinaten für Breiten- und Längengrad angegeben, während Orte in der Three.js-Szene Vector3-Koordinaten sind. Die Berechnung der Conversion zwischen den beiden Systemen ist nicht einfach. Um dies zu beheben, übergibt WebGLOverlayView ein coordinateTransformer-Objekt an den Lebenszyklus-Webhook OnDraw, der eine Funktion namens fromLatLngAltitude enthält. fromLatLngAltitude verwendet ein LatLngAltitude- oder LatLngAltitudeLiteral-Objekt und optional eine Reihe von Argumenten, die eine Transformation für die Szene definieren. Anschließend werden diese in eine Modellansichtsprojektmatrix (MVP) umgewandelt. Sie müssen nur angeben, wo auf der Karte die Three.js-Szene gerendert werden soll. Außerdem bestimmen Sie, wie sie umgestaltet werden soll. WebGLOverlayView erledigt den Rest. Anschließend kannst du die MVP-Matrix in ein Three.js-Matrix4-Array umwandeln und darauf festlegen.

    Im Code unten wird dem zweiten Argument mitgeteilt, dass WebGl Overlay View die Höhe der Three.js-Szene 120 m über dem Boden festlegen soll, wodurch das Modell unverankert wirkt.

    Wenn Sie die Projektionsmatrix der Kamera festlegen möchten, hängen Sie Folgendes an den Webhook onDraw an:
    const latLngAltitudeLiteral = {
        lat: mapOptions.center.lat,
        lng: mapOptions.center.lng,
        altitude: 120
    }
    const matrix = transformer.fromLatLngAltitude(latLngAltitudeLiteral);
    camera.projectionMatrix = new THREE.Matrix4().fromArray(matrix);
    
  5. Modell transformieren.

    Sie sehen, dass die Stecknadel nicht senkrecht zur Karte steht. In einem 3D-Grafikbereich hat jedes Objekt neben der Welt mit eigenen x-, y- und z-Achsen, die die Ausrichtung bestimmen, auch einen eigenen Objektbereich mit unabhängigen Achsen.

    In diesem Fall wurde das Objekt nicht mit dem Element erstellt, das normalerweise die oberste Position der Markierung auf der y-Achse betrifft, sodass Sie das Objekt in die gewünschte Richtung relativ zum Weltraum ausrichten müssen. Rufen Sie dazu rotation.set auf. Beachten Sie, dass die Rotation in Three.js in Radianten und nicht in Grad angegeben wird. Es ist im Allgemeinen einfacher, in Graden nachzudenken. Daher muss die entsprechende Conversion mithilfe der Formel degrees * Math.PI/180 erfolgen.

    Außerdem ist das Modell etwas klein. Deswegen kannst du es auf allen Achsen gleichmäßig skalieren, indem du scale.set(x, y ,z) aufrufst.

    Wenn Sie das Modell drehen und skalieren möchten, fügen Sie Folgendes im loader-Callback von onAdd vor scene.add(gltf.scene) hinzu. Dadurch wird das gLTF in die Szene aufgenommen:
    gltf.scene.scale.set(25,25,25);
    gltf.scene.rotation.x = 180 * Math.PI/180;
    

Die Markierung steht jetzt aufrecht im Verhältnis zur Karte.

Kontrast

Ihre onAdd- und onDraw-Webhooks sollten jetzt so aussehen:

    webGLOverlayView.onAdd = () => {
      scene = new THREE.Scene();
      camera = new THREE.PerspectiveCamera();
      const ambientLight = new THREE.AmbientLight( 0xffffff, 0.75 ); // soft white light
      scene.add( ambientLight );
      const directionalLight = new THREE.DirectionalLight(0xffffff, 0.25);
      directionalLight.position.set(0.5, -1, 0.5);
      scene.add(directionalLight);

      loader = new GLTFLoader();
      const source = 'pin.gltf';
      loader.load(
        source,
        gltf => {
          gltf.scene.scale.set(25,25,25);
          gltf.scene.rotation.x = 180 * Math.PI/180;
          scene.add(gltf.scene);
        }
      );
    }

    webGLOverlayView.onDraw = ({gl, transformer}) => {
      const latLngAltitudeLiteral = {
        lat: mapOptions.center.lat,
        lng: mapOptions.center.lng,
        altitude: 100
      }

      const matrix = transformer.fromLatLngAltitude(latLngAltitudeLiteral);
      camera.projectionMatrix = new THREE.Matrix4().fromArray(matrix);

      webGLOverlayView.requestRedraw();
      renderer.render(scene, camera);
      renderer.resetState();
    }

Als Nächstes kommt eine Kameraanimation.

8. Kamera animieren

Da Sie nun ein Modell auf der Karte gerendert haben und alles drei Dimensionen verschieben können, sollten Sie diese Aufgabe als Nächstes programmatisch steuern. Mit der Funktion moveCamera kannst du die Eigenschaften der Mitte, des Zooms und der Neigung der Karte gleichzeitig festlegen. Dadurch erhältst du eine genauere Kontrolle über die Nutzererfahrung. Außerdem kann moveCamera in einer Animationsschleife aufgerufen werden, um fließende Übergänge zwischen Frames mit einer Framerate von fast 60 Bildern pro Sekunde zu erstellen.

  1. Warten Sie, bis das Modell geladen ist.

    Um eine optimale Nutzererfahrung zu ermöglichen, solltest du mit dem Bewegen der Kamera warten, bis das GLTF-Modell geladen wurde. Hängen Sie dazu den Lade-Handler onLoad an den Webhook onContextRestored an:
    loader.manager.onLoad = () => {}
    
  2. Animationsschleife erstellen

    Es gibt mehrere Möglichkeiten, eine Animationsschleife zu erstellen, beispielsweise die Verwendung von setInterval oder requestAnimationFrame. In diesem Fall verwenden Sie die Funktion setAnimationLoop des Three.js-Renderers, der jedes Mal, wenn in Three.js ein neuer Frame gerendert wird, automatisch Code aufgerufen wird, den Sie im Callback deklarieren. Fügen Sie dem Ereignis-Handler onLoad im vorherigen Schritt Folgendes hinzu, um die Animationsschleife zu erstellen:
    renderer.setAnimationLoop(() => {});
    
  3. Kameraposition in der Animationsschleife festlegen

    Rufe dann moveCamera auf, um die Karte zu aktualisieren. Hier werden die Eigenschaften des mapOptions-Objekts, mit dem die Karte geladen wurde, zum Definieren der Kameraposition verwendet:
    map.moveCamera({
      "tilt": mapOptions.tilt,
      "heading": mapOptions.heading,
      "zoom": mapOptions.zoom
    });
    
  4. Aktualisieren Sie die Kamera in jedem Frame.

    Letzter Schritt! Aktualisiere das mapOptions-Objekt am Ende jedes Frames, um die Kameraposition für den nächsten Frame festzulegen. In diesem Code wird eine if-Anweisung verwendet, um die Neigung zu erhöhen, bis der maximale Neigungswert von 67,5 % erreicht ist.Dann wird die Ausrichtung in jedem Frame etwas geändert, bis die Kamera eine vollständige 360°-Drehung abgeschlossen hat. Nach Abschluss der gewünschten Animation wird null an setAnimationLoop übergeben, damit die Animation abgebrochen wird. Sie wird dann nicht dauerhaft ausgeführt.
    if (mapOptions.tilt < 67.5) {
      mapOptions.tilt += 0.5
    } else if (mapOptions.heading <= 360) {
      mapOptions.heading += 0.2;
    } else {
      renderer.setAnimationLoop(null)
    }
    

Ihr onContextRestored-Webhook sollte jetzt so aussehen:

    webGLOverlayView.onContextRestored = ({gl}) => {
      renderer = new THREE.WebGLRenderer({
        canvas: gl.canvas,
        context: gl,
        ...gl.getContextAttributes(),
      });

      renderer.autoClear = false;

      loader.manager.onLoad = () => {
        renderer.setAnimationLoop(() => {
           map.moveCamera({
            "tilt": mapOptions.tilt,
            "heading": mapOptions.heading,
            "zoom": mapOptions.zoom
          });

          if (mapOptions.tilt < 67.5) {
            mapOptions.tilt += 0.5
          } else if (mapOptions.heading <= 360) {
            mapOptions.heading += 0.2;
          } else {
            renderer.setAnimationLoop(null)
          }
        });
      }
    }

9. Glückwunsch

Wenn alles wie geplant läuft, sollten Sie jetzt eine Karte mit einer großen 3D-Markierung erstellen können, die so aussieht:

Finale 3D-Markierung

Das haben Sie gelernt

In diesem Codelab haben Sie einiges gelernt. Hier die Highlights:

  • WebGLOverlayView und seine Lebenszyklus-Webhooks implementieren
  • Three.js in die Karte einbinden
  • Grundlegende Informationen zum Erstellen einer Three.js-Szene mit Kameras und Beleuchtung.
  • 3D-Modelle werden geladen und mit Three.js bearbeitet.
  • Mit der moveCamera Kamera kannst du die Kamera steuern und animieren.

Nächste Schritte

WebGL und Computergrafiken im Allgemeinen sind ein komplexes Thema. Deshalb gibt es immer viel zu lernen. Hier sind ein paar hilfreiche Ressourcen zum Thema: