Menggunakan perender Ubin 3D

Kartu 3D Fotorealistik berada dalam format glTF standar OGC, yang berarti Anda dapat menggunakan perender yang mendukung spesifikasi Kartu 3D OGC untuk membuat visualisasi 3D. Misalnya, Cesium adalah library open source dasar untuk merender visualisasi 3D.

Menggunakan CesiumJS

CesiumJS adalah library JavaScript open source untuk visualisasi 3D di web. Untuk informasi selengkapnya tentang penggunaan CesiumJS, lihat Mempelajari CesiumJS.

Kontrol pengguna

Perender kartu CesiumJS memiliki sekumpulan standar kontrol pengguna.

Tindakan Deskripsi
Tampilan geser Klik & tarik kiri
Tampilan zoom Klik & tarik kanan, atau scroll roda mouse
Putar tampilan Ctrl + klik & tarik kiri/kanan, atau klik & tarik tengah

Praktik terbaik

Ada beberapa pendekatan yang dapat Anda lakukan untuk mengurangi waktu pemuatan 3D CesiumJS. Contoh:

  • Aktifkan permintaan simultan dengan menambahkan pernyataan berikut ke HTML rendering Anda:

    Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = <REQUEST_COUNT>
    

    Makin tinggi REQUEST_COUNT, makin cepat kartu dimuat. Namun, saat memuat di browser Chrome dengan REQUEST_COUNT yang lebih besar dari 10 dan cache dinonaktifkan, Anda mungkin akan mengalami masalah Chrome umum. Untuk sebagian besar kasus penggunaan, sebaiknya gunakan REQUEST_COUNT dengan nilai 18 untuk performa yang optimal.

  • Aktifkan melewatkan tingkat detail. Untuk mengetahui informasi selengkapnya, lihat masalah Cesium ini.

Pastikan Anda menampilkan atribusi data secara tepat dengan mengaktifkan showCreditsOnScreen: true. Untuk informasi selengkapnya, lihat Kebijakan.

Metrik rendering

Untuk menemukan kecepatan frame, lihat berapa kali per detik metode requestAnimationFrame dipanggil.

Untuk mengetahui cara menghitung latensi frame, lihat class PerformanceDisplay.

Contoh perender CesiumJS

Anda dapat menggunakan perender CesiumJS dengan Kartu 3D Map Tiles API hanya dengan menyediakan URL root tileset.

Contoh sederhana

Contoh berikut menginisialisasi perender CesiumJS, lalu memuat kartu root.

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <title>CesiumJS 3D Tiles Simple Demo</title>
  <script src="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Cesium.js"></script>
  <link href="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
</head>
<body>
  <div id="cesiumContainer"></div>
  <script>

    // Enable simultaneous requests.
    Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = 18;

    // Create the viewer.
    const viewer = new Cesium.Viewer('cesiumContainer', {
      imageryProvider: false,
      baseLayerPicker: false,
      geocoder: false,
      globe: false,
      // https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/#enabling-request-render-mode
      requestRenderMode: true,
    });

    // Add 3D Tiles tileset.
    const tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
      url: "https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY",
      // This property is needed to appropriately display attributions
      // as required.
      showCreditsOnScreen: true,
    }));
  </script>
</body>

Untuk mengetahui informasi tentang requestRenderMode, lihat Mengaktifkan mode render permintaan.

Halaman HTML dirender seperti yang ditunjukkan di sini.

Integrasi Places API

Anda dapat menggunakan CesiumJS dengan Places API untuk mengambil informasi selengkapnya. Anda dapat menggunakan widget Autocomplete untuk terbang ke area pandang Tempat. Contoh ini menggunakan Places Autocomplete API, yang diaktifkan dengan mengikuti petunjuk ini, dan Maps JavaScript API, yang diaktifkan dengan mengikuti petunjuk ini.

<!DOCTYPE html>
<head>
 <meta charset="utf-8" />
 <title>CesiumJS 3D Tiles Places API Integration Demo</title>
 <script src="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Cesium.js"></script>
 <link href="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
</head>
<body>
 <label for="pacViewPlace">Go to a place: </label>
 <input
   type="text"
   id="pacViewPlace"
   name="pacViewPlace"
   placeholder="Enter a location..."
   style="width: 300px"
 />
 <div id="cesiumContainer"></div>
 <script>
   // Enable simultaneous requests.
   Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = 18;

   // Create the viewer.
   const viewer = new Cesium.Viewer("cesiumContainer", {
     imageryProvider: false,
     baseLayerPicker: false,
     requestRenderMode: true,
     geocoder: false,
     globe: false,
   });

   // Add 3D Tiles tileset.
   const tileset = viewer.scene.primitives.add(
     new Cesium.Cesium3DTileset({
       url: "https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY",
       // This property is required to display attributions as required.
       showCreditsOnScreen: true,
     })
   );

   const zoomToViewport = (viewport) => {
     viewer.entities.add({
       polyline: {
         positions: Cesium.Cartesian3.fromDegreesArray([
           viewport.getNorthEast().lng(), viewport.getNorthEast().lat(),
           viewport.getSouthWest().lng(), viewport.getNorthEast().lat(),
           viewport.getSouthWest().lng(), viewport.getSouthWest().lat(),
           viewport.getNorthEast().lng(), viewport.getSouthWest().lat(),
           viewport.getNorthEast().lng(), viewport.getNorthEast().lat(),
         ]),
         width: 10,
         clampToGround: true,
         material: Cesium.Color.RED,
       },
     });
     viewer.flyTo(viewer.entities);
   };

   function initAutocomplete() {
     const autocomplete = new google.maps.places.Autocomplete(
       document.getElementById("pacViewPlace"),
       {
         fields: [
           "geometry",
           "name",
         ],
       }
     );
     autocomplete.addListener("place_changed", () => {
       viewer.entities.removeAll();
       const place = autocomplete.getPlace();
       if (!place.geometry || !place.geometry.viewport) {
         window.alert("No viewport for input: " + place.name);
         return;
       }
       zoomToViewport(place.geometry.viewport);
     });
   }
 </script>
 <script
   async=""
   src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initAutocomplete"
 ></script>
</body>

Tampilan drone berputar

Anda dapat mengontrol kamera untuk dianimasikan melalui tileset. Jika dikombinasikan dengan Places API dan Elevation API, animasi ini akan menyimulasikan flyover drone interaktif dari lokasi menarik mana pun.

Anda akan diarahkan dengan contoh kode ini di sekitar tempat yang Anda pilih di widget Autocomplete.

<!DOCTYPE html>
<head>
  <meta charset="utf-8" />
  <title>CesiumJS 3D Tiles Rotating Drone View Demo</title>
  <script src="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Cesium.js"></script>
  <link href="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
</head>
<body>
  <label for="pacViewPlace">Go to a place: </label>
  <input type="text" id="pacViewPlace" name="pacViewPlace" placeholder="Enter a location..." style="width: 300px" />
  <div id="cesiumContainer"></div>
  <script>
    // Enable simultaneous requests.
    Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = 18;

    // Create the viewer and remove unneeded options.
    const viewer = new Cesium.Viewer("cesiumContainer", {
      imageryProvider: false,
      baseLayerPicker: false,
      homeButton: false,
      fullscreenButton: false,
      navigationHelpButton: false,
      vrButton: false,
      sceneModePicker: false,
      geocoder: false,
      globe: false,
      infobox: false,
      selectionIndicator: false,
      timeline: false,
      projectionPicker: false,
      clockViewModel: null,
      animation: false,
      requestRenderMode: true,
    });

    // Add 3D Tile set.
    const tileset = viewer.scene.primitives.add(
      new Cesium.Cesium3DTileset({
        url: "https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY",
        // This property is required to display attributions.
        showCreditsOnScreen: true,
      })
    );

    // Point the camera at a location and elevation, at a viewport-appropriate distance.
    function pointCameraAt(location, viewport, elevation) {
      const distance = Cesium.Cartesian3.distance(
        Cesium.Cartesian3.fromDegrees(
          viewport.getSouthWest().lng(), viewport.getSouthWest().lat(), elevation),
        Cesium.Cartesian3.fromDegrees(
          viewport.getNorthEast().lng(), viewport.getNorthEast().lat(), elevation)
      ) / 2;
      const target = new Cesium.Cartesian3.fromDegrees(location.lng(), location.lat(), elevation);
      const pitch = -Math.PI / 4;
      const heading = 0;
      viewer.camera.lookAt(target, new Cesium.HeadingPitchRange(heading, pitch, distance));
    }

    // Rotate the camera around a location and elevation, at a viewport-appropriate distance.
    let unsubscribe = null;
    function rotateCameraAround(location, viewport, elevation) {
      if(unsubscribe) unsubscribe();
      pointCameraAt(location, viewport, elevation);
      unsubscribe = viewer.clock.onTick.addEventListener(() => {
        viewer.camera.rotate(Cesium.Cartesian3.UNIT_Z);
      });
    }

    function initAutocomplete() {
      const autocomplete = new google.maps.places.Autocomplete(
        document.getElementById("pacViewPlace"), {
          fields: [
            "geometry",
            "name",
          ],
        }
      );
      
      autocomplete.addListener("place_changed", async () => {
        const place = autocomplete.getPlace();
        
        if (!(place.geometry && place.geometry.viewport && place.geometry.location)) {
          window.alert(`Insufficient geometry data for place: ${place.name}`);
          return;
        }
        // Get place elevation using the ElevationService.
        const elevatorService = new google.maps.ElevationService();
        const elevationResponse =  await elevatorService.getElevationForLocations({
          locations: [place.geometry.location],
        });

        if(!(elevationResponse.results && elevationResponse.results.length)){
          window.alert(`Insufficient elevation data for place: ${place.name}`);
          return;
        }
        const elevation = elevationResponse.results[0].elevation || 10;

        rotateCameraAround(
          place.geometry.location,
          place.geometry.viewport,
          elevation
        );
      });
    }
  </script>
  <script async src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initAutocomplete"></script>
</body>

Menggambar polyline dan label

Contoh kode ini menunjukkan cara menambahkan polyline dan label ke peta. Anda dapat menambahkan polyline ke peta untuk menampilkan rute mobil dan jalan kaki, menampilkan batas properti, atau menghitung durasi mengemudi dan berjalan kaki. Anda juga bisa mendapatkan atribut tanpa benar-benar merender scene.

Anda dapat mengajak pengguna mengikuti tur pilihan ke suatu kawasan, atau menampilkan properti di sekitar yang saat ini sedang promo, lalu Anda dapat menambahkan objek 3D seperti baliho ke adegan.

Anda dapat meringkas perjalanan, dengan mencantumkan properti yang Anda lihat, dan menampilkan detail ini dalam objek virtual.

<!DOCTYPE html>
<head>
  <meta charset="utf-8" />
  <title>CesiumJS 3D Tiles Polyline and Label Demo</title>
  <script src="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Cesium.js"></script>
  <link 
    href="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Widgets/widgets.css"
    rel="stylesheet"
  />
</head>
<body>
  <div id="cesiumContainer"></div>
  <script>
    // Enable simultaneous requests.
    Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = 18;

    // Create the viewer.
    const viewer = new Cesium.Viewer("cesiumContainer", {
      imageryProvider: false,
      baseLayerPicker: false,
      requestRenderMode: true,
      geocoder: false,
      globe: false,
    });

    // Add 3D Tiles tileset.
    const tileset = viewer.scene.primitives.add(
      new Cesium.Cesium3DTileset({
        url: "https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY",

        // This property is required to display attributions as required.
        showCreditsOnScreen: true,
      })
    );

    // Draws a circle at the position, and a line from the previous position.
    const drawPointAndLine = (position, prevPosition) => {
      viewer.entities.removeAll();
      if (prevPosition) {
        viewer.entities.add({
          polyline: {
            positions: [prevPosition, position],
            width: 3,
            material: Cesium.Color.WHITE,
            clampToGround: true,
            classificationType: Cesium.ClassificationType.CESIUM_3D_TILE,
          },
        });
      }
      viewer.entities.add({
        position: position,
        ellipsoid: {
          radii: new Cesium.Cartesian3(1, 1, 1),
          material: Cesium.Color.RED,
        },
      });
    };

    // Compute, draw, and display the position's height relative to the previous position.
    var prevPosition;
    const processHeights = (newPosition) => {
      drawPointAndLine(newPosition, prevPosition);

      const newHeight = Cesium.Cartographic.fromCartesian(newPosition).height;
      let labelText = "Current altitude (meters above sea level):\n\t" + newHeight;
      if (prevPosition) {
        const prevHeight =
          Cesium.Cartographic.fromCartesian(prevPosition).height;
        labelText += "\nHeight from previous point (meters):\n\t" + Math.abs(newHeight - prevHeight);
      }
      viewer.entities.add({
        position: newPosition,
        label: {
          text: labelText,
          disableDepthTestDistance: Number.POSITIVE_INFINITY,
          pixelOffset: new Cesium.Cartesian2(0, -10),
          showBackground: true,
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        }
      });

      prevPosition = newPosition;
    };

    const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
    handler.setInputAction(function (event) {
      const earthPosition = viewer.scene.pickPosition(event.position);
      if (Cesium.defined(earthPosition)) {
        processHeights(earthPosition);
      }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  </script>
</body>

Orbit kamera

Di Cesium, Anda dapat mengorbitkan kamera di sekitar lokasi menarik untuk menghindari tumbukan dengan bangunan. Atau, Anda dapat membuat bangunan menjadi transparan saat kamera bergerak melewatinya.

Pertama, kunci kamera ke titik tertentu, lalu buat orbit kamera untuk menampilkan aset Anda. Anda dapat melakukannya dengan menggunakan fungsi lookAtTransform kamera dengan pemroses peristiwa, seperti yang ditunjukkan dalam contoh kode ini.

// Lock the camera onto a point.
const center = Cesium.Cartesian3.fromRadians(
  2.4213211833389243,
  0.6171926869414084,
  3626.0426275055174
);

const transform = Cesium.Transforms.eastNorthUpToFixedFrame(center);

viewer.scene.camera.lookAtTransform(
  transform,
  new Cesium.HeadingPitchRange(0, -Math.PI / 8, 2900)
);

// Orbit around this point.
viewer.clock.onTick.addEventListener(function (clock) {
  viewer.scene.camera.rotateRight(0.005);
});

Untuk mengetahui informasi selengkapnya tentang mengontrol kamera, lihat Mengontrol kamera

Menggunakan Cesium untuk Unreal

Untuk menggunakan Plugin Cesium for Unreal dengan 3D Tiles API, ikuti langkah-langkah di bawah ini.

  1. Instal plugin Cesium for Unreal.

  2. Buat project Unreal baru.

  3. Menghubungkan ke Google Photorealistic 3D Tiles API.

    1. Buka jendela Cesium dengan memilih Cesium > Cesium dari menu.

    2. Pilih Blank 3D Tiles Tileset.

    3. Di World Outliner, buka panel Details dengan memilih Cesium3DTileset ini.

    4. Ubah Source dari From Cesium Ion ke From URL.

    5. Setel URL menjadi URL Ubin Google 3D.

    https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
    
    1. Aktifkan Tampilkan Kredit di Layar untuk menampilkan atribusi dengan benar.
  4. Hal ini akan memuat dunia. Untuk berpindah ke LatLng, pilih item CesiumGeoreference di panel Outliner, lalu edit Origin Latitude/Longitude/Height pada panel Details.

Menggunakan Cesium untuk Unity

Untuk menggunakan ubin fotorealistik dengan Cesium untuk Unity, ikuti langkah-langkah di bawah ini.

  1. Membuat project Unity baru.

  2. Tambahkan Scoped Registry baru di bagian Package Manager (melalui Editor > Project Settings).

    • Nama: Cesium

    • URL: https://unity.pkg.cesium.com

    • Cakupan: com.cesium.unity

  3. Instal paket Cesium untuk Unity.

  4. Menghubungkan ke Google Photorealistic 3D Tiles API.

    1. Buka jendela Cesium dengan memilih Cesium > Cesium dari menu.

    2. Klik Blank 3D Tiles Tiles.

    3. Di panel sebelah kiri, pada opsi Tileset Source di bagian Source, pilih From URL (bukan From Cesium Ion).

    4. Setel URL ke URL Ubin Google 3D.

    https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
    
    1. Aktifkan Tampilkan Kredit di Layar untuk menampilkan atribusi dengan benar.
  5. Hal ini akan memuat dunia. Untuk berpindah ke LatLng, pilih item CesiumGeoreference di Scene Hierarchy, lalu edit Origin Latitude/Longitude/Height di Inspector.

Menggunakan deck.gl

deck.gl, dengan teknologi WebGL, adalah framework JavaScript open source untuk visualisasi data berskala besar dan berperforma tinggi.

Atribusi

Pastikan Anda menampilkan atribusi data dengan benar dengan mengekstrak kolom copyright dari asset tile gltf, lalu menampilkannya pada tampilan yang dirender. Untuk mengetahui informasi selengkapnya, lihat Atribusi data tampilan.

Contoh perender deck.gl

Contoh sederhana

Contoh berikut menginisialisasi perender deck.gl, lalu memuat tempat dalam 3D. Dalam kode Anda, pastikan untuk mengganti YOUR_API_KEY dengan kunci API yang sebenarnya.

<!DOCTYPE html>
<html>
 <head>
   <title>deck.gl Photorealistic 3D Tiles example</title>
   <script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
   <style>
     body { margin: 0; padding: 0;}
     #map { position: absolute; top: 0;bottom: 0;width: 100%;}
     #credits { position: absolute; bottom: 0; right: 0; padding: 2px; font-size: 15px; color: white;
        text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;}
   </style>
 </head>

 <body>
   <div id="map"></div>
   <div id="credits"></div>
   <script>
     const GOOGLE_API_KEY = YOUR_API_KEY;
     const TILESET_URL = `https://tile.googleapis.com/v1/3dtiles/root.json`;
     const creditsElement = document.getElementById('credits');
     new deck.DeckGL({
       container: 'map',
       initialViewState: {
         latitude: 50.0890,
         longitude: 14.4196,
         zoom: 16,
         bearing: 90,
         pitch: 60,
         height: 200
       },
       controller: {minZoom: 8},
       layers: [
         new deck.Tile3DLayer({
           id: 'google-3d-tiles',
           data: TILESET_URL,
           loadOptions: {
            fetch: {
              headers: {
                'X-GOOG-API-KEY': GOOGLE_API_KEY
              }
            }
          },
           onTilesetLoad: tileset3d => {
             tileset3d.options.onTraversalComplete = selectedTiles => {
               const credits = new Set();
               selectedTiles.forEach(tile => {
                 const {copyright} = tile.content.gltf.asset;
                 copyright.split(';').forEach(credits.add, credits);
                 creditsElement.innerHTML = [...credits].join('; ');
               });
               return selectedTiles;
             }
           }
         })
       ]
     });
   </script>
 </body>
</html>

Visualisasikan lapisan 2D di atas Ubin 3D Fotorealistik Google

deck.gl TerrainExtension, merender data 2D ke platform 3D. Misalnya, Anda dapat menggantungkan GeoJSON jejak bangunan di atas Geometri Ubin Fotorealistik 3D.

Pada contoh berikut, lapisan bangunan divisualisasikan dengan poligon yang disesuaikan dengan permukaan Kartu 3D Fotorealistik.

<!DOCTYPE html>
<html>
 <head>
   <title>Google 3D tiles example</title>
   <script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
   <style>
     body { margin: 0; padding: 0;}
     #map { position: absolute; top: 0;bottom: 0;width: 100%;}
     #credits { position: absolute; bottom: 0; right: 0; padding: 2px; font-size: 15px; color: white;
        text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;}
   </style>
 </head>

 <body>
   <div id="map"></div>
   <div id="credits"></div>
   <script>
     const GOOGLE_API_KEY = YOUR_API_KEY;
     const TILESET_URL = `https://tile.googleapis.com/v1/3dtiles/root.json`;
     const BUILDINGS_URL = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/google-3d-tiles/buildings.geojson'
     const creditsElement = document.getElementById('credits');
     const deckgl = new deck.DeckGL({
       container: 'map',
       initialViewState: {
         latitude: 50.0890,
         longitude: 14.4196,
         zoom: 16,
         bearing: 90,
         pitch: 60,
         height: 200
       },
       controller: true,
       layers: [
         new deck.Tile3DLayer({
           id: 'google-3d-tiles',
           data: TILESET_URL,
           loadOptions: {
            fetch: {
              headers: {
                'X-GOOG-API-KEY': GOOGLE_API_KEY
              }
            }
          },
          onTilesetLoad: tileset3d => {
             tileset3d.options.onTraversalComplete = selectedTiles => {
               const credits = new Set();
               selectedTiles.forEach(tile => {
                 const {copyright} = tile.content.gltf.asset;
                 copyright.split(';').forEach(credits.add, credits);
                 creditsElement.innerHTML = [...credits].join('; ');
               });
               return selectedTiles;
             }
           },
           operation: 'terrain+draw'
         }),
         new deck.GeoJsonLayer({
           id: 'buildings',
           // This dataset is created by CARTO, using other Open Datasets available. More info at: https://3dtiles.carto.com/#about.
           data: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/google-3d-tiles/buildings.geojson',
           stroked: false,
           filled: true,
           getFillColor: ({properties}) => {
             const {tpp} = properties;
             // quantiles break
             if (tpp < 0.6249)
               return [254, 246, 181]
             else if (tpp < 0.6780)
               return [255, 194, 133]
             else if (tpp < 0.8594)
               return [250, 138, 118]
             return [225, 83, 131]
           },
           opacity: 0.2,
           extensions: [new deck._TerrainExtension()]
         })
       ]
     });
   </script>
 </body>
</html>