1. Antes de começar
Este codelab ensina a usar os recursos da API Maps JavaScript com a tecnologia WebGL para renderizar e controlar o mapa vetorial em três dimensões.
Pré-requisitos
Este codelab pressupõe que você tem conhecimento intermediário de JavaScript e da API Maps JavaScript. Para aprender os conceitos básicos da API Maps JS, consulte o codelab Adicionar um mapa ao seu site (JavaScript).
O que você aprenderá
- Gerar um ID do mapa com o mapa vetorial para JavaScript ativado
- Controlar o mapa com inclinação e rotação programáticas
- Renderizar objetos 3D no mapa com
WebGLOverlayView
e three.js (em inglês) - Animar movimentos de câmera com
moveCamera
Recursos necessários
- Uma conta do Google Cloud Platform com o faturamento ativado
- Uma chave de API da Plataforma Google Maps com a API Maps JavaScript ativada
- Conhecimento intermediário de JavaScript, HTML e CSS
- Um editor de texto ou ambiente de desenvolvimento integrado de sua escolha
- Node.js (link em inglês)
2. Começar a configuração
Para a etapa abaixo, é necessário ativar a API Maps JavaScript.
Configurar a Plataforma Google Maps
Caso você ainda não tenha uma conta do Google Cloud Platform e um projeto com faturamento ativado, veja como criá-los no guia da Plataforma Google Maps.
- No Console do Cloud, clique no menu suspenso do projeto e selecione o projeto que você quer usar neste codelab.
- Ative as APIs e os SDKs da Plataforma Google Maps necessários para este codelab no Google Cloud Marketplace. Para fazer isso, siga as etapas descritas neste vídeo ou nesta documentação.
- Gere uma chave de API na página Credenciais do Console do Cloud. Siga as etapas indicadas neste vídeo ou nesta documentação. Todas as solicitações feitas à Plataforma Google Maps exigem uma chave de API.
Configuração do Node.js
Acesse https://nodejs.org/ (em inglês), faça o download do ambiente de execução do Node.js e instale no computador, caso ainda não tenha feito isso.
O Node.js vem com o gerenciador de pacotes npm, que é necessário para instalar as dependências deste codelab.
Fazer o download do modelo inicial do projeto
Antes de iniciar este codelab, siga as instruções abaixo para fazer o download do modelo inicial do projeto e do código completo da solução.
- Faça o download ou crie uma bifurcação do repositório GitHub deste codelab em https://github.com/googlecodelabs/maps-platform-101-webgl/ (em inglês). O projeto inicial está localizado no diretório
/starter
e inclui a estrutura de arquivos básica necessária para concluir o codelab. Tudo o que você precisa para trabalhar está no diretório/starter/src
. - Depois de fazer o download do projeto inicial, execute
npm install
no diretório/starter
. Isso instala todas as dependências necessárias listadas empackage.json
. - Depois de instalar as dependências, execute
npm start
no diretório.
O projeto inicial foi configurado para que você possa usar o webpack-dev-server, que compila e executa o código escrito localmente. O webpack-dev-server também recarrega automaticamente o app no navegador sempre que você faz alterações no código.
Se você quiser ver o código completo da solução em execução, conclua as etapas de configuração acima no diretório /solution
.
Adicionar sua chave de API
O app inicial inclui todo o código necessário para carregar o mapa com o JS API Loader (em inglês). Assim, basta informar a chave de API e o ID do mapa. O JS API Loader é uma biblioteca simples que abstrai o método tradicional de carregamento da API Maps JS in-line no modelo HTML com uma tag script
. Assim, você pode processar tudo no código JavaScript.
Para adicionar a chave de API, faça o seguinte no projeto inicial:
- Abra
app.js
. - No objeto
apiOptions
, defina sua chave de API como o valor deapiOptions.apiKey
.
3. Gerar e usar um ID do mapa
Para usar os recursos baseados em WebGL da API Maps JavaScript, você precisa de um ID do mapa com o mapa vetorial ativado.
Como gerar um ID do mapa
- No Console do Google Cloud, acesse "Plataforma Google Maps" > "Gerenciamento de mapas".
- Clique em "CREATE MAP ID".
- No campo "Nome", insira um nome para este ID do mapa.
- Na lista suspensa "Tipo de mapa", selecione "JavaScript". As "Opções de JavaScript" serão exibidas.
- Selecione a opção "Vetor" e as caixas de seleção "Inclinação" e "Rotação".
- Opcional: no campo "Descrição", insira uma descrição para a chave de API.
- Clique no botão "Salvar". A página "Detalhes do ID do mapa" é exibida.
- Copie o ID do mapa. Você precisará dele para carregar o mapa na próxima etapa.
Como usar um ID do mapa
Para carregar o mapa vetorial, é necessário inserir um ID do mapa como uma propriedade nas opções ao instanciar o mapa. Você também pode inserir o mesmo ID do mapa ao carregar a API Maps JavaScript.
Para carregar o mapa com seu ID, faça o seguinte:
- Defina seu ID do mapa como o valor de
mapOptions.mapId
.
Inserir o ID ao instanciar o mapa informa à Plataforma Google Maps quais dos seus mapas devem ser carregados em uma determinada instância. Você pode reutilizar o mesmo ID do mapa em vários apps ou várias visualizações no mesmo app.const mapOptions = { "tilt": 0, "heading": 0, "zoom": 18, "center": { lat: 35.6594945, lng: 139.6999859 }, "mapId": "YOUR_MAP_ID" };
Verifique o app em execução no seu navegador. O mapa vetorial com inclinação e rotação ativados deve carregar sem problemas. Para verificar se a inclinação e a rotação estão ativadas, mantenha a tecla Shift pressionada e arraste com o mouse ou use as setas do teclado.
Se o mapa não carregar, verifique se você inseriu uma chave de API válida em apiOptions
. Se o mapa não inclinar ou rotacionar, verifique se você inseriu um ID do mapa com inclinação e rotação ativadas em apiOptions
e mapOptions
.
Seu arquivo app.js
ficou assim:
import { Loader } from '@googlemaps/js-api-loader';
const apiOptions = {
"apiKey": 'YOUR_API_KEY',
"version": "beta"
};
const mapOptions = {
"tilt": 0,
"heading": 0,
"zoom": 18,
"center": { lat: 35.6594945, lng: 139.6999859 },
"mapId": "YOUR_MAP_ID"
}
async function initMap() {
const mapDiv = document.getElementById("map");
const apiLoader = new Loader(apiOptions);
await apiLoader.load();
return new google.maps.Map(mapDiv, mapOptions);
}
function initWebGLOverlayView (map) {
let scene, renderer, camera, loader;
// WebGLOverlayView code goes here
}
(async () => {
const map = await initMap();
})();
4. Implementar WebGLOverlayView
O WebGLOverlayView
oferece acesso direto ao mesmo contexto de renderização WebGL usado para renderizar o mapa básico vetorial. Isso significa que você pode renderizar objetos 2D e 3D diretamente no mapa usando WebGL, bem como bibliotecas de gráficos baseadas em WebGL.
O WebGLOverlayView
expõe cinco hooks que você pode usar no ciclo de vida do contexto de renderização WebGL do mapa. Veja uma breve descrição de cada hook e como eles podem ser usados:
onAdd()
: chamado quando a sobreposição é adicionada a um mapa. Para isso, chamesetMap
em uma instânciaWebGLOverlayView
. É aqui que você deve fazer qualquer trabalho em WebGL que não exija acesso direto ao contexto WebGL.onContextRestored()
: chamado quando o contexto WebGL fica disponível, mas antes da renderização de qualquer elemento. É aqui que você precisa inicializar objetos, vincular o estado por binding e fazer qualquer outra coisa que precise de acesso ao contexto WebGL, mas que possa ser realizada fora da chamadaonDraw()
. Isso permite que você configure tudo o que precisa sem sobrecarregar a renderização do mapa, que já usa muita GPU.onDraw()
: chamado uma vez por frame quando o WebGL começa a renderizar o mapa e o que mais você tiver solicitado. Faça o mínimo de alterações possível noonDraw()
para evitar problemas de desempenho na renderização do mapa.onContextLost()
: chamado quando o contexto de renderização WebGL é perdido por qualquer motivo.onRemove()
: chamado quando a sobreposição é removida do mapa. Para isso, chamesetMap(null)
em uma instânciaWebGLOverlayView
.
Nesta etapa, você criará uma instância de WebGLOverlayView
e implementará três hooks de ciclo de vida: onAdd
, onContextRestored
e onDraw
. Para manter tudo limpo e fácil de acompanhar, todo o código da sobreposição será processado na função initWebGLOverlayView()
fornecida no modelo inicial deste codelab.
- Crie uma instância
WebGLOverlayView()
.
A sobreposição é fornecida pela API Maps JS nogoogle.maps.WebGLOverlayView
. Para começar, crie uma instância anexando o código a seguir ainitWebGLOverlayView()
:const webGLOverlayView = new google.maps.WebGLOverlayView();
- Implemente hooks de ciclo de vida.
Para implementar os hooks de ciclo de vida, anexe o seguinte ainitWebGLOverlayView()
:webGLOverlayView.onAdd = () => {}; webGLOverlayView.onContextRestored = ({gl}) => {}; webGLOverlayView.onDraw = ({gl, coordinateTransformer}) => {};
- Adicione a instância de sobreposição ao mapa.
Agora, chamesetMap()
na instância de sobreposição e transmita o mapa anexando o seguinte ainitWebGLOverlayView()
:webGLOverlayView.setMap(map)
- Chame
initWebGLOverlayView
.
A última etapa é executarinitWebGLOverlayView()
adicionando o seguinte à função invocada imediatamente na parte de baixo doapp.js
:initWebGLOverlayView(map);
O initWebGLOverlayView
e a função imediatamente invocada vão ficar assim:
async function initWebGLOverlayView (map) {
let scene, renderer, camera, loader;
const webGLOverlayView = new google.maps.WebGLOverlayView();
webGLOverlayView.onAdd = () => {}
webGLOverlayView.onContextRestored = ({gl}) => {}
webGLOverlayView.onDraw = ({gl, coordinateTransformer}) => {}
webGLOverlayView.setMap(map);
}
(async () => {
const map = await initMap();
initWebGLOverlayView(map);
})();
Isso é tudo o que você precisa para implementar o WebGLOverlayView
. Agora você vai configurar todo o necessário para renderizar um objeto 3D no mapa usando o three.js.
5. Configurar uma cena three.js
Usar o WebGL pode ser muito complicado, porque exige que você defina todos os aspectos de cada objeto manualmente. Para facilitar muito as coisas, para este codelab, você usará o three.js, uma biblioteca de gráficos bastante utilizada que insere uma camada de abstração simplificada por cima do WebGL. O three.js vem com uma ampla variedade de funções de conveniência que fazem de tudo, desde a criação de um renderizador WebGL até o desenho de formas comuns de objetos 2D e 3D, o controle de câmeras, transformações de objeto e muito mais.
Existem três tipos básicos de objeto no three.js necessários para exibir qualquer coisa:
- Cena: um "contêiner" em que todos os objetos, fontes de luz, texturas etc. são renderizados e exibidos.
- Câmera: representa o ponto de vista da cena. Vários tipos de câmera estão disponíveis, e uma ou mais câmeras podem ser adicionadas a uma única cena.
- Renderizador: faz o processamento e a exibição de todos os objetos na cena. No three.js,
WebGLRenderer
é o renderizador mais usado, mas alguns outros estão disponíveis como contingência caso o cliente não seja compatível com o WebGL.
Nesta etapa, você carregará todas as dependências necessárias para o three.js e criará uma cena básica.
- Carregue o three.js.
Você precisará de duas dependências para este codelab: a biblioteca three.js e o glTF Loader, uma classe que permite carregar objetos 3D no Formato de transferência GL, ou glTF (em inglês). O three.js oferece carregadores especializados para muitos formatos de objetos 3D diferentes, mas o uso do glTF é recomendado.
No código abaixo, toda a biblioteca three.js é importada. Em um app de produção, é recomendável importar apenas as classes necessárias, mas, para este codelab, importe toda a biblioteca para simplificar. Observe também que o glTF Loader não está incluído na biblioteca padrão e precisa ser importado de um caminho separado na dependência. Esse é o caminho em que você pode acessar todos os carregadores fornecidos pelo three.js.
Para importar o three.js e o glTF Loader, adicione o seguinte no início deapp.js
:import * as THREE from 'three'; import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js';
- Crie uma cena three.js.
Para criar uma cena, instancie a classeScene
do three.js anexando o seguinte ao hookonAdd
:scene = new THREE.Scene();
- Adicione uma câmera à cena.
Como mencionado anteriormente, a câmera representa a perspectiva da visualização da cena e determina como o three.js faz a renderização visual de objetos em uma cena. Sem uma câmera, a cena não é "vista", o que significa que os objetos não serão exibidos porque não serão renderizados.
O three.js oferece várias câmeras diferentes que afetam o modo como o renderizador trata os objetos em relação a aspectos como perspectiva e profundidade. Na cena, você usará aPerspectiveCamera
, o tipo de câmera mais usado no three.js, projetado para emular a forma como o olho humano perceberia a cena. Isso significa que os objetos mais distantes da câmera serão menores do que os que estão mais próximos, a cena terá um ponto de fuga, entre outros.
Para adicionar uma câmera de perspectiva à cena, anexe o seguinte ao hookonAdd
:
Com acamera = new THREE.PerspectiveCamera();
PerspectiveCamera
, também é possível configurar os atributos que compõem o ponto de vista, incluindo as áreas próximas e distantes, a proporção e o campo de visão (FOV, na sigla em inglês). Coletivamente, esses atributos compõem o que é conhecido como frustum de visualização (link em inglês), um conceito que é importante compreender quando se trabalha em 3D, mas que não faz parte do escopo deste codelab. A configuração padrão dePerspectiveCamera
já é suficiente. - Adicione fontes de luz à cena.
Por padrão, os objetos renderizados em uma cena three.js aparecerão pretos, independentemente das texturas aplicadas. Isso ocorre porque uma cena three.js imita como os objetos agem no mundo real, onde a visibilidade da cor depende da luz refletida por um objeto. Em resumo: sem luz, sem cor.
O three.js oferece vários tipos diferentes de luz. Você usará dois: AmbientLight
: aplica uma fonte de luz difusa que ilumina de maneira uniforme todos os objetos na cena, de todos os ângulos. Isso dará à cena uma quantidade de luz referencial para garantir que as texturas em todos os objetos fiquem visíveis.DirectionalLight
: aplica uma luz vinda de uma direção específica na cena. Ao contrário de como uma luz posicionada funcionaria no mundo real, os raios de luz emitidos porDirectionalLight
são todos paralelos e não se espalham nem difundem conforme se afastam da fonte de luz.
Você pode configurar a cor e a intensidade de cada luz para agregar efeitos de iluminação. Por exemplo, no código abaixo, a luz ambiente emite uma tonalidade branca suave para toda a cena, enquanto a luz direcional é secundária e atinge objetos em um ângulo para baixo. No caso da luz direcional, o ângulo é definido usandoposition.set(x, y ,z)
, em que cada valor é relativo ao respectivo eixo. Por exemplo,position.set(0,1,0)
colocaria a luz diretamente acima da cena no eixo y apontando para baixo.
Para adicionar fontes de luz à cena, anexe o seguinte ao hookonAdd
:const ambientLight = new THREE.AmbientLight( 0xffffff, 0.75 ); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.25); directionalLight.position.set(0.5, -1, 0.5); scene.add(directionalLight);
Seu hook onAdd
ficou assim:
webGLOverlayView.onAdd = () => {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera();
const ambientLight = new THREE.AmbientLight( 0xffffff, 0.75 );
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.25);
directionalLight.position.set(0.5, -1, 0.5);
scene.add(directionalLight);
}
Sua cena está configurada e pronta para renderização. Em seguida, você vai configurar o renderizador WebGL e renderizar a cena.
6. Renderizar a cena
É hora de renderizar a cena. Até agora, tudo que você criou com o three.js está inicializado no código, mas essencialmente não existe porque ainda não foi renderizado no contexto de renderização WebGL. O WebGL renderiza o conteúdo 2D e 3D no navegador usando a API Canvas. Se você já usou a API Canvas, provavelmente conhece o context
de uma tela HTML, que é onde tudo é renderizado. O que talvez você não saiba é que essa é uma interface que expõe o contexto de renderização dos gráficos OpenGL por meio da API WebGLRenderingContext
no navegador.
Para facilitar o gerenciamento do renderizador do WebGL, o three.js oferece o WebGLRenderer
(em inglês), um wrapper que facilita a configuração do contexto de renderização WebGL para que o three.js possa renderizar cenas no navegador. No entanto, no caso do mapa, não basta renderizar a cena three.js no navegador ao lado do mapa. O three.js precisa ser renderizado exatamente no mesmo contexto de renderização do mapa, de modo que o mapa e todos os objetos da cena three.js sejam renderizados no mesmo espaço mundial. Isso possibilita que o renderizador processe as interações entre os objetos no mapa e na cena, como a oclusão, que é uma maneira sofisticada de dizer que um objeto ocultará elementos atrás dele.
Parece complicado, não parece? Felizmente, o three.js vem de novo ao resgate.
- Configure o renderizador WebGL.
Quando cria uma nova instância doWebGLRenderer
do three.js, você pode dar a ela o contexto de renderização WebGL específico em que você quer renderizar a cena. Lembra do argumentogl
que é transmitido para o hookonContextRestored
? Esse objetogl
é o contexto de renderização WebGL do mapa. Tudo o que você precisa fazer é inserir o contexto, a tela e os atributos na instânciaWebGLRenderer
, todos disponíveis pelo objetogl
. Nesse código, a propriedadeautoClear
do renderizador também é definida comofalse
para que ele não apague a saída a cada frame.
Para configurar o renderizador, anexe o seguinte ao hookonContextRestored
:renderer = new THREE.WebGLRenderer({ canvas: gl.canvas, context: gl, ...gl.getContextAttributes(), }); renderer.autoClear = false;
- Renderize a cena.
Depois de configurar o renderizador, chamerequestRedraw
na instânciaWebGLOverlayView
para informar à sobreposição que é necessário redesenhar quando o próximo frame for renderizado. Em seguida, chamerender
no renderizador. e transmita a cena e a câmera do three.js para renderizar. Por fim, limpe o estado do contexto de renderização WebGL. Essa é uma etapa importante para evitar conflitos de estado GL, já que o uso da visualização de sobreposição do WebGL depende do estado GL compartilhado. Se o estado não for redefinido ao final de cada chamada de desenho, os conflitos de estado GL poderão causar uma falha no renderizador.
Para fazer isso, anexe o seguinte ao hookonDraw
para que ele seja executado em cada frame:webGLOverlayView.requestRedraw(); renderer.render(scene, camera); renderer.resetState();
Os hooks onContextRestored
e onDraw
ficarão assim:
webGLOverlayView.onContextRestored = ({gl}) => {
renderer = new THREE.WebGLRenderer({
canvas: gl.canvas,
context: gl,
...gl.getContextAttributes(),
});
renderer.autoClear = false;
}
webGLOverlayView.onDraw = ({gl, transformer}) => {
webGLOverlayView.requestRedraw();
renderer.render(scene, camera);
renderer.resetState();
}
7. Renderizar um modelo 3D no mapa
Você já colocou todas as peças no lugar. Você configurou a visualização de sobreposição do WebGL e criou uma cena three.js, mas há um problema: não há nada nela. Agora é hora de renderizar um objeto 3D na cena. Para isso, vamos usar o glTF Loader que você importou em uma etapa anterior.
Os modelos 3D têm vários formatos diferentes. No entanto, para o three.js, o formato glTF é o preferido devido ao tamanho e ao desempenho do ambiente de execução. Neste codelab, um modelo para você renderizar na cena já está disponível em /src/pin.gltf
.
- Crie uma instância de carregador de modelo.
Anexe o seguinte aonAdd
:loader = new GLTFLoader();
- Carregue um modelo 3D.
Os carregadores de modelo são assíncronos e executam um callback quando o modelo é totalmente carregado. Para carregarpin.gltf
, anexe o seguinte aonAdd
:const source = "pin.gltf"; loader.load( source, gltf => {} );
- Adicione o modelo à cena.
Agora, é possível adicionar o modelo à cena anexando o seguinte ao callbackloader
. Observe que o que está sendo adicionado égltf.scene
, e nãogltf
:scene.add(gltf.scene);
- Configure a matriz de projeção da câmera.
A última coisa que você precisa para renderizar corretamente o modelo no mapa é definir a matriz de projeção da câmera na cena three.js. A matriz de projeção é especificada como uma matrizMatrix4
do three.js, que define um ponto em um espaço tridimensional com transformações, como rotações, distorção, escala e muito mais.
No caso doWebGLOverlayView
, a matriz de projeção é usada para informar ao renderizador onde e como renderizar a cena three.js em relação ao mapa básico. Mas há um problema. Os locais no mapa são especificados como pares de coordenadas de latitude e longitude, enquanto os locais na cena three.js são coordenadasVector3
. Como você pode ter imaginado, o cálculo da conversão entre os dois sistemas não é simples. Para resolver isso, oWebGLOverlayView
transmite um objetocoordinateTransformer
para o hook de ciclo de vidaOnDraw
que contém uma função chamadafromLatLngAltitude
. OfromLatLngAltitude
usa um objetoLatLngAltitude
ouLatLngAltitudeLiteral
e, opcionalmente, um conjunto de argumentos que definem uma transformação para a cena e a convertem em uma matriz de projeção de visualização do modelo (MVP, na sigla em inglês) para você. Tudo o que você precisa fazer é especificar no mapa onde quer que a cena three.js seja renderizada, além de como quer que ela seja transformada, e oWebGLOverlayView
fará o resto. Depois, é possível converter a matriz de MVP em uma matrizMatrix4
do three.js e definir a matriz de projeção da câmera nela.
No código abaixo, o segundo argumento diz à visualização de sobreposição do WebGL para definir a altitude da cena three.js em 120 metros acima do solo, o que fará com que o modelo pareça flutuar.
Para definir a matriz de projeção da câmera, anexe o seguinte ao hookonDraw
:const latLngAltitudeLiteral = { lat: mapOptions.center.lat, lng: mapOptions.center.lng, altitude: 120 } const matrix = transformer.fromLatLngAltitude(latLngAltitudeLiteral); camera.projectionMatrix = new THREE.Matrix4().fromArray(matrix);
- Transforme o modelo.
Você vai perceber que o alfinete não está perpendicular ao mapa. Nos gráficos 3D, além do espaço mundial ter os próprios eixos x, y e z que determinam a orientação, cada objeto também tem o próprio espaço, com um conjunto independente de eixos.
No caso desse modelo, ele não foi criado com o que normalmente consideramos a "parte superior" do alfinete voltada para cima no eixo y. Portanto, é necessário transformar o objeto para orientá-lo na direção desejada relativa ao espaço mundial, chamandorotation.set
nele. No three.js, a rotação é especificada em radianos, não em graus. Geralmente, é mais fácil pensar em graus. Por isso, a conversão apropriada precisa ser feita usando a fórmuladegrees * Math.PI/180
.
Além disso, o modelo é pequeno, então você também irá escalonar de maneira uniforme em todos os eixos dele chamandoscale.set(x, y ,z)
.
Para girar e dimensionar o modelo, adicione o seguinte no callbackloader
deonAdd
antes descene.add(gltf.scene)
, que adiciona o glTF à cena:gltf.scene.scale.set(25,25,25); gltf.scene.rotation.x = 180 * Math.PI/180;
Agora o alfinete está na posição vertical em relação ao mapa.
Os hooks onAdd
e onDraw
ficarão assim:
webGLOverlayView.onAdd = () => {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera();
const ambientLight = new THREE.AmbientLight( 0xffffff, 0.75 ); // soft white light
scene.add( ambientLight );
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.25);
directionalLight.position.set(0.5, -1, 0.5);
scene.add(directionalLight);
loader = new GLTFLoader();
const source = 'pin.gltf';
loader.load(
source,
gltf => {
gltf.scene.scale.set(25,25,25);
gltf.scene.rotation.x = 180 * Math.PI/180;
scene.add(gltf.scene);
}
);
}
webGLOverlayView.onDraw = ({gl, transformer}) => {
const latLngAltitudeLiteral = {
lat: mapOptions.center.lat,
lng: mapOptions.center.lng,
altitude: 100
}
const matrix = transformer.fromLatLngAltitude(latLngAltitudeLiteral);
camera.projectionMatrix = new THREE.Matrix4().fromArray(matrix);
webGLOverlayView.requestRedraw();
renderer.render(scene, camera);
renderer.resetState();
}
A seguir, veremos as animações da câmera.
8. Animar a câmera
Agora que você renderizou um modelo no mapa e consegue mover tudo nas três dimensões, o próximo passo será controlar esse movimento de forma programática. A função moveCamera
permite definir as propriedades de centro, zoom, inclinação e direção do mapa simultaneamente, dando a você um controle preciso da experiência do usuário. Além disso, o moveCamera
pode ser chamado em um loop de animação para criar transições fluidas a quase 60 frames por segundo.
- Aguarde o carregamento do modelo.
Para criar uma experiência perfeita para o usuário, aguarde para começar a mover a câmera até que o modelo glTF seja carregado. Para fazer isso, anexe o manipulador de eventosonLoad
do carregador ao hookonContextRestored
:loader.manager.onLoad = () => {}
- Crie um loop de animação.
Há mais de uma maneira de criar um loop de animação, como usarsetInterval
ourequestAnimationFrame
. Neste caso, você usará a funçãosetAnimationLoop
do renderizador three.js, que chamará automaticamente qualquer código declarado no callback sempre que o three.js renderizar um novo frame. Para criar o loop de animação, adicione o seguinte ao manipulador de eventosonLoad
da etapa anterior:renderer.setAnimationLoop(() => {});
- Defina a posição da câmera no loop de animação.
Em seguida, chamemoveCamera
para atualizar o mapa. Aqui, as propriedades do objetomapOptions
, usado para carregar o mapa, irão definir a posição da câmera:map.moveCamera({ "tilt": mapOptions.tilt, "heading": mapOptions.heading, "zoom": mapOptions.zoom });
- Atualize a câmera a cada frame.
Última etapa! Atualize o objetomapOptions
no final de cada frame para definir a posição da câmera para o próximo frame. Neste código, uma instruçãoif
é usada para aumentar a inclinação até que o valor máximo de 67,5 seja atingido. A direção é alterada um pouco a cada frame até a câmera concluir a rotação de 360 graus. Quando a animação desejada for concluída,null
será transmitido parasetAnimationLoop
a fim de cancelar a animação para que ela não seja exibida para sempre.if (mapOptions.tilt < 67.5) { mapOptions.tilt += 0.5 } else if (mapOptions.heading <= 360) { mapOptions.heading += 0.2; } else { renderer.setAnimationLoop(null) }
Seu hook onContextRestored
ficou assim:
webGLOverlayView.onContextRestored = ({gl}) => {
renderer = new THREE.WebGLRenderer({
canvas: gl.canvas,
context: gl,
...gl.getContextAttributes(),
});
renderer.autoClear = false;
loader.manager.onLoad = () => {
renderer.setAnimationLoop(() => {
map.moveCamera({
"tilt": mapOptions.tilt,
"heading": mapOptions.heading,
"zoom": mapOptions.zoom
});
if (mapOptions.tilt < 67.5) {
mapOptions.tilt += 0.5
} else if (mapOptions.heading <= 360) {
mapOptions.heading += 0.2;
} else {
renderer.setAnimationLoop(null)
}
});
}
}
9. Parabéns!
Se tudo correu de acordo com o plano, você terá um mapa com um grande alfinete 3D que tem esta aparência:
O que você aprendeu
Neste codelab, você aprendeu várias coisas. Veja os destaques:
- Implementar
WebGLOverlayView
e seus hooks de ciclo de vida - Integrar o three.js ao mapa
- Criar uma cena básica do three.js, incluindo câmeras e iluminação
- Carregar e manipular modelos 3D usando o three.js
- Controlar e animar a câmera para o mapa usando
moveCamera
Qual é a próxima etapa?
O WebGL e os gráficos de computador em geral são assuntos complexos, por isso sempre há muito a aprender. Para começar, veja abaixo alguns recursos importantes:
- Documentação da visualização de sobreposição do WebGL
- Primeiros passos com o WebGL
- Documentação do three.js (em inglês)
- Ajude a criar o conteúdo mais relevante para você respondendo à pergunta abaixo: «codelabs/maps-platform/shared/_next-lab-survey.lab.md» O codelab que você quer não está listado acima? Crie um novo "Issue" aqui para sugerir o codelab (link em inglês).