1. Zanim zaczniesz
Dzięki nim dowiesz się, jak korzystać z funkcji WebGL API interfejsu Maps JavaScript do wyświetlania i renderowania mapy wektorowej na 3 wymiarach.
Wymagania wstępne
W tym ćwiczeniu zakładamy, że znasz język JavaScript i interfejs API Maps JavaScript. Aby poznać podstawy korzystania z interfejsu Maps JS API, skorzystaj z ćwiczenia z programowania dotyczącego dodawania kodu JavaScript do witryny.
Czego się nauczysz
- Generowanie identyfikatora mapy z mapą wektorową dla JavaScriptu.
- Kontrolowanie mapy za pomocą automatycznego pochylenia i obrotu.
- Renderowanie obiektów 3D na mapie przy użyciu znaczników
WebGLOverlayView
i Three.js. - Animowanie ruchów kamery za pomocą
moveCamera
.
Czego potrzebujesz
- konto Google Cloud Platform z włączonymi płatnościami,
- Klucz interfejsu API Google Maps Platform z włączonym interfejsem Maps JavaScript API
- biegła znajomość JavaScriptu, HTML i CSS
- Twój wybrany edytor tekstu lub IDE
- Node.js
2. Konfiguracja
Aby włączyć tę funkcję, musisz włączyć interfejs Maps JavaScript API.
Konfigurowanie Google Maps Platform
Jeśli nie masz jeszcze konta Google Cloud Platform ani projektu z włączonymi płatnościami, przeczytaj przewodnik Pierwsze kroki z Google Maps Platform, by utworzyć konto rozliczeniowe i projekt.
- W Cloud Console kliknij menu projektu i wybierz projekt, którego chcesz użyć w tym ćwiczeniu z programowania.
- Włącz interfejsy API i pakiety SDK Google Maps Platform wymagane w ramach tego ćwiczenia z ćwiczeń w Google Cloud Marketplace. W tym celu wykonaj czynności opisane w tym filmie lub w tej dokumentacji.
- Wygeneruj klucz interfejsu API na stronie Dane logowania w Cloud Console. Odpowiednie instrukcje znajdziesz w tym filmie lub w tej dokumentacji. Wszystkie żądania wysyłane do Google Maps Platform wymagają klucza interfejsu API.
Konfiguracja Node.js
Jeśli nie masz jeszcze środowiska Node.js, otwórz stronę https://nodejs.org/, aby je pobrać i zainstalować na komputerze.
Do środowiska Node.js dołączona jest menedżer pakietów npm, który musisz zainstalować w zależności od tego ćwiczenia z programowania.
Pobierz szablon startowy projektu
Zanim rozpoczniesz to ćwiczenie programowania, wykonaj te czynności, by pobrać szablon projektu startowego oraz pełny kod rozwiązania:
- Pobierz lub utwórz rozwidlenie repozytorium GitHub dla tego ćwiczenia z programowania na https://github.com/googlecodelabs/maps-platform-101-webgl/. Projekt początkowy znajduje się w katalogu
/starter
i obejmuje podstawową strukturę pliku potrzebną do ukończenia ćwiczeń z programowania. Wszystkie potrzebne informacje znajdziesz w katalogu/starter/src
. - Po pobraniu projektu początkowego uruchom
npm install
w katalogu/starter
. Spowoduje to zainstalowanie wszystkich wymaganych zależności wymienionych w narzędziupackage.json
. - Po zainstalowaniu zależności uruchom
npm start
w katalogu.
Projekt startowy został skonfigurowany tak, aby użytkownicy mogli korzystać z Webpack-dev-server, który kompiluje kod i uruchamia go lokalnie. Komponent webpack-dev-server automatycznie ładuje też Twoją aplikację w przeglądarce za każdym razem, gdy zmieniasz kod.
Jeśli chcesz zobaczyć pełny kod rozwiązania, możesz wykonać opisane powyżej czynności konfiguracyjne w katalogu /solution
.
Dodaj klucz interfejsu API
Aplikacja startowa zawiera cały kod potrzebny do wczytania mapy za pomocą biblioteki JS API, więc wystarczy, że podasz klucz interfejsu API i identyfikator mapy. Biblioteka JS API to prosta biblioteka, która abstrakcyjnie tradycyjną metodę wczytywania interfejsu API JS Map Google w szablonie HTML za pomocą tagu script
umożliwia obsługę wszystkich elementów w kodzie JavaScript.
Aby dodać klucz interfejsu API, wykonaj te czynności w projekcie początkowym:
- Otwórz aplikację
app.js
. - W obiekcie
apiOptions
ustaw klucz interfejsu API na wartośćapiOptions.apiKey
.
3. Generowanie i używanie identyfikatora mapy
Aby korzystać z funkcji Maps JavaScript API opartych na WebGL, potrzebujesz identyfikatora mapy z włączoną mapą wektorową.
Generowanie identyfikatora mapy
- W Google Cloud Console otwórz „Google Maps Platform” i „Zarządzanie mapami”.
- Kliknij „UTWÓRZ NOWY IDENTYFIKATOR MAPY”.
- W polu „Nazwa mapy” wpisz nazwę dla identyfikatora mapy.
- Z menu „Typ mapy” wybierz „JavaScript'. Pojawią się opcje JavaScript.
- W sekcji „Opcje JavaScript” zaznacz pole „Wektor i 39; zaznacz pole wyboru „Przechylanie” i „Obrót'.”.
- Opcjonalnie. W polu „Opis” wpisz opis klucza interfejsu API.
- Kliknij przycisk „Dalej”. Pojawi się strona „Szczegóły identyfikatora mapy”.
- Skopiuj identyfikator mapy. Użyjesz go w następnym kroku, aby wczytać mapę.
Używanie identyfikatora mapy
Aby wczytać mapę wektorową, musisz podać identyfikator mapy jako właściwość w opcjach tworzenia instancji tej mapy. Opcjonalnie możesz też podać ten sam identyfikator mapy podczas wczytywania interfejsu Maps JavaScript API.
Aby wczytać mapę za pomocą identyfikatora mapy, wykonaj te czynności:
- Ustaw identyfikator mapy jako wartość
mapOptions.mapId
.
Jeśli podasz identyfikator mapy podczas tworzenia instancji mapy, Google Maps Platform wskaże, które z nich mają zostać wczytane w danej instancji. Możesz wykorzystać ten sam identyfikator mapy w wielu aplikacjach lub różnych widokach w tej samej aplikacji.const mapOptions = { "tilt": 0, "heading": 0, "zoom": 18, "center": { lat: 35.6594945, lng: 139.6999859 }, "mapId": "YOUR_MAP_ID" };
Sprawdź aplikację działającą w przeglądarce. Mapa wektorowa z włączonym przechyleniem i obrotem powinna się wczytywać. Aby sprawdzić, czy pochylenie i obrót są włączone, przytrzymaj klawisz Shift i przeciągnij kursorem myszy lub użyj klawiszy strzałek na klawiaturze.
Jeśli mapa się nie wczytuje, sprawdź, czy w kluczu apiOptions
podano prawidłowy klucz interfejsu API. Jeśli mapa nie przechyla się i obraca, sprawdź, czy w apiOptions
i mapOptions
masz podany identyfikator mapy z włączonym pochyleniem i obrotem.
Twój plik app.js
powinien teraz wyglądać tak:
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. Wdrażanie WebGLOverlayView
WebGLOverlayView
zapewnia bezpośredni dostęp do tego samego kontekstu renderowania WebGL, który jest używany do renderowania mapy podstawowej wektorowej. Oznacza to, że obiekty 2D i 3D możesz renderować bezpośrednio na mapie przy użyciu WebGL, a także w popularnych bibliotekach graficznych opartych na WebGL.
WebGLOverlayView
udostępnia 5 haków na cykl życia renderowania WebGL mapy, którego możesz użyć. Oto krótki opis każdego przywiązania i jego celu:
onAdd()
: wywoływane, gdy nakładka zostanie dodana do mapy, wywołującsetMap
w instancjiWebGLOverlayView
. W tym miejscu wykonaj zadania związane z WebGL, które nie wymagają bezpośredniego dostępu do kontekstu WebGL.onContextRestored()
: element jest wywoływany, gdy kontekst WebGL będzie dostępny, ale przed wyrenderowaniem czegokolwiek. W tym miejscu należy zainicjować obiekty, powiązać stan i wykonać inne czynności, które wymagają dostępu do kontekstu WebGL, ale mogą być wykonywane poza wywołaniemonDraw()
. Dzięki temu możesz skonfigurować wszystko, czego potrzebujesz, bez nadmiernego nakładu pracy związanego z renderowaniem mapy, które jest już bardzo zaawansowane z użyciem GPU.onDraw()
: wywoływana raz na klatkę, gdy WebGL zaczyna renderować mapę i wszelkie inne żądane elementy. Postaraj się jak najszybciej wykonać zadanie w wersjionDraw()
, aby uniknąć problemów z wydajnością renderowania mapy.onContextLost()
: wywoływane, gdy kontekst renderowania WebGL zostanie utracony z dowolnego powodu.onRemove()
: wywołana, gdy nakładka została usunięta z mapy, wywołującsetMap(null)
w instancjiWebGLOverlayView
.
W tym kroku utworzysz instancję WebGLOverlayView
i zaimplementujesz 3 haczyki cyklu życia: onAdd
, onContextRestored
i onDraw
. W celu zachowania czytelności i łatwości śledzenia cały kod nakładki będzie obsługiwany przez funkcję initWebGLOverlayView()
podaną w szablonie początkowym tego ćwiczenia z ćwiczenia.
- Utwórz instancję
WebGLOverlayView()
.
Nakładka jest dostarczana przez interfejs Maps JS API wgoogle.maps.WebGLOverlayView
. Na początek utwórz instancję, oczekując nainitWebGLOverlayView()
:const webGLOverlayView = new google.maps.WebGLOverlayView();
- Zaimplementuj haki cyklu życia.
Aby zaimplementować cykle cyklu życia, dodaj ten ciąg do elementuinitWebGLOverlayView()
:webGLOverlayView.onAdd = () => {}; webGLOverlayView.onContextRestored = ({gl}) => {}; webGLOverlayView.onDraw = ({gl, coordinateTransformer}) => {};
- Dodaj wystąpienie mapy do mapy.
Teraz wywołajsetMap()
w instancji nakładki i przekaż mapę, dodając następujący fragment doinitWebGLOverlayView()
:webGLOverlayView.setMap(map)
- Zadzwoń do firmy
initWebGLOverlayView
.
Ostatnim krokiem jest wykonanieinitWebGLOverlayView()
, dodając poniższy kod do funkcji wywoływanej u dołu elementuapp.js
:initWebGLOverlayView(map);
Funkcja initWebGLOverlayView
i natychmiastowo wywołana funkcja powinny wyglądać tak:
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);
})();
To wszystko, czego potrzebujesz, aby wdrożyć WebGLOverlayView
. Następnie skonfiguruj wszystko, czego potrzebujesz do renderowania obiektu 3D na mapie, używając Three.js.
5. Skonfiguruj scenę 3.js
Korzystanie z WebGL może być bardzo skomplikowane, ponieważ wymaga ręcznego określenia wszystkich aspektów każdego obiektu, a potem niektórych. Dla ułatwienia do tego ćwiczenia wykorzystamy 3.js – popularną bibliotekę grafiki – udostępnia ona uproszczoną warstwę abstrakcyjną oprócz interfejsu WebGL. Three.js oferuje szeroką gamę wygodnych funkcji, które pozwalają korzystać z wielu elementów – od renderowania renderowania WebGL po rysowanie popularnych kształtów obiektów 2D i 3D, a także sterowanie kamerami i przekształcaniem obiektów.
W pliku Three.js istnieją 3 podstawowe typy obiektów, które są wymagane do wyświetlania czegokolwiek:
- Scena: &kontener" gdzie wszystkie obiekty, źródła światła, tekstury itd. są renderowane i wyświetlane.
- Aparat: kamera reprezentująca punkt widokowy. Dostępnych jest wiele rodzajów kamer. Do jednej sceny można dodać co najmniej jedną kamerę.
- Mechanizm renderowania: mechanizm renderowania, który służy do przetwarzania i wyświetlania wszystkich obiektów w scenie. W aplikacji Three.js najczęściej używany jest
WebGLRenderer
, ale kilka innych jest dostępnych jako działanie awaryjne na wypadek, gdyby klient nie obsługiwał WebGL.
W tym kroku wczytasz wszystkie zależności wymagane przez kod Three.js i skonfigurujesz podstawową scenę.
- Wczytaj trzy.js
Te ćwiczenia z programowania będą wymagały 2 zależności: biblioteki Three.js i modułu wczytywania GLTF, który umożliwia ładowanie obiektów 3D w formacie GL Trasmission (gLTF). Three.js oferuje specjalistyczne narzędzia do wczytywania różnych formatów obiektów 3D, ale zalecamy używanie gLTF.
W poniższym kodzie zaimportowana jest cała biblioteka Three.js. W aplikacji produkcyjnej możesz chcieć zaimportować tylko te zajęcia, z którymi chcesz korzystać. Aby ułatwić sobie pracę z programowaniem, zaimportuj całą bibliotekę. Zauważ też, że komponent GLTF Loader nie znajduje się w bibliotece domyślnej i trzeba go zaimportować z osobnej ścieżki w zależności – jest to ścieżka, na której masz dostęp do wszystkich modułów wczytywania dostępnych w Three.js.
Aby zaimportować 3.js i ładowanie GLTF, u góry stronyapp.js
dodaj:import * as THREE from 'three'; import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js';
- Utwórz scenę 3.js.
Aby utworzyć scenę, wymień klasę 3.jsScene
, dołączając do haczykaonAdd
:scene = new THREE.Scene();
- Dodaj kamerę do sceny.
Jak wspomnieliśmy wcześniej, kamera reprezentuje perspektywę sceny, a także określa, jak Three.js obsługuje wizualne renderowanie obiektów w ramach sceny. Bez kamery scena nie będzie „widoczna”, co oznacza, że obiekty nie pojawią się, ponieważ nie zostaną wyrenderowane.
Strona 3.js udostępnia różne aparaty, które wpływają na to, jak mechanizm renderowania traktuje obiekty względem określonej perspektywy i głębi. W tej scenie użyjeszPerspectiveCamera
(najczęściej stosowanego typu kamery w Three.js), który ma emulować sposób postrzegania obrazu przez ludzkie oko. Oznacza to, że obiekty w większej odległości od kamery będą mniejsze od obiektów, które są bliżej, scena ma znikać i nie tylko.
Aby dodać do punktu widzenia kamerę z perspektywy, dołącz do uchwytuonAdd
te elementy:
W usłudzecamera = new THREE.PerspectiveCamera();
PerspectiveCamera
możesz też skonfigurować atrybuty składające się na punkt widokowy, w tym zbliżenia i odległe obszary, format obrazu oraz pole widzenia (fov). Łącznie tworzą one tzw. frustrację oglądania, co jest niezbędną podstawą przy pracy w 3D, ale wykracza poza zakres tych ćwiczeń z programowania. Wystarczy Ci domyślna konfiguracjaPerspectiveCamera
. - Dodaj do sceny źródła światła.
Domyślnie obiekty renderowane w widoku 3.js są wyświetlane jako czarne, bez względu na zastosowane tekstury. Dzieje się tak, ponieważ scena Three.js naśladuje sposób działania obiektów w świecie rzeczywistym, gdzie widoczność jest zależna od oświetlenia obiektu. Krótko mówiąc, bez światła, bez koloru.
W aplikacji Three.js znajdziesz różnego rodzaju światła, których możesz użyć: AmbientLight
: źródło światła rozproszonego, które równomiernie rozmieszcza wszystkie obiekty w trójkońcu, ze wszystkich kątów. W ten sposób obiekt uzyska podstawową ilość światła, co zapewni, że tekstury na wszystkich obiektach będą widoczne.DirectionalLight
: światło nadaje się do kierunku. W przeciwieństwie do tego, jak działało światło w rzeczywistym świecie, promienie emitujące światło, które emitująDirectionalLight
, są równoległe i nie rozkładają się ani nie rozpraszają, gdy oddalają się od źródła światła.
Możesz skonfigurować kolor i intensywność każdego światła, aby uzyskać efekt zagregowanego oświetlenia. Na przykład w poniższym kodzie światło otoczenia jest dostarczane przez miękkie, białe światło przez całą scenę, a światło kierunkowe zapewnia światło dodatkowe, które pada na kąt obrotu. W przypadku światła kierunkowego kąt jest ustawiany za pomocą wartościposition.set(x, y ,z)
, gdzie każda wartość jest określana względem odpowiedniej osi. Na przykładposition.set(0,1,0)
może umieścić światło bezpośrednio nad sceną na osi Y, skierowanej prosto w dół.
Aby dodać do sceny źródła światła, do haka typuonAdd
dodaj te informacje: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);
Twoja reguła onAdd
powinna wyglądać tak:
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);
}
Scena jest teraz skonfigurowana i gotowa do renderowania. Następnie skonfigurujesz mechanizm renderowania WebGL i renderujesz scenę.
6. Renderuj scenę
Czas wyrenderowania sceny. Do tej pory wszystko, co zostało utworzone przez Ciebie w języku Three.js, jest inicjowane w kodzie, ale zasadniczo nie istnieje, ponieważ nie zostało jeszcze wyrenderowane w kontekście renderowania WebGL. Interfejs WebGL umożliwia renderowanie treści 2D i 3D w przeglądarce przy użyciu interfejsu canvas API. Jeśli zdarzyło Ci się już korzystać z interfejsu Canvas API, prawdopodobnie znasz interfejs context
kanwy HTML, w której wszystko jest renderowane. Możesz nie wiedzieć, że to jest interfejs, który ujawnia kontekst renderowania grafiki przez OpenGL za pomocą interfejsu API WebGLRenderingContext
w przeglądarce.
Aby ułatwić obsługę mechanizmu renderowania WebGL, Three.js udostępnia opakowanie WebGLRenderer
, które pozwala stosunkowo łatwo skonfigurować kontekst renderowania WebGL, co pozwoli przeglądarce Three.js renderować sceny w przeglądarce. W przypadku mapy nie wystarczy jednak po prostu wyrenderowanie sceny Three.js w przeglądarce obok mapy. Kod 3.js musi renderować się w tym samym kontekście co mapa, tak aby mapa i wszystkie obiekty ze sceny Three.js były renderowane w tym samym miejscu w świecie. Dzięki temu mechanizm renderowania może obsługiwać interakcje między obiektami na mapie i innymi obiektami w scenie, np. przesłanianie – to świetna metoda, dzięki której obiekt ukrywa się za obiektem.
Brzmi skomplikowanie, prawda? Na szczęście Three.js przychodzi z pomocą.
- Skonfiguruj mechanizm renderowania WebGL.
Podczas tworzenia nowego wystąpienia pliku Three.jsWebGLRenderer
możesz podać specyficzny kontekst renderowania WebGL, w którym chcesz renderować swoją scenę. Pamiętasz argumentgl
przekazywany do hakaonContextRestored
? Ten obiektgl
to kontekst renderowania mapy za pomocą WebGL. Wystarczy, że podasz kontekst, jego obszar roboczy i jego atrybuty do instancjiWebGLRenderer
. Wszystkie one będą dostępne w obiekciegl
. W tym kodzie właściwośćautoClear
jest też ustawiona nafalse
, dzięki czemu mechanizm renderowania nie czyści danych wyjściowych każdej klatki.
Aby skonfigurować mechanizm renderowania, dodaj te informacje do haczykaonContextRestored
:renderer = new THREE.WebGLRenderer({ canvas: gl.canvas, context: gl, ...gl.getContextAttributes(), }); renderer.autoClear = false;
- Renderuj scenę.
Po skonfigurowaniu mechanizmu renderowania wywołaj metodęrequestRedraw
w instancjiWebGLOverlayView
, aby poinformować nakładkę, że przy renderowaniu następnej klatki potrzebna jest edycja, a następnie wywołajrender
w mechanizmie renderowania i prześlij mu scenę i kamerę Three.js, które mają być renderowane. Na koniec wyczyść stan renderowania WebGL. To ważny krok w celu uniknięcia konfliktów stanu GL, ponieważ korzystanie z widoku nakładki WebGL wykorzystuje stan współużytkowany. Jeśli stan nie zostanie zresetowany na końcu każdego wywołania rysowania, konflikty stanu GL mogą spowodować błędy mechanizmu renderowania.
Aby to zrobić, dołącz do haka typuonDraw
ten fragment, tak by został on uruchomiony w każdej klatce:webGLOverlayView.requestRedraw(); renderer.render(scene, camera); renderer.resetState();
Haki onContextRestored
i onDraw
powinny wyglądać tak:
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. Renderowanie modelu 3D na mapie
Dobrze, masz już wszystko. Udało Ci się skonfigurować widok nakładki WebGL i utworzyć scenę w formacie Three.js, ale wystąpił jeden problem: nie ma w tym nic. Teraz czas wyrenderować obiekt 3D w scenie. Aby to zrobić, użyj zaimportowanego wcześniej GLTF Loader.
Modele 3D są dostępne w wielu różnych formatach, ale w przypadku Three.js preferowany jest format gLTF ze względu na rozmiar i wydajność. W tym ćwiczeniu z programowania masz już model do renderowania na scenie w /src/pin.gltf
.
- Utwórz instancję modelu wczytującego model.
Dołącz do użytkownikaonAdd
te informacje:loader = new GLTFLoader();
- Wczytaj model 3D.
Ładowarki modeli są asynchroniczne i wykonują wywołanie zwrotne po całkowitym wczytaniu modelu. Aby wczytaćpin.gltf
, dołącz doonAdd
ten element:const source = "pin.gltf"; loader.load( source, gltf => {} );
- Dodaj model do sceny.
Teraz możesz dodać model do sceny, dołączając do wywołania zwrotnegoloader
poniższy kod. Dodajemy kolumnęgltf.scene
, a niegltf
:scene.add(gltf.scene);
- Skonfiguruj macierz projekcji.
Ostatnią rzeczą, jakiej potrzebujesz do prawidłowego wyrenderowania modelu na mapie, jest ustawienie macierzy projekcji w scenie 3.js. Macierz projekcji jest określana jako tablica Three.jsMatrix4
, która definiuje punkt w przestrzeni trójwymiarowej razem z przekształceniami, takimi jak obrót, przecięcie, skala i inne.
W przypadkuWebGLOverlayView
tablica projekcji służy do renderowania mechanizmu renderowania, aby pokazać, gdzie i jak ma renderować scenę 3.js w odniesieniu do mapy bazowej. Wystąpił jednak problem. Lokalizacje na mapie są określane jako współrzędne szerokości i długości geograficznej, a w widoku Three.js – lokalizacjeVector3
. Jak można się domyślić, obliczenie konwersji między dwoma systemami nie jest proste. Aby rozwiązać ten problem,WebGLOverlayView
przekazuje obiektcoordinateTransformer
do webhooka cyklu życiaOnDraw
, który zawiera funkcję o nazwiefromLatLngAltitude
.fromLatLngAltitude
przyjmuje obiektLatLngAltitude
lubLatLngAltitudeLiteral
oraz opcjonalnie zbiór argumentów, które definiują przekształcenie sceny, a następnie obejmują je za matrycę wyświetlania modelu (MVP). Wystarczy, że określisz miejsce na mapie, w którym ma być renderowana scena Three.js, oraz sposób, w jaki ma ona zostać przekształcona, aWebGLOverlayView
zajmie się resztą. Potem możesz przekonwertować matrycę MVP na tablicęMatrix4
z danymi Three.js i ustawić na nią macierz projekcji.
W drugim kodzie drugi argument informuje widok nakładki Webgl o ustawieniu wysokości sceny 3.js nad 120 metrów nad ziemią. Dzięki temu model będzie się unosił.
Aby ustawić macierz wyświetlania obrazu z kameryonDraw
, zamieść ten fragment:const latLngAltitudeLiteral = { lat: mapOptions.center.lat, lng: mapOptions.center.lng, altitude: 120 } const matrix = transformer.fromLatLngAltitude(latLngAltitudeLiteral); camera.projectionMatrix = new THREE.Matrix4().fromArray(matrix);
- Przekształć model.
Zobaczysz, że pinezka nie znajduje się prostopadle do mapy. Oprócz grafiki świata w przestrzeni 3D z osobnymi osiami x, y i z, która określa orientację, każdy obiekt ma też własną przestrzeń obiektu z niezależnym osiem.
W tym modelu nie został on utworzony z zastosowaniem parametru „góra'”, czyli pinezki składającej się z górą osi Y, dlatego musisz przekształcić obiekt, aby ustawić go w odpowiedni sposób względem powierzchni świata, wywołując gorotation.set
. Pamiętaj, że w aplikacji Three.js obrót jest określany w radianach, a nie w stopniach. Ogólnie rzecz biorąc, łatwiej jest myśleć w stopniach, dlatego odpowiednią konwersję należy zrealizować za pomocą formułydegrees * Math.PI/180
.
Ponadto model jest trochę za mały, więc możesz ustawić jego skalowanie równomiernie na wszystkich osiach, wywołując właściwośćscale.set(x, y ,z)
.
Aby obrócić i skalować model, dodaj te elementy w wywołaniu zwrotnymloader
(onAdd
) przedscene.add(gltf.scene)
, które dodaje gLTF do sceny:gltf.scene.scale.set(25,25,25); gltf.scene.rotation.x = 180 * Math.PI/180;
Od tej pory pinezka stoi pionowo na mapie.
Haki onAdd
i onDraw
powinny wyglądać tak:
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();
}
Dalej mamy animacje animacji.
8. Animowanie kamery
Po wyrenderowaniu modelu na mapie i przeniesieniu wszystkiego w sposób trójwymiarowy możesz chcieć kontrolować swój ruch. Funkcja moveCamera
umożliwia jednoczesne ustawianie środka, powiększenia, pochylenia i właściwości nagłówka mapy, dzięki czemu masz większą kontrolę nad wrażeniami użytkowników. Dodatkowo można wywołać funkcję moveCamera
w pętli animacji, aby utworzyć płynne przejścia między klatkami o prawie 60 klatek na sekundę.
- Poczekaj na załadowanie modelu.
Aby zadbać o wygodę użytkowników, poczekaj, aż rozpocznie się przenoszenie kamery aż do załadowania modelu gLTF. Aby to zrobić, dołącz moduł obsługi zdarzeńonLoad
programu wczytującegoonContextRestored
haczyk:loader.manager.onLoad = () => {}
- Utwórz pętlę animacji.
Istnieje więcej niż 1 sposób tworzenia pętli animacji, np.setInterval
lubrequestAnimationFrame
. W takim przypadku użyjesz funkcjisetAnimationLoop
mechanizmu renderowania Three.js, która będzie automatycznie wywoływać kod zadeklarowany w wywołaniu zwrotnym za każdym razem, gdy wyrenderuje ona nową klatkę. Aby utworzyć pętlę animacji, w poprzednim kroku dodaj do modułu obsługi zdarzeńonLoad
ten element:renderer.setAnimationLoop(() => {});
- Ustaw pozycję kamery w pętli animacji.
Następnie zadzwoń do:moveCamera
, aby zaktualizować mapę. Do określenia pozycji kamery służą właściwości z obiektumapOptions
, które zostały użyte do załadowania mapy:map.moveCamera({ "tilt": mapOptions.tilt, "heading": mapOptions.heading, "zoom": mapOptions.zoom });
- Aparat aktualizuje każdą klatkę.
To już ostatni krok: Zaktualizuj obiektmapOptions
na końcu każdej klatki, aby w następnej klatce ustawić jej położenie. W tym kodzie instrukcjaif
zwiększa nachylenie do osiągnięcia maksymalnej wartości nachylenia, która wynosi 67,5, a następnie zmienia się nieco w każdej klatce, dopóki kamera nie przewróci w pełni obrotu o 360 stopni. Po zakończeniu wybranej animacjinull
jest przekazywana dosetAnimationLoop
w celu anulowania animacji, dzięki czemu nie będzie ona działać na zawsze.if (mapOptions.tilt < 67.5) { mapOptions.tilt += 0.5 } else if (mapOptions.heading <= 360) { mapOptions.heading += 0.2; } else { renderer.setAnimationLoop(null) }
Twoja reguła onContextRestored
powinna wyglądać tak:
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. Gratulacje
Jeśli wszystko poszło zgodnie z planem, powinna być mapa z dużą pinezką 3D, która wygląda tak:
Czego się nauczysz
Dzięki tym ćwiczeniom nauczysz się wielu rzeczy:
- Implementacja znaczników
WebGLOverlayView
i haków cyklu życia. - Integracja Three.js z mapą.
- Podstawy tworzenia sceny Three.js, w tym kamery i oświetlenie.
- Wczytywanie modeli 3D i manipulowanie nimi za pomocą Three.js.
- Sterowanie kamerą i animowanie jej na mapie za pomocą funkcji
moveCamera
.
Co dalej?
WebGL i grafika komputerowa to zagadnienie skomplikowane, dlatego zawsze warto się uczyć. Na początek zapoznaj się z tymi materiałami:
- Dokumentacja widoku nakładki WebGL
- Pierwsze kroki z WebGL.
- Dokumentacja dotycząca 3.js
- Pomóż nam w tworzeniu treści, które będą dla Ciebie najbardziej przydatne, odpowiadając na pytanie poniżej: «codelabs/maps-platform/shared/_next-lab-survey.lab.md» Czy ćwiczenia z programowania nie zostały wymienione powyżej? Tutaj możesz poprosić o nowy problem.