Membuat peta versi 3D dengan Tampilan Overlay WebGL

1. Sebelum memulai

Codelab ini mengajarkan cara menggunakan fitur dengan teknologi WebGL dari Maps JavaScript API untuk mengontrol dan merender pada peta vektor dalam tiga dimensi.

PIN 3D Final

Prasyarat

Codelab ini mengasumsikan bahwa Anda memiliki pengetahuan yang cukup terkait JavaScript dan Maps JavaScript API. Untuk mempelajari dasar-dasar penggunaan Maps JS API, coba Tambahkan peta ke codelab situs (JavaScript) Anda.

Yang akan Anda pelajari

  • Membuat ID Peta dengan peta vektor untuk JavaScript yang diaktifkan.
  • Mengontrol peta dengan kemiringan dan rotasi terprogram.
  • Merender objek 3D pada peta dengan WebGLOverlayView dan Three.js.
  • Menganimasikan gerakan kamera dengan moveCamera.

Yang akan Anda perlukan

  • Akun Google Cloud Platform dengan penagihan diaktifkan
  • Kunci API Google Maps Platform dengan Maps JavaScript API diaktifkan
  • Pengetahuan yang cukup tentang JavaScript, HTML, dan CSS
  • Editor teks atau IDE pilihan Anda
  • Node.js

2. Mulai persiapan

Untuk langkah pengaktifan di bawah, Anda harus mengaktifkan Maps JavaScript API.

Menyiapkan Google Maps Platform

Jika Anda belum memiliki akun Google Cloud Platform dan project dengan penagihan diaktifkan, lihat panduan Memulai Google Maps Platform untuk membuat akun penagihan dan project.

  1. Di Cloud Console, klik menu drop-down project, lalu pilih project yang ingin Anda gunakan untuk codelab ini.

  1. Aktifkan API dan SDK Google Maps Platform yang diperlukan untuk codelab ini di Google Cloud Marketplace. Untuk melakukannya, ikuti langkah-langkah dalam video ini atau dokumentasi ini.
  2. Buat kunci API di halaman Kredensial di Cloud Console. Anda dapat mengikuti langkah-langkah dalam video ini atau dokumentasi ini. Semua permintaan ke Google Maps Platform memerlukan kunci API.

Penyiapan Node.js

Jika Anda belum memilikinya, buka https://nodejs.org/ untuk mendownload dan menginstal runtime Node.js di komputer.

Node.js disertai dengan npm, sebuah pengelola paket yang Anda perlukan untuk menginstal dependensi untuk codelab ini.

Mendownload template permulaan project

Sebelum memulai codelab ini, lakukan hal berikut untuk mendownload template project permulaan, serta kode solusi lengkap:

  1. Download atau fork repositori GitHub untuk codelab ini di https://github.com/googlecodelabs/maps-platform-101-webgl/. Project permulaan berada di direktori /starter dan menyertakan struktur file dasar yang Anda butuhkan untuk menyelesaikan codelab. Semua yang Anda butuhkan ada di direktori /starter/src.
  2. Setelah mendownload project permulaan, jalankan npm install di direktori /starter. Tindakan ini akan menginstal semua dependensi yang diperlukan yang tercantum di package.json.
  3. Setelah dependensi diinstal, jalankan npm start di direktori.

Project permulaan telah disiapkan agar Anda dapat menggunakan webpack-dev-server, yang mengompilasi dan menjalankan kode yang Anda tulis secara lokal. webpack-dev-server juga otomatis memuat ulang aplikasi Anda di browser setiap kali Anda membuat perubahan kode.

Jika ingin melihat kode solusi lengkap berjalan, Anda dapat menyelesaikan langkah-langkah penyiapan di atas pada direktori /solution.

Menambahkan kunci API

Aplikasi awal menyertakan semua kode yang diperlukan untuk memuat peta dengan JS API Loader, sehingga Anda hanya perlu menyediakan kunci API dan ID Peta. JS API Loader adalah library sederhana yang memisahkan metode tradisional pemuatan Maps JS API secara inline di template HTML menggunakan tag script, sehingga Anda dapat menangani semuanya di kode JavaScript.

Untuk menambahkan kunci API, lakukan hal berikut pada project permulaan:

  1. Buka app.js.
  2. Di objek apiOptions, tetapkan kunci API Anda sebagai nilai apiOptions.apiKey.

3. Buat dan gunakan ID Peta

Untuk menggunakan fitur berbasis WebGL dari Maps JavaScript API, Anda memerlukan ID Peta dengan peta vektor diaktifkan.

Membuat ID Peta

Pembuatan ID Peta

  1. Di Google Cloud Console, buka 'Google Maps Platform' > 'Pengelolaan Peta'.
  2. Klik 'BUAT ID PETA BARU'.
  3. Di kolom 'Nama peta', masukkan nama untuk ID Peta Anda.
  4. Pada menu dropdown 'Jenis peta', pilih 'JavaScript'. 'Opsi JavaScript' akan muncul.
  5. Di bagian 'Opsi JavaScript', pilih tombol pilihan 'Vektor', kotak centang 'Kemiringan', dan kotak centang 'Rotasi'.
  6. Opsional. Di kolom 'Deskripsi', masukkan deskripsi untuk kunci API Anda.
  7. Klik tombol 'Berikutnya'. Halaman 'Detail ID Peta' akan muncul.

    Halaman Detail Peta
  8. Salin ID Peta. Anda akan menggunakan ID Peta ini pada langkah berikutnya untuk memuat Peta.

Menggunakan ID Peta

Untuk memuat peta vektor, Anda harus memasukkan ID Peta sebagai properti pada opsi saat membuat instance Peta. Secara opsional, Anda juga dapat memasukkan ID Peta yang sama saat memuat Maps JavaScript API.

Untuk memuat peta dengan ID Peta Anda, lakukan hal berikut:

  1. Tetapkan ID Peta Anda sebagai nilai mapOptions.mapId.

    Dengan memasukkan ID Peta saat membuat instance peta, Google Maps Platform akan mengetahui peta mana yang akan dimuat untuk instance tertentu. Anda dapat menggunakan kembali ID Peta yang sama di beberapa aplikasi atau tampilan dalam aplikasi yang sama.
    const mapOptions = {
      "tilt": 0,
      "heading": 0,
      "zoom": 18,
      "center": { lat: 35.6594945, lng: 139.6999859 },
      "mapId": "YOUR_MAP_ID"
    };
    

Periksa aplikasi yang berjalan di browser Anda. Peta vektor dengan kemiringan dan rotasi yang diaktifkan harus berhasil dimuat. Untuk memeriksa apakah kemiringan dan rotasi diaktifkan, tahan tombol shift, lalu tarik dengan mouse atau gunakan tombol panah pada keyboard.

Jika peta tidak dimuat, pastikan Anda telah memasukkan kunci API yang valid dalam apiOptions. Jika peta tidak miring dan berputar, pastikan Anda telah memasukkan ID Peta yang telah mengaktifkan kemiringan dan rotasi di apiOptions dan mapOptions.

Peta yang Dimiringkan

File app.js Anda sekarang akan terlihat seperti ini:

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

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

    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. Terapkan WebGLOverlayView

WebGLOverlayView memberi Anda akses langsung ke konteks untuk proses rendering WebGL yang sama dengan yang digunakan untuk merender peta dasar vektor. Dengan demikian, Anda dapat merender objek 2D dan 3D langsung di peta menggunakan WebGL, serta library grafik berbasis WebGL yang populer.

WebGLOverlayView mengekspos lima hook ke dalam siklus proses konteks untuk rendering WebGL dari peta yang dapat Anda gunakan. Berikut adalah deskripsi singkat tentang masing-masing hook dan kegunaannya:

  • onAdd(): Dipanggil saat overlay ditambahkan ke peta dengan memanggil setMap pada instance WebGLOverlayView. Di hook ini, Anda harus melakukan tindakan yang berkaitan dengan WebGL yang tidak memerlukan akses langsung ke konteks WebGL.
  • onContextRestored(): Dipanggil saat konteks WebGL tersedia, tetapi sebelum proses rendering berjalan. Di hook ini, Anda harus menginisialisasi objek, melakukan binding pada status, dan melakukan hal lain yang memerlukan akses ke konteks WebGL, tetapi dapat dilakukan di luar panggilan onDraw(). Tindakan ini memungkinkan Anda menyiapkan semua yang dibutuhkan tanpa menambahkan kelebihan overhead ke proses rendering peta yang sebenarnya, yang sudah menggunakan GPU secara intensif.
  • onDraw(): Dipanggil sekali per frame setelah WebGL mulai merender peta dan hal lain yang Anda minta. Anda harus melakukan tindakan seminimal mungkin di onDraw() agar tidak menyebabkan masalah performa dalam proses rendering peta.
  • onContextLost(): Dipanggil saat konteks untuk proses rendering WebGL hilang karena alasan apa pun.
  • onRemove(): Dipanggil saat overlay dihapus dari peta dengan memanggil setMap(null) pada instance WebGLOverlayView.

Pada langkah ini, Anda akan membuat instance WebGLOverlayView dan menerapkan tiga hook siklus prosesnya: onAdd, onContextRestored, dan onDraw. Agar teratur dan mudah diikuti, semua kode untuk overlay akan ditangani di fungsi initWebGLOverlayView() yang disediakan di template permulaan untuk codelab ini.

  1. Buat instance WebGLOverlayView().

    Overlay diberikan oleh Maps JS API di google.maps.WebGLOverlayView. Untuk memulai, buat instance dengan menambahkan kode berikut ke initWebGLOverlayView():
    const webGLOverlayView = new google.maps.WebGLOverlayView();
    
  2. Terapkan hook siklus proses.

    Untuk menerapkan hook siklus proses, tambahkan kode berikut ke initWebGLOverlayView():
    webGLOverlayView.onAdd = () => {};
    webGLOverlayView.onContextRestored = ({gl}) => {};
    webGLOverlayView.onDraw = ({gl, coordinateTransformer}) => {};
    
  3. Tambahkan instance overlay ke peta.

    Sekarang, panggil setMap() pada instance overlay dan teruskan peta dengan menambahkan kode berikut ke initWebGLOverlayView():
    webGLOverlayView.setMap(map)
    
  4. Panggil initWebGLOverlayView.

    Langkah terakhir adalah menjalankan initWebGLOverlayView() dengan menambahkan kode berikut ke fungsi yang langsung dipanggil di bagian bawah app.js:
    initWebGLOverlayView(map);
    

Fungsi yang langsung dipanggil dan initWebGLOverlayView sekarang akan terlihat seperti ini:

    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);
    })();

Hanya itu yang Anda butuhkan untuk menerapkan WebGLOverlayView. Selanjutnya, Anda akan menyiapkan semua yang diperlukan untuk merender objek 3D pada peta menggunakan Three.js.

5. Siapkan tampilan three.js

Menggunakan WebGL bisa jadi sangat rumit karena Anda harus mendefinisikan semua aspek dari setiap objek secara manual dan hal lainnya. Untuk mempermudah, Anda akan menggunakan Three.js, sebuah library grafik populer yang menyediakan lapisan abstraksi sederhana di atas WebGL, pada codelab ini. Three.js dilengkapi dengan berbagai fungsi praktis yang bisa digunakan untuk apa saja, mulai dari membuat perender WebGL, menggambar bentuk objek 2D dan 3D umum, hingga mengontrol kamera, transformasi objek, dan banyak lagi.

Ada tiga jenis objek dasar di Three.js yang diperlukan untuk menampilkan apa pun:

  • Tampilan: "Penampung" tempat semua objek, sumber cahaya, tekstur, dll. dirender dan ditampilkan.
  • Kamera: Kamera yang merepresentasikan sudut pandang tampilan. Terdapat beberapa jenis kamera, dan satu atau beberapa kamera dapat ditambahkan ke satu tampilan.
  • Perender: Perender yang menangani pemrosesan dan penampilan semua objek dalam tampilan. Di Three.js, WebGLRenderer paling sering digunakan, tetapi beberapa fungsi lain tersedia sebagai pengganti jika klien tidak mendukung WebGL.

Pada langkah ini, Anda akan memuat semua dependensi yang diperlukan untuk Three.js dan menyiapkan tampilan dasar.

  1. Muat Three.js

    Anda akan memerlukan dua dependensi untuk codelab ini: library Three.js dan GLTFLoader, class yang memungkinkan Anda memuat objek 3D di GL Transmission Format (glTF). Three.js menawarkan loader khusus untuk berbagai format objek 3D, tetapi sebaiknya gunakan glTF.

    Pada kode di bawah, seluruh library Three.js diimpor. Dalam aplikasi produksi, Anda mungkin hanya ingin mengimpor class yang dibutuhkan, tetapi untuk codelab ini, impor seluruh library guna memudahkan Anda. Perhatikan juga bahwa GLTFLoader tidak disertakan dalam library default, dan perlu diimpor dari jalur terpisah pada dependensi - jalur ini adalah tempat Anda dapat mengakses semua loader yang disediakan oleh Three.js.

    Untuk mengimpor Three.js dan GLTFLoader, tambahkan kode berikut ke bagian atas app.js:
    import * as THREE from 'three';
    import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js';
    
  2. Buat tampilan three.js.

    Untuk membuat tampilan, buat instance class Scene Three.js dengan menambahkan kode berikut ke hook onAdd:
    scene = new THREE.Scene();
    
  3. Tambahkan kamera ke tampilan.

    Seperti yang telah disebutkan, kamera merepresentasikan sudut pandang tampilan, dan menentukan cara Three.js menangani rendering visual objek dalam tampilan. Tanpa kamera, tampilan tidak "terlihat" secara efektif, yang berarti objek tidak akan muncul karena tidak akan dirender.

    Three.js menawarkan berbagai jenis kamera yang memengaruhi cara perender memperlakukan objek sehubungan dengan sudut pandang dan kedalamannya. Dalam tampilan ini, Anda akan menggunakan PerspectiveCamera, jenis kamera yang paling sering digunakan di Three.js, yang dirancang untuk meniru cara mata manusia melihat tampilan. Artinya, objek yang lebih jauh dari kamera akan tampak lebih kecil daripada objek yang lebih dekat, tampilan akan terlihat semakin kecil saat semakin jauh, dan lainnya.

    Untuk menambahkan kamera perspektif ke tampilan, tambahkan kode berikut ke hook onAdd:
    camera = new THREE.PerspectiveCamera();
    
    Dengan PerspectiveCamera, Anda juga dapat mengonfigurasi atribut yang membentuk sudut pandang, termasuk bidang dekat dan jauh, rasio lebar tinggi, dan ruang pandang (fov). Secara kolektif, atribut ini membentuk frustum sudut pandang, sebuah konsep penting untuk dipahami saat mengerjakan 3D, tetapi di luar cakupan codelab ini. Konfigurasi PerspectiveCamera default sudah cukup.
  4. Tambahkan sumber cahaya ke tampilan.

    Secara default, objek yang dirender dalam tampilan Three.js akan berwarna hitam, apa pun tekstur yang diterapkan pada objek tersebut. Warna hitam ini muncul karena tampilan Three.js meniru bagaimana objek terlihat di dunia nyata, yang visibilitas warnanya bergantung pada cahaya yang memantul dari objek. Singkatnya, tanpa cahaya, tidak akan ada warna.

    Three.js menyediakan berbagai jenis cahaya dan Anda akan menggunakan dua jenis cahaya berikut:

  5. AmbientLight: Memberikan sumber cahaya menyebar yang menerangi semua objek di tampilan secara merata dari semua sudut. Jenis cahaya ini akan memberikan cahaya dasar pada tampilan untuk memastikan tekstur pada semua objek terlihat.
  6. DirectionalLight: Memberikan cahaya yang berasal dari satu arah pada tampilan. Berbeda dari cara kerja cahaya yang diposisikan di dunia nyata, cahaya yang memancar dari DirectionalLight semuanya merambat lurus dan tidak menyebar maupun membaur saat menjauh dari sumber cahaya.

    Anda dapat mengonfigurasi warna dan intensitas setiap cahaya untuk membuat efek pencahayaan gabungan. Misalnya, pada kode di bawah ini, cahaya ruangan memancarkan cahaya putih lembut untuk seluruh tampilan, sedangkan cahaya terarah memancarkan cahaya sekunder yang mengenai objek dengan sudut menurun. Dalam kasus cahaya terarah, sudut ditetapkan menggunakan position.set(x, y ,z), yang masing-masing nilainya relatif terhadap sumbunya. Jadi, misalnya, position.set(0,1,0) akan memosisikan cahaya langsung di atas tampilan pada sumbu y yang mengarah lurus ke bawah.

    Untuk menambahkan sumber cahaya ke tampilan, tambahkan kode berikut ke hook onAdd:
    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);
    

Hook onAdd Anda sekarang akan terlihat seperti ini:

    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);
    }

Tampilan Anda kini sudah siap dan dapat dirender. Selanjutnya, Anda akan mengonfigurasi perender WebGL dan merender tampilan.

6. Render tampilan

Saatnya merender tampilan. Sejauh ini, semua yang telah Anda buat dengan Three.js diinisialisasi dalam kode, tetapi pada dasarnya tampilan tersebut tidak ada karena belum dirender ke dalam konteks untuk proses rendering WebGL. WebGL merender konten 2D dan 3D di browser menggunakan Canvas API. Jika sudah pernah menggunakan Canvas API sebelumnya, Anda mungkin terbiasa dengan context kanvas HTML, tempat untuk merender semua hal. Anda mungkin tidak mengetahui bahwa ini adalah antarmuka yang mengekspos konteks untuk proses rendering grafik OpenGL melalui API WebGLRenderingContext di browser.

Untuk mempermudah penggunaan perender WebGL, Three.js menyediakan WebGLRenderer, sebuah wrapper yang memudahkan konfigurasi konteks untuk proses rendering WebGL sehingga Three.js dapat merender tampilan di browser. Namun, untuk peta, merender tampilan Three.js di browser bersama peta saja tidak cukup. Three.js harus dirender ke dalam konteks untuk proses rendering yang sama persis dengan peta, sehingga peta dan objek apa pun dari tampilan Three.js dirender ke ruang dunia yang sama. Tindakan ini memungkinkan perender menangani interaksi antara objek pada peta dan objek dalam tampilan, seperti oklusi, yang merupakan cara keren dalam menyebut objek akan menyembunyikan objek di belakangnya agar tidak terlihat.

Tampaknya cukup rumit, bukan? Untungnya, Three.js siap membantu lagi.

  1. Siapkan perender WebGL.

    Saat membuat instance WebGLRenderer Three.js baru, Anda dapat memberikan konteks spesifik untuk proses rendering WebGL yang diinginkan sebagai tempat untuk merender tampilan Anda. Ingat argumen gl yang diteruskan ke hook onContextRestored? Objek gl tersebut adalah konteks untuk proses rendering WebGL dari peta. Anda hanya perlu menyediakan konteks, kanvasnya, dan atributnya ke instance WebGLRenderer. Semua hal tersebut tersedia melalui objek gl. Dalam kode ini, properti autoClear dari perender juga ditetapkan ke false sehingga perender tidak menghapus outputnya dari setiap frame.

    Untuk mengonfigurasi perender, tambahkan kode berikut ke hook onContextRestored:
    renderer = new THREE.WebGLRenderer({
      canvas: gl.canvas,
      context: gl,
      ...gl.getContextAttributes(),
    });
    renderer.autoClear = false;
    
  2. Render tampilan.

    Setelah perender dikonfigurasi, panggil requestRedraw pada instance WebGLOverlayView untuk menginformasikan overlay bahwa penggambaran ulang diperlukan saat frame berikutnya dirender, lalu panggil render pada perender dan teruskan kamera serta tampilan Three.js yang ingin dirender ke perender. Terakhir, hapus status konteks untuk proses rendering WebGL. Langkah ini penting dilakukan untuk menghindari konflik status GL, karena penggunaan Tampilan Overlay WebGL bergantung pada status GL yang dibagikan. Jika status tidak direset pada akhir setiap panggilan gambar, konflik status GL dapat menyebabkan perender gagal.

    Untuk melakukan ini, tambahkan kode berikut ke hook onDraw sehingga reset status ini dijalankan di setiap frame:
    webGLOverlayView.requestRedraw();
    renderer.render(scene, camera);
    renderer.resetState();
    

Hook onContextRestored dan onDraw Anda sekarang akan terlihat seperti ini:

    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. Render model 3D pada peta

Semua yang Anda butuhkan sudah siap. Anda telah menyiapkan Tampilan Overlay WebGL dan membuat tampilan Three.js, tetapi ada satu masalah: tidak ada apa pun di dalamnya. Jadi, langkah berikutnya adalah merender objek 3D dalam tampilan. Untuk melakukan hal ini, Anda akan menggunakan GLTFLoader yang Anda impor sebelumnya.

Model 3D tersedia dalam berbagai format, tetapi untuk Three.js, format gLTF lebih disarankan karena ukuran dan performa runtime-nya. Di codelab ini, model yang akan Anda render dalam tampilan sudah disediakan untuk Anda di /src/pin.gltf.

  1. Buat instance loader model.

    Tambahkan kode berikut ke onAdd:
    loader = new GLTFLoader();
    
  2. Muat model 3D.

    Loader model bersifat asinkron dan menjalankan callback setelah model dimuat sepenuhnya. Untuk memuat pin.gltf, tambahkan kode berikut ke onAdd:
    const source = "pin.gltf";
    loader.load(
      source,
      gltf => {}
    );
    
  3. Tambahkan model ke tampilan.

    Sekarang, Anda dapat menambahkan model ke tampilan dengan menambahkan kode berikut ke callback loader. Perhatikan bahwa yang ditambahkan adalah gltf.scene, bukan gltf:
    scene.add(gltf.scene);
    
  4. Konfigurasi matriks proyeksi kamera.

    Langkah terakhir yang perlu Anda lakukan agar model dirender dengan benar pada peta adalah menyetel matriks proyeksi kamera di tampilan Three.js. Matriks proyeksi ditetapkan sebagai array Matrix4 Three.js, yang menentukan titik dalam ruang tiga dimensi beserta transformasinya, seperti rotasi, pergeseran, skala, dan lainnya.

    Dalam kasus WebGLOverlayView, matriks proyeksi digunakan untuk menginformasikan perender tempat dan cara merender tampilan Three.js secara relatif terhadap peta dasar. Namun, ada satu masalah. Lokasi pada peta ditetapkan sebagai pasangan koordinat lintang dan bujur, sedangkan lokasi pada tampilan Three.js merupakan koordinat Vector3. Seperti yang mungkin sudah Anda duga, menghitung konversi antara kedua sistem tersebut bukanlah hal yang mudah. Untuk mengatasi perbedaan ini, WebGLOverlayView meneruskan objek coordinateTransformer ke hook siklus proses OnDraw yang berisi fungsi bernama fromLatLngAltitude. Fungsi fromLatLngAltitude mengambil objek LatLngAltitude atau LatLngAltitudeLiteral, dan secara opsional sekumpulan argumen yang menentukan transformasi untuk tampilan, lalu mengubahnya ke matriks proyeksi tampilan model (MVP) untuk Anda. Anda hanya perlu menentukan lokasi pada peta tempat Anda ingin tampilan Three.js dirender, serta transformasi yang diinginkan untuk tampilan, dan WebGLOverlayView akan melakukan sisanya. Selanjutnya, Anda dapat mengubah matriks MVP ke array Matrix4 Three.js dan menetapkan matriks proyeksi kamera ke array tersebut.

    Pada kode di bawah, argumen kedua menginformasikan Tampilan Overlay WebGL untuk menetapkan ketinggian tampilan Three.js ke 120 meter di atas permukaan tanah, sehingga model akan tampak mengambang.

    Untuk menetapkan matriks proyeksi kamera, tambahkan kode berikut ke hook onDraw:
    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. Lakukan transformasi pada model.

    Anda akan melihat pin yang tidak tegak lurus terhadap peta. Dalam grafik 3D, selain ruang dunia yang memiliki sumbu x, y, dan z sendiri untuk menentukan orientasi, setiap objek juga memiliki ruang objeknya sendiri dengan pasangan sumbu independen.

    Dalam kasus model ini, model tidak dibuat dengan yang biasa kita anggap sebagai 'bagian atas' pin yang menghadap ke sumbu y, sehingga Anda harus mengubah objek untuk mengorientasikannya sesuai yang diinginkan yang relatif terhadap ruang dunia dengan memanggil rotation.set pada objek tersebut. Perhatikan bahwa di Three.js, rotasi ditetapkan dalam radian, bukan derajat. Umumnya, satuan derajat lebih mudah untuk dibayangkan, sehingga konversi yang sesuai harus dibuat menggunakan formula degrees * Math.PI/180.

    Selain itu, model ini berukuran sedikit kecil, sehingga Anda juga akan menskalakannya secara merata di semua sumbu dengan memanggil scale.set(x, y ,z).

    Untuk memutar dan menskalakan model, tambahkan kode berikut dalam callback loader dari onAdd sebelum scene.add(gltf.scene) yang menambahkan glTF ke tampilan:
    gltf.scene.scale.set(25,25,25);
    gltf.scene.rotation.x = 180 * Math.PI/180;
    

Sekarang pin diletakkan tegak lurus relatif terhadap peta.

Pin Tegak Lurus

Hook onAdd dan onDraw Anda sekarang akan terlihat seperti ini:

    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();
    }

Selanjutnya adalah animasi kamera.

8. Animasikan kamera

Setelah Anda merender model pada peta dan dapat memindahkan semua objek dalam tiga dimensi, hal berikutnya yang harus Anda lakukan adalah mengontrol gerakan tersebut secara terprogram. Fungsi moveCamera memungkinkan Anda menetapkan pusat, zoom, kemiringan, dan arah properti pada peta secara bersamaan, sehingga memberi Anda kontrol yang lebih mendetail atas pengalaman pengguna. Selain itu, moveCamera dapat dipanggil dalam loop animasi untuk membuat transisi yang halus antar-frame pada kecepatan frame mendekati 60 frame per detik.

  1. Tunggu hingga model dimuat.

    Agar dapat menciptakan pengalaman pengguna yang lancar, Anda harus menunggu untuk mulai menggerakkan kamera hingga model glTF dimuat. Untuk melakukan ini, tambahkan pengendali peristiwa onLoad loader ke hook onContextRestored:
    loader.manager.onLoad = () => {}
    
  2. Buat loop animasi.

    Ada lebih dari satu cara untuk membuat loop animasi, seperti menggunakan setInterval atau requestAnimationFrame. Dalam hal ini, Anda akan menggunakan fungsi setAnimationLoop dari perender Three.js, yang akan otomatis memanggil kode yang Anda deklarasikan dalam callback-nya setiap kali Three.js merender frame baru. Untuk membuat loop animasi, tambahkan kode berikut ke pengendali peristiwa onLoad di langkah sebelumnya:
    renderer.setAnimationLoop(() => {});
    
  3. Tetapkan posisi kamera di loop animasi.

    Selanjutnya, panggil moveCamera untuk memperbarui peta. Di sini, properti dari objek mapOptions yang dipakai untuk memuat peta digunakan untuk menentukan posisi kamera:
    map.moveCamera({
      "tilt": mapOptions.tilt,
      "heading": mapOptions.heading,
      "zoom": mapOptions.zoom
    });
    
  4. Perbarui kamera untuk setiap frame.

    Langkah terakhir. Perbarui objek mapOptions di bagian akhir setiap frame guna menetapkan posisi kamera untuk frame berikutnya. Dalam kode ini, pernyataan if digunakan untuk meningkatkan kemiringan hingga mencapai nilai kemiringan maksimum sebesar 67,5 derajat, lalu arah pada setiap frame berubah sedikit sampai kamera menyelesaikan rotasi penuh 360 derajat. Setelah animasi yang diinginkan selesai, null diteruskan ke setAnimationLoop untuk membatalkan animasi agar tidak berjalan terus-menerus.
    if (mapOptions.tilt < 67.5) {
      mapOptions.tilt += 0.5
    } else if (mapOptions.heading <= 360) {
      mapOptions.heading += 0.2;
    } else {
      renderer.setAnimationLoop(null)
    }
    

Hook onContextRestored Anda sekarang akan terlihat seperti ini:

    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. Selamat

Jika semuanya berjalan sesuai rencana, Anda akan melihat peta dengan pin 3D besar yang terlihat seperti ini:

PIN 3D Final

Yang telah Anda pelajari

Dalam codelab ini, Anda telah mempelajari banyak hal; berikut adalah sorotannya:

  • Menerapkan WebGLOverlayView dan hook siklus prosesnya.
  • Mengintegrasikan Three.js ke peta.
  • Dasar-dasar pembuatan tampilan Three.js, termasuk kamera dan pencahayaan.
  • Memuat dan memanipulasi model 3D menggunakan Three.js.
  • Mengontrol dan menganimasikan kamera untuk peta menggunakan moveCamera.

Apa selanjutnya?

WebGL, dan grafik komputer secara umum, adalah topik yang kompleks, jadi selalu ada banyak hal yang perlu dipelajari. Berikut ini beberapa referensi untuk membantu Anda memulai: