Menggunakan perender Ubin 3D

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

Bekerja dengan CesiumJS

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

Kontrol pengguna

Perender kartu CesiumJS memiliki serangkaian kontrol pengguna standar.

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

Praktik terbaik

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

  • Aktifkan permintaan serentak 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 lebih dari 10 dan cache dinonaktifkan, Anda mungkin mengalami masalah Chrome. Untuk sebagian besar kasus penggunaan, kami merekomendasikan REQUEST_COUNT sebesar 18 untuk performa yang optimal.

  • Mengaktifkan lewati tingkat detail. Untuk mengetahui informasi selengkapnya, lihat masalah Cesium ini.

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

Metrik rendering

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

Untuk melihat cara menghitung latensi frame, lihat class PerformanceDisplay.

Contoh perender CesiumJS

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

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 akan 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 melakukan animasi melalui rangkaian ubin. Jika digabungkan dengan Places API dan Elevation API, animasi ini akan menyimulasikan flyover drone interaktif di lokasi menarik mana pun.

Contoh kode ini mengarahkan Anda ke 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, atau menampilkan batas properti, atau untuk menghitung durasi mengemudi dan berjalan kaki. Anda juga bisa mendapatkan atribut tanpa benar-benar merender scene.

Anda dapat mengajak pengguna mengikuti tur pilihan di suatu lingkungan, atau menunjukkan properti tetangga yang saat ini dijual, lalu Anda dapat menambahkan objek 3D seperti baliho ke lokasi tersebut.

Anda dapat meringkas perjalanan, 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 mengorbit kamera di sekitar lokasi menarik untuk menghindari tabrakan dengan bangunan. Atau, Anda dapat membuat bangunan transparan saat kamera bergerak melaluinya.

Pertama, kunci kamera ke suatu titik, lalu Anda dapat membuat 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

Bekerja dengan 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. Hubungkan ke Google Photorealistic 3D Tiles API.

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

    2. Pilih Blank 3D Tiles Tiles.

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

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

    5. Tetapkan URL menjadi URL Ubin 3D Google.

    https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
    
    1. Aktifkan Tampilkan Kredit di Layar untuk menampilkan atribusi dengan benar.
  4. Ini akan memuat dunia. Untuk berpindah ke LatLng apa pun, pilih item CesiumGeoreference di panel Outliner, lalu edit Lintang/Bujur/Tinggi Asal di panel Details.

Bekerja dengan Cesium untuk Unity

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

  1. Buat 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. Hubungkan 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 pada bagian Source, pilih From URL (bukan From Cesium Ion).

    4. Tetapkan URL ke URL Ubin 3D Google.

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

Menggunakan deck.gl

deck.gl, yang didukung oleh WebGL, adalah framework JavaScript open source untuk visualisasi data berskala besar berperforma tinggi.

Atribusi

Pastikan Anda menampilkan atribusi data dengan benar dengan mengekstrak kolom copyright dari ubin gltf asset, lalu menampilkannya di tampilan yang dirender. Untuk informasi selengkapnya, lihat Menampilkan atribusi data.

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 Kartu 3D Fotorealistik Google

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

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>