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. 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 ubin CesiumJS memiliki kumpulan kontrol pengguna standar.

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

Praktik terbaik

Ada beberapa pendekatan yang dapat Anda lakukan untuk mengurangi waktu pemuatan 3D CesiumJS. 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 frekuensi panggilan metode requestAnimationFrame per detik.

Untuk melihat cara menghitung latensi frame, lihat class PerformanceDisplay.

Contoh perender CesiumJS

Anda dapat menggunakan perender CesiumJS dengan Kartu 3D Map Tiles API dengan hanya menyediakan URL set kartu 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 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 membuka area pandang Places. 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>

Memutar tampilan drone

Anda dapat mengontrol kamera untuk menganimasikan melalui tileset. Jika digabungkan dengan Places API dan Elevation API, animasi ini akan menyimulasikan flyover drone interaktif dari lokasi menarik apa 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 untuk menampilkan batas properti, atau untuk menghitung durasi berkendara dan berjalan kaki. Anda juga dapat mendapatkan atribut tanpa benar-benar merender tampilan.

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 mengorbitkan kamera di sekitar lokasi menarik, sehingga menghindari tabrakan dengan bangunan. Atau, Anda dapat membuat bangunan menjadi transparan saat kamera bergerak melaluinya.

Pertama, kunci kamera ke suatu titik, lalu Anda dapat membuat orbit kamera untuk menampilkan aset. 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 informasi selengkapnya tentang cara 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. Menghubungkan ke Google Photorealistic 3D Tiles API.

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

    2. Pilih Set Kartu Kartu Kartu 3D Kosong.

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

    4. Ubah Sumber dari Dari Cesium Ion menjadi Dari URL.

    5. Tetapkan URL sebagai URL Kartu 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. Tindakan ini akan memuat dunia. Untuk berpindah ke LatLng mana pun, pilih item CesiumGeoreference di panel Outliner, lalu edit Origin Latitude/Longitude/Height di panel Details.

Menggunakan Cesium untuk Unity

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

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

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

    2. Klik Set Kartu Kartu Kartu 3D Kosong.

    3. Di panel sebelah kiri, pada opsi Sumber Kartu di bagian Sumber, pilih Dari URL (bukan Dari Cesium Ion).

    4. Tetapkan URL ke URL Kartu 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. Tindakan 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 dan 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 mengetahui 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 Anda 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>

Memvisualisasikan lapisan 2D di atas Petak 3D Fotorealistik Google

TerrainExtension deck.gl merender data 2D ke platform 3D. Misalnya, Anda dapat menempatkan GeoJSON jejak bangunan di atas Geometri Kartu 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>