Trabalhar com um renderizador de blocos 3D

Os blocos fotorrealistas em 3D estão formato glTF padrão OGC, ou seja, é possível usar qualquer renderizador compatível com a especificação dos blocos OGC em 3D para criar suas visualizações em 3D. Por exemplo: Cesium (link em inglês) é uma biblioteca de código aberto fundamental para a renderização de visualizações em 3D.

Trabalhar com CesiumJS

CesiumJS é uma biblioteca JavaScript de código aberto para visualização 3D na web. Para obter mais informações sobre como usar o CesiumJS, consulte Saiba mais sobre o CesiumJS.

Controles de usuário

O renderizador de bloco do CesiumJS tem um conjunto padrão de controles de usuário.

Ação Descrição
Mover visualização Clique com o botão esquerdo do mouse e arrastar
Visualização com zoom Clique com o botão direito do mouse e arrastar ou rolar a roda do mouse
Girar visualização Ctrl + clicar com o botão esquerdo/direito e arrastar ou clicar com o botão do meio e arrastar

Práticas recomendadas

Há várias abordagens que podem ser adotadas para diminuir o carregamento 3D do CesiumJS vezes. Exemplo:

  • Para ativar solicitações simultâneas, adicione a seguinte instrução ao HTML de renderização:

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

    Quanto maior o REQUEST_COUNT, mais rápido os blocos serão carregados. No entanto, ao carregar em um navegador Chrome com a REQUEST_COUNT for maior do que 10 e o cache desativado, poderá ocorrer o Problema do Chrome. Na maioria dos casos de uso, recomendamos um REQUEST_COUNT de 18 para otimizar desempenho.

  • Ativar pular níveis de detalhes. Para mais informações, consulte Problema do Cesium.

Verifique se as atribuições de dados são exibidas corretamente. showCreditsOnScreen: true: Para mais informações, consulte Políticas.

Métricas de renderização

Para encontrar o frame rate, veja quantas vezes por segundo a requestAnimationFrame é chamado.

Para ver como a latência de frames é calculada, consulte a PerformanceDisplay .

Exemplos do renderizador CesiumJS

Você pode usar o renderizador CesiumJS com os blocos 3D da API Map Tiles simplesmente fornecendo o URL do conjunto de blocos raiz.

Exemplo simples

O exemplo a seguir inicializa o renderizador CesiumJS e, em seguida, carrega a raiz conjunto de blocos.

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

Para saber mais sobre requestRenderMode, consulte Como ativar o modo de renderização da solicitação.

A página HTML é renderizada conforme mostrado aqui.

Integração da API Places

Você pode usar o CesiumJS com o API Places para recuperar mais informações. Você pode usar o widget de preenchimento automático para voar até janela de visualização do Places. Este exemplo usa a API Places Autocomplete, que é ativado pela seguindo estas instruções, e a API Maps JavaScript, que é ativada pela seguindo estas instruções.

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

Visualização em rotação de drone

É possível controlar a câmera para animar o conjunto de blocos. Quando combinado com a API Places e a API Elevation, esta animação simula uma sobrevoo de drones de qualquer ponto de interesse.

Esse exemplo de código vai voar ao redor do lugar que você selecionou na Widget de preenchimento automático.

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

Desenhar polilinhas e rótulos

Este exemplo de código demonstra como adicionar polilinhas e rótulos a um mapa. Você pode adicione polilinhas a um mapa para mostrar rotas de carro e a pé ou para mostrar limites de propriedades ou para calcular durações de percurso e caminhada. Você também pode e receber atributos sem realmente renderizar o cenário.

Você pode levar os usuários em um tour organizado de uma vizinhança ou mostrar propriedades vizinhas que estão à venda no momento, e adicionar recursos em 3D objetos como outdoors na cena.

Você poderia resumir uma viagem, listando as propriedades que acessou, esses detalhes em objetos virtuais.

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

Órbita da câmera

No Cesium, é possível orbitar a câmera em torno de um ponto de interesse, evitando em caso de colisões com edifícios. Como alternativa, é possível tornar os edifícios transparentes quando a câmera se move por eles.

Primeiro, fixe a câmera em um ponto e depois crie uma órbita com ela para mostrar seu recurso. Você pode fazer isso usando o lookAtTransform com um listener de eventos, conforme demonstrado neste exemplo de código.

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

Para mais informações sobre como controlar a câmera, consulte Controlar a câmera

Trabalhar com o Cesium para Unreal

Para usar o plug-in do Cesium para Unreal com a API 3D Tiles, siga as etapas. a seguir.

  1. Instale o plug-in do Cesium para Unreal.

  2. Crie um novo projeto do Unreal.

  3. Conectar-se à API de blocos fotorrealistas em 3D do Google.

    1. Abra a janela do Cesium selecionando Cesium >. Cesium no menu.

    2. Selecione Conjunto de blocos de blocos 3D em branco.

    3. No Painel de navegação, abra o painel Detalhes selecionando este Cesium3DTileset (link em inglês).

    4. Altere a Origem de Do íon Cesium para Do URL.

    5. Defina o URL como o URL do Google 3D Tiles.

    https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
    
    1. Ative Mostrar créditos na tela para mostrar as atribuições corretamente.
  4. Isso carrega o mundo. Para mover-se para qualquer LatLng, selecione o CesiumGeoreference no painel Painel de navegação e edite o Latitude/longitude/altura da origem no painel Detalhes.

Trabalhar com o Cesium para Unity

Para usar blocos fotorrealistas com o Cesium para Unity, siga as etapas abaixo.

  1. Crie um novo projeto do Unity.

  2. Adicione um novo registro com escopo na seção "Package Manager" (em Editor > Project Settings).

    • Nome: Cesium

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

    • Escopos: com.cesium.unity

  3. Instale o pacote Cesium para Unity.

  4. Conecte-se à API de blocos fotorrealistas em 3D do Google.

    1. Abra a janela do Cesium selecionando Cesium >. Cesium no menu.

    2. Clique em Conjunto de blocos de blocos em 3D em branco.

    3. No painel do lado esquerdo, na opção Tileset Source em Source, selecione Do URL (em vez de Do Cesium Ion).

    4. Defina o URL como o URL do Google 3D Tiles.

    https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
    
    1. Ative Mostrar créditos na tela para mostrar as atribuições corretamente.
  5. Isso carrega o mundo. Para mover-se para qualquer LatLng, selecione o CesiumGeoreference na Hierarquia de cenas e edite o Latitude/longitude/altura de origem no Inspector.

Trabalhar com o deck.gl

deck.gl (link em inglês) com tecnologia WebGL, é um framework de JavaScript de código aberto para visualizações de dados em grande escala.

Atribuição

Verifique se as atribuições de dados estão sendo mostradas corretamente. Para isso, extraia o copyright dos blocos gltf asset e, em seguida, exibi-lo na visualização renderizada. Para mais informações, consulte Exibir atribuições de dados.

Exemplos do renderizador deck.gl

Exemplo simples

O exemplo a seguir inicializa o renderizador deck.gl e, em seguida, carrega um local em 3D. No seu código, não se esqueça de substituir YOUR_API_KEY pelo seu chave de API real.

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

Visualize camadas em 2D sobre os blocos fotorrealistas em 3D do Google

O deck.gl TerrainExtension renderiza dados em 2D em uma superfície em 3D; Por exemplo, é possível cobrir GeoJSON da planta de um edifício sobre a geometria fotorrealista dos blocos em 3D.

No exemplo a seguir, uma camada de edifícios é visualizada com os polígonos adaptado para a superfície dos blocos fotorrealistas em 3D.

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