Menggunakan perender Ubin 3D

Ubin 3D Fotorealistik berada dalam Format glTF standar OGC, artinya Anda dapat menggunakan perender apa pun yang mendukung spesifikasi Ubin 3D OGC untuk dibangun visualisasi 3D. Misalnya, Cesium adalah perpustakaan {i>open source<i} dasar untuk merender visualisasi 3D.

Bekerja dengan CesiumJS

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

Kontrol pengguna

Perender kartu CesiumJS memiliki serangkaian kontrol pengguna standar.

Tindakan Deskripsi
Geser tampilan Klik kiri & tarik
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 pemuatan CesiumJS 3D kali. Contoh:

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

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

    Semakin tinggi REQUEST_COUNT, semakin cepat ubin akan dimuat. Namun, saat memuat di browser Chrome dengan REQUEST_COUNT lebih besar dari 10 dan cache dinonaktifkan, Anda mungkin menemui Masalah Chrome. Untuk sebagian besar kasus penggunaan, sebaiknya gunakan REQUEST_COUNT sebesar 18 untuk mengoptimalkan tingkat tinggi.

  • Mengaktifkan lewati tingkat detail. Untuk informasi selengkapnya, lihat Masalah Cesium.

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

Metrik rendering

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

Untuk melihat cara menghitung latensi frame, lihat PerformanceDisplay .

Contoh perender CesiumJS

Anda dapat menggunakan perender CesiumJS dengan 3D Tiles API Map Tiles API yang menyediakan URL rangkaian petak root.

Contoh sederhana

Contoh berikut menginisialisasi perender CesiumJS, lalu memuat root kumpulan ubin.

<!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 akan dirender seperti yang ditunjukkan di sini.

Integrasi Places API

Anda dapat menggunakan CesiumJS dengan Places API untuk mengambil informasi lebih lanjut. Anda dapat menggunakan widget Autocomplete untuk terbang ke area tampilan Places. Contoh ini menggunakan Places Autocomplete API, yang diaktifkan oleh dengan mengikuti petunjuk ini, dan Maps JavaScript API, yang diaktifkan oleh 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. Bila digabungkan dengan Places API dan Elevation API, animasi ini menyimulasikan jalan layang drone di lokasi menarik mana pun.

Contoh kode ini membantu Anda berkeliling tempat yang Anda pilih pada 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 berjalan kaki, atau menampilkan batas properti, atau untuk menghitung durasi mengemudi dan berjalan kaki. Anda juga dapat mendapatkan atribut tanpa benar-benar merender scene.

Anda dapat mengajak pengguna mengikuti tur pilihan di suatu lingkungan, atau Anda dapat menunjukkan properti di sekitar yang sedang dijual, lalu Anda dapat menambahkan seperti baliho ke lokasi kejadian.

Anda dapat meringkas perjalanan, mencantumkan properti yang Anda lihat, menampilkan detail informasi 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, menghindari bertabrakan dengan bangunan. Atau, Anda bisa membuat bangunan transparan saat kamera bergerak melaluinya.

Pertama, kunci kamera ke sebuah titik, lalu Anda dapat membuat orbit kamera untuk menampilkan aset Anda. Anda dapat melakukannya menggunakan lookAtTransform 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 mengontrol kamera, lihat Mengontrol kamera

Bekerja dengan Cesium untuk Unreal

Untuk menggunakan Plugin Cesium for Unreal dengan 3D Tiles API, ikuti langkah-langkahnya 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.

    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, pilih Item CesiumGeoreference di panel Outliner, lalu edit Lintang/Bujur/Tinggi Asal di panel Detail.

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, di opsi Tileset Source di 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, pilih Item CesiumGeoreference di Hierarki Scene, lalu edit Lintang/Bujur/Tinggi Asal di Inspektur.

Menggunakan deck.gl

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

Atribusi

Pastikan Anda menampilkan atribusi data dengan benar dengan mengekstrak copyright dari ubin gltf asset, lalu menampilkannya pada tampilan yang dirender. Sebagai 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 aktual.

<!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

Deck.gl TerrainExtension merender data 2D lain ke permukaan 3D. Misalnya, Anda dapat menjatuhkan GeoJSON jejak bangunan di atas Geometri Ubin 3D Fotorealistik.

Pada contoh berikut, lapisan bangunan divisualisasikan dengan poligon yang disesuaikan dengan permukaan Ubin 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>