Primeiro, o modelo
Um GLB reúne geometria e materiais; também pode incluir animações. O <model-viewer> trata de o mostrar.
Publica um modelo 3D numa página, deixa a pessoa explorá-lo e acrescenta reações úteis. Aprende o que o componente faz bem, onde termina o seu alcance e como entregar uma experiência leve e acessível.
Em ecrãs largos, o modelo acompanha a leitura e o scroll muda o ângulo da câmara. No telemóvel, experimenta os controlos e depois lê os cartões por ordem.
Um GLB coloca o personagem na página.
A carregar o modelo 3D incluído nesta apostila…
RobotExpressive · Tomás Laulhé e Don McCurdy · CC0 1.0.
Um GLB reúne geometria e materiais; também pode incluir animações. O <model-viewer> trata de o mostrar.
A câmara muda de ângulo. A pessoa também pode arrastar o modelo e explorar os detalhes ao seu ritmo.
Aprender a controlar a câmara →Quando o GLB inclui animações, podes escolhê-las e reproduzi-las pelos botões. Experimenta o robô, a raposa ou o modelo Hero; o abacate mostra o caso sem animação.
Aprender a ligar animações →Uma boa experiência também precisa de texto alternativo, carregamento progressivo e testes em telemóvel.
Preparar a publicação →<model-viewer> é um componente HTML para mostrar modelos glTF/GLB. Dá-te carregamento, câmara orbital, iluminação, animações, anotações e um caminho para AR sem montares um motor 3D completo.
| Objetivo | É boa escolha? | Porquê |
|---|---|---|
| Produto que a pessoa pode rodar e ampliar | Sim | Os controlos de câmara já existem. |
| Personagem que acena ao clicar | Sim | Podes escolher animações incluídas no modelo e iniciar a reprodução por JavaScript. |
| Boneco cuja cabeça segue continuamente o cursor | Limitado | Podes mover a câmara ou trocar animações; controlar diretamente cada osso pede uma solução 3D de nível mais baixo. |
| Jogo com física, IA e vários personagens | Não é a opção habitual | Three.js, React Three Fiber ou um motor de jogo oferecem controlo mais adequado. |
O GLB é uma forma de empacotar um modelo glTF. O mesmo componente também aceita um ficheiro .gltf com recursos associados. Em ambos os casos, as animações têm de vir no asset: o componente apresenta-as, mas não as cria.
Se a interação se resume a ver, rodar, ampliar, clicar em pontos e escolher animações, começa aqui. Se precisas de mexer continuamente nos ossos do personagem, avalia Three.js.
Descarrega o RobotExpressive.glb (CC0 1.0), guarda-o ao lado de index.html e cria exemplo.css. Este primeiro exemplo funciona sem precisares de criar um modelo. Serve o projeto por HTTP local ou publica-o; abrir diretamente com file:// pode bloquear o carregamento dos ficheiros.
http:// ou https://, se o GLB está no caminho indicado em src e se a biblioteca foi carregada. Ao abrir um HTML por file://, o browser pode bloquear os ficheiros locais.Para abrir esta apostila localmente, inicia o servidor na pasta vercelUniana, que contém playbooks e models:
python3 -m http.server 8000
# Abre http://localhost:8000/playbooks/apostila-model-viewer.html<!doctype html>
<html lang="pt-PT">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>O meu personagem 3D</title>
<script type="module"
src="https://cdn.jsdelivr.net/npm/@google/model-viewer@4.4.0/dist/model-viewer.min.js"></script>
<link rel="stylesheet" href="exemplo.css">
</head>
<body>
<h1>Conhece o personagem</h1>
<model-viewer src="RobotExpressive.glb"
alt="Robô 3D branco e laranja que pode ser rodado"
camera-controls touch-action="pan-y" loading="eager">
</model-viewer>
</body>
</html>model-viewer {
display: block;
width: 100%;
height: 480px;
background: #edf4ee;
}Para testar este exemplo separado, guarda index.html, exemplo.css e RobotExpressive.glb na mesma pasta. Inicia o servidor nessa pasta e abre http://localhost:8000/. Se a porta 8000 já estiver ocupada pela apostila, encerra aquele servidor antes de iniciar outro.
src aponta para o modelo; alt descreve-o; camera-controls permite rodar e ampliar; touch-action="pan-y" ajuda a manter o scroll vertical no telemóvel. A altura em CSS é essencial: sem espaço visível, o modelo não aparece como esperado.
Os botões no percurso visual trocam o modelo sem recarregar a página. O GLB escolhido só é transferido quando o selecionas; o abacate, mais pesado, permite observar a diferença numa ligação móvel. As setas percorrem as animações disponíveis. Um modelo sem animações continua a poder ser rodado, mas os controlos de reprodução ficam inativos.
| Modelo | O que demonstra | Origem e licença |
|---|---|---|
| RobotExpressive.glb | 14 animações; cerca de 464 KB. | Tomás Laulhé e Don McCurdy; CC0 1.0. |
| hero.glb | Modelo do projeto, com animação de rotação; cerca de 2,6 MB. | Fornecido neste projeto; verifica os direitos antes de o reutilizar fora dele. |
| Fox.glb | Raposa leve com três animações; cerca de 163 KB. | Modelo de PixelMannen (CC0); rig e animações de tomkranis e conversão de AsoboStudio/scurest (CC BY 4.0). Créditos completos. |
| Avocado.glb | Modelo estático com textura; cerca de 8,1 MB. | Microsoft; CC0 1.0. Fonte e licença. |
Na versão fornecida, hero.glb usa 29 materiais. Isso não impede a visualização na página, mas ultrapassa a referência de 10 materiais recomendada pelo Scene Viewer para AR no Android. Testa o resultado no telemóvel antes de decidir se precisas de simplificar o asset.
O atributo src aceita modelos glTF 2.0 em dois formatos: .glb e .gltf. Escolhe GLB para começar, porque costuma ser mais simples de transportar. Um .gltf é um ficheiro JSON que pode apontar para ficheiros .bin e texturas; publica esses recursos mantendo os caminhos relativos. Um GLB também pode, em certos casos, referenciar recursos externos, por isso verifica sempre o resultado exportado.
| Ficheiro | Uso no model-viewer | O que fazer |
|---|---|---|
.glb | Sim, em src | Formato binário de glTF; opção mais simples para o primeiro projeto. |
.gltf | Sim, em src | Publicar também os .bin e as imagens que o ficheiro referenciar. |
.usdz ou .reality | Apenas em ios-src para AR Quick Look | Não substituem o modelo glTF/GLB mostrado na página. |
.fbx, .obj, .stl, .blend | Não diretamente em src | Exportar ou converter para GLB/glTF antes de publicar. |
<model-viewer src="assets/personagem.gltf"
alt="Personagem 3D que pode ser rodado"
camera-controls touch-action="pan-y">
</model-viewer>Se personagem.gltf apontar para personagem.bin e texturas/cor.png, publica os três caminhos. Mudar apenas o nome da extensão de um FBX ou OBJ para .gltf não converte o ficheiro.
“Extensões” também pode significar recursos opcionais dentro de um glTF: Draco comprime geometria, KTX2 comprime texturas e Meshopt é outra opção de compressão. O model-viewer suporta esses casos, mas o Meshopt exige configurar o descodificador. Testa a compatibilidade do asset depois de o otimizares.
Sim, o Blender exporta diretamente para GLB. Escolhe Ficheiro → Exportar → glTF 2.0 (.glb, .gltf) e, no painel de exportação, seleciona glTF Binary (.glb). O GLB reúne modelo, materiais, texturas e animações num único ficheiro. Os nomes exatos das opções podem variar com a versão do Blender.
Idle e Wave; verifica Actions/NLA, esqueleto e shape keys no GLB final. Simulações e movimentos gerados só durante o render precisam de ser convertidos em keyframes quando não são exportáveis.O poster dá uma primeira imagem enquanto o 3D carrega. Com loading="lazy", um visualizador mais abaixo na página só começa a carregar quando se aproxima da área visível.
<model-viewer
src="/assets/personagem.glb"
poster="/assets/personagem.webp"
alt="Personagem 3D estilizado, em pé, que pode ser rodado"
camera-controls touch-action="pan-y"
loading="lazy">
</model-viewer>Exporta o mesmo modelo como GLB e glTF, testa ambos no navegador e compara silhueta, texturas, animações e tamanho total transferido, incluindo recursos externos.
camera-controls permite que a pessoa explore o modelo. camera-orbit define o ângulo inicial da câmara. auto-rotate pode chamar a atenção, mas movimento constante deve ser usado com moderação e respeitar a preferência de movimento reduzido.
Para um modelo que será apresentado no <model-viewer>, começa por não exportar as câmaras nem as luzes da cena de trabalho. Configura o enquadramento na página com camera-orbit, camera-target e field-of-view; configura o aspeto com environment-image, exposure, tone-mapping e shadow-intensity. A câmara usada no render do Blender não define automaticamente a vista inicial deste componente.
O Blender pode exportar câmaras e luzes pontuais, direcionais ou focos para glTF. Mantém esses dados apenas se outro visualizador ou motor da tua produção precisar deles e testa esse destino. Luzes Area e iluminação World não passam como luzes glTF equivalentes. Se quiseres um aspeto específico no model-viewer, testa o GLB com a iluminação de ambiente que a página realmente usará. Evita gravar sombras fortes no Base Color se também haverá iluminação e sombras dinâmicas, sobretudo em AR.
<model-viewer src="personagem.glb"
alt="Personagem 3D com roupa azul"
camera-controls
camera-orbit="25deg 75deg auto"
touch-action="pan-y">
</model-viewer>Se quiseres ligar o scroll à apresentação, podes atualizar a órbita da câmara em JavaScript. Isso faz o ponto de vista mudar; não faz a cabeça do boneco mover-se de forma independente. Para uma página normal, evita bloquear o scroll ou capturar todos os gestos dentro do 3D.
model-viewer. O segundo pede animações preparadas ou acesso direto ao esqueleto numa biblioteca 3D.As animações têm de existir no GLB ou glTF. Depois de o modelo carregar, availableAnimations devolve os seus nomes. Podes escolher uma com animationName e reproduzi-la com play().
Experimenta a demonstração interativa no início da apostila: escolhe uma animação, reproduz e pausa o robô.
No percurso visual, as setas escolhem uma animação e o botão Reproduzir inicia-a; o scroll muda apenas a câmara. Se o GLB não tiver animações, podes continuar a rodar o modelo. O exemplo abaixo usa um botão com o teu próprio ficheiro:
<model-viewer id="personagem" src="personagem.glb"
alt="Personagem 3D que acena" camera-controls>
</model-viewer>
<button id="acenar" type="button" disabled>Acenar</button>
<script>
const viewer = document.querySelector('#personagem');
const botao = document.querySelector('#acenar');
viewer.addEventListener('load', () => {
botao.disabled = !viewer.availableAnimations.includes('Wave');
});
botao.addEventListener('click', () => {
viewer.animationName = 'Wave';
viewer.currentTime = 0;
viewer.play({ repetitions: 1 });
});
</script>O nome Wave é apenas um exemplo: usa o nome real exportado no teu modelo. Para alternar entre Idle e Wave, podes ouvir o evento finished e voltar à animação de repouso. O componente pode fazer transição suave quando mudas animationName.
Guarda RobotExpressive.glb e Avocado.glb ao lado do HTML. Este exemplo mostra o estado de carregamento e informa se o GLB escolhido contém animações. O abacate continua interativo mesmo sem clips.
<model-viewer id="visor" src="RobotExpressive.glb"
alt="Robô 3D que pode ser rodado" camera-controls
touch-action="pan-y"></model-viewer>
<button type="button" data-modelo="RobotExpressive.glb"
data-descricao="Robô 3D que pode ser rodado">Robô</button>
<button type="button" data-modelo="Avocado.glb"
data-descricao="Meio abacate 3D que pode ser rodado">Abacate</button>
<p id="estado" role="status">A carregar…</p>
<script>
const visor = document.querySelector('#visor');
const estado = document.querySelector('#estado');
document.querySelectorAll('[data-modelo]').forEach(botao => {
botao.addEventListener('click', () => {
estado.textContent = 'A carregar…';
visor.setAttribute('alt', botao.dataset.descricao);
visor.setAttribute('src', botao.dataset.modelo);
});
});
visor.addEventListener('load', () => {
const total = visor.availableAnimations.length;
estado.textContent = total
? `Modelo pronto: ${total} animação(ões).`
: 'Modelo pronto, sem animações.';
});
visor.addEventListener('error', () => {
estado.textContent = 'Falha ao carregar. Verifica o caminho do GLB.';
});
</script>Este exemplo usa os modelos descarregáveis do capítulo 2. Mantém o tamanho e o fundo do visualizador definidos em CSS, como no primeiro exemplo. O nome exportado da animação pode ser pouco legível, como acontece com o hero.glb; dá nomes curtos às ações no Blender quando puderes.
Adiciona uma animação curta de saudação e um botão. Só ativa o botão quando availableAnimations confirmar que a animação existe. Depois troca para um modelo sem animação e confirma que o modelo ainda pode ser rodado.
Um hotspot é um elemento HTML colocado numa posição do modelo. Serve para identificar uma peça ou abrir uma explicação. As coordenadas têm de corresponder ao teu GLB; a forma mais fácil de as obter é usar o editor do projeto model-viewer.
<model-viewer src="produto.glb"
alt="Cadeira 3D com detalhes identificados"
camera-controls touch-action="pan-y">
<button slot="hotspot-encosto"
data-position="0m 0.7m 0m"
data-normal="0m 0m 1m"
type="button">Ver encosto</button>
</model-viewer>Para oferecer AR em dispositivos compatíveis, adiciona ar. Os modos disponíveis incluem WebXR, Scene Viewer e Quick Look; a disponibilidade real depende do dispositivo e do navegador. O atributo ios-src pode apontar para um .usdz ou .reality para Quick Look; sem ele, o componente pode gerar um USDZ. Essa geração automática ainda tem limitações, incluindo animações, por isso testa a experiência em iOS.
<model-viewer src="produto.glb"
alt="Cadeira 3D que pode ser vista no espaço real"
camera-controls touch-action="pan-y"
ar ar-modes="webxr scene-viewer quick-look"
ar-scale="fixed">
</model-viewer>.glb ou .gltf mesmo quando forneces ios-src para AR. Confirma tamanho, orientação, sombras e colocação num telemóvel compatível. Os botões HTML da página não acompanham o modelo quando ele abre nas aplicações nativas Scene Viewer ou Quick Look.Uma cena 3D leve começa no asset. Mede o peso do GLB, reduz polígonos invisíveis ou desnecessários e adequa as texturas ao tamanho real do visualizador. Usa poster e loading="lazy" em conteúdo abaixo da dobra.
Conta triângulos exportados, não apenas “polígonos” no Blender. Não existe um limite universal para model-viewer: desempenho depende também de materiais, texturas, transparências, animações, tamanho do canvas e aparelho. Como referência para assets usados no Scene Viewer do Android, a Google indica 30–50 mil triângulos como faixa ideal e recomenda não ultrapassar 100 mil, até 10 materiais, texturas até 2048 × 2048 e cerca de 10 MB de modelo. Trata estes valores como ponto de partida e restrições desse modo de AR, não como garantia de fluidez para toda a web.
| O que medir | Primeira decisão | Quando rever |
|---|---|---|
| Triângulos e vértices exportados | Preserva a silhueta; simplifica superfícies planas, costas invisíveis e subdivisão. | Se a rotação perder fluidez ou o asset ultrapassar a meta para o aparelho-alvo. |
| Materiais e chamadas de desenho | Reutiliza materiais e considera um atlas de texturas. | Se muitas peças pequenas ou transparências degradarem o desempenho. |
| Texturas e memória | Começa com a menor resolução que ainda se lê no tamanho de apresentação; comprime e compara. | Se o GLB carregar devagar ou o browser fechar/recarregar a página. |
| Download e descodificação | Compara GLB original e versões com Draco, Meshopt ou KTX2 no telemóvel. | Se a poupança de rede introduzir espera de descodificação ou incompatibilidade. |
| Rig e animações | Remove ossos, shape keys e clips sem uso. | Se a animação baixar a taxa de imagens ou inflar o ficheiro. |
alt útil: descreve o que a pessoa precisa de saber sobre o objeto.const viewer = document.querySelector('model-viewer');
const menosMovimento = matchMedia('(prefers-reduced-motion: reduce)');
viewer.autoRotate = !menosMovimento.matches;
menosMovimento.addEventListener('change', event => {
viewer.autoRotate = !event.matches;
});Se houver uma ação importante, oferece também um botão fora do modelo. A pessoa não deve ter de descobrir um gesto 3D oculto para completar a tarefa.
O objetivo é que a página continue fácil de ler e percorrer, mesmo quando o 3D demora a abrir, não cabe no ecrã ou AR não está disponível. Testa sempre o aparelho mais modesto que pretendes suportar e uma ligação móvel real.
| Desafio | Adaptação prática | Como verificar |
|---|---|---|
| Download lento ou dados móveis limitados | Publica um poster leve; usa loading="lazy" para modelos abaixo da dobra. Mantém loading="eager" apenas para uma demonstração que é o foco imediato. Evita carregar vários visualizadores ao mesmo tempo. | Abre a página com rede limitada e observa o tempo até à imagem e até ao 3D interativo. |
| Memória, GPU e calor | Reduz triângulos, resolução e quantidade de texturas, transparências, materiais e animações. Pausa movimentos quando não são necessários e compara versões comprimidas. | Roda o modelo durante alguns minutos num telemóvel menos potente; procura quebras, aquecimento e recarregamentos. |
| Canvas estreito e diferentes orientações | Usa largura fluida, altura suficiente para o objeto e um camera-orbit que mostre a silhueta completa. Ajusta o enquadramento após testar retrato e paisagem; afasta botões das bordas do ecrã. | Testa ecrãs pequenos, rotação do aparelho, texto ampliado e zoom da página. |
| Conflito entre rodar e fazer scroll | Usa touch-action="pan-y"; mantém o scroll vertical da página. Explica que o arrasto horizontal roda o objeto e oferece controlos HTML para ações importantes. | Percorre a apostila com um dedo a começar sobre o modelo; confirma que não ficas preso no canvas. |
| Alvos de toque pequenos | Dá espaço aos botões, mantém rótulos visíveis e evita ações escondidas num hotspot minúsculo. O modelo não deve ser a única forma de chegar à informação. | Usa só uma mão e testa com aumento de texto e leitor de ecrã. |
| Movimento e bateria | Respeita prefers-reduced-motion; evita rotação automática incessante e animação ligada ao scroll quando a pessoa pede menos movimento. | Ativa a preferência de movimento reduzido no sistema e volta a abrir a página. |
| AR diferente entre Android e iPhone | Oferece primeiro o 3D na página. Testa Scene Viewer, WebXR e Quick Look separadamente; para iOS, fornece ios-src se precisares de controlar o USDZ. Não pressuponhas que botões HTML aparecem no modo AR nativo. | Experimenta os modos em aparelhos reais e confirma escala, orientação, materiais e regresso à página. |
| Falha de rede ou bloqueio do GLB | Mostra estado de carregamento, mensagem de erro e uma imagem/texto alternativo. Publica por HTTPS ou testa num servidor HTTP local; file:// pode bloquear o GLB. | Desliga a rede, erra deliberadamente o caminho do ficheiro e confirma que a pessoa recebe uma explicação. |
Cria uma página de apresentação de um personagem. A experiência deve ser compreensível mesmo antes do GLB carregar.
poster e um texto alt descritivo.model-viewer com camera-controls e touch-action="pan-y"; ajusta o enquadramento em retrato e paisagem.load.Próximo passo: se o projeto exigir que olhos, cabeça ou braços acompanhem continuamente o cursor, leva o mesmo GLB para uma cena Three.js ou React Three Fiber. A preparação do asset e as decisões de acessibilidade continuam a ser úteis.
src, atributos, propriedades e eventos.file:// pode bloquear recursos.Os modelos adicionais vêm do catálogo oficial de exemplos glTF da Khronos. A biblioteca incluída localmente é o model-viewer 4.4.0, sob Apache 2.0; a licença e a origem do ficheiro estão em js/vendor.
Documentação consultada em 10 de outubro de 2026. A API e os modos de AR podem evoluir; verifica a referência oficial antes de publicar.