Any3DAny3D
·Any3D Team

Afinando um Modelo, Lição Um: As Três Armas da Compressão de Vértices

3d-compressionvertex-compressionmeshoptdracogltf

No artigo anterior, abrimos um arquivo GLB e vimos que as texturas consomem 80% do tamanho, enquanto os vértices ocupam apenas 10-20%. Então a compressão de vértices é irrelevante?

Bem pelo contrário. Quando as texturas de um modelo já estão comprimidas em KTX2 e os vértices são densos, os 20% restantes são vértices—e esses 20% podem ser cortados pela metade, ou até 90%. Mais importante ainda, a compressão de vértices é uma das poucas otimizações que é quase gratuita e funciona instantaneamente: adicione alguns comandos, troque um decoder e o arquivo fica mais leve.

Este artigo esclarece três pontos: como os dados de vértices realmente se parecem; o temperamento de cada uma das três abordagens (quantização, MeshOpt, Draco); e uma conclusão que o salvará de armadilhas—não existe solução "melhor", apenas a solução "mais adequada".

Quão grande é um vértice

Primeiro, o que há dentro de um vértice. No glTF, cada vértice é composto por vários atributos:

AtributoFunçãoPrecisão padrãoBytes por vértice
positionCoordenadas do vértice no espaço3 × float3212
normalDetermina a direção da iluminação3 × float3212
tangentCálculo do normal-map4 × float3216
texcoord_0 (UV)Coordenadas de amostragem de textura2 × float328
color (vertex color)Sombreamento por vértice4 × float3216

Um vértice com o conjunto completo de atributos PBR ocupa 48-64 bytes apenas para dados geométricos. Um modelo com 100.000 vértices tem 5-6MB só em vértices.

Note que quase tudo aqui usa float32 (floats de 32 bits). Esse é o padrão, e também é a superfície de ataque para a compressão de vértices—porque a grande maioria dos atributos simplesmente não precisa de precisão de 32 bits.

Arma um: Quantização

Quantização é o princípio fundamental de toda compressão de vértices; Draco e MeshOpt também a usam internamente.

Quantização (mapear floats de alta precisão para inteiros de baixa precisão) se resume a isto: para o float 3.14159265, lembrar 3.14 é suficiente. Para um conjunto de coordenadas dentro de um espaço, em vez de registrar cada casa decimal com precisão em 32 bits, você usa um inteiro com menor amplitude para representá-lo.

Original:  position.x = 1.234567   (float32, 4 bytes)
Quantizado: position.x = 1234       (int16,   2 bytes)  + uma escala/deslocamento para restaurar

Antes vs depois da quantização:

Atributobytes float32Quantizado (16-bit)Economia
position12650%
normal126 (ou 4, usando int8 + octaedral)50-67%
tangent164-850-75%
texcoord8450%

Para aquele vértice de 48-64 bytes, a quantização basicamente o comprime para 16-24 bytes, mais da metade do tamanho.

Quando usar quantização

  • Você quer apenas reduzir o tamanho e não precisa da máxima taxa de compressão
  • Você quer zero dependências de decoder—um glTF quantizado usa a extensão padrão KHR_mesh_quantization, suportada nativamente pelos principais engines, sem necessidade de trazer uma biblioteca decoder extra
  • Sua plataforma alvo é sensível ao tamanho do pacote (por exemplo, Mini Programas do WeChat, onde incluir um decoder Draco custa dezenas de KB)

Quando não usar

  • O modelo é minúsculo e o detalhe é o diferencial (por exemplo, peças industriais com precisão milimétrica). A quantização se trai mais em modelos pequenos—texturas podem estar ok, mas um deslocamento de vértice de 0.1mm é visível em close-up.

O custo real da perda de precisão: uma cena de vitrine de joias quantizou um modelo de anel para 16 bits, e a borda metálica mostrou aliasing em close-ups. A causa não foi poucos vértices; a escala no espaço global era pequena demais para inteiros de 16 bits expressarem com precisão suficiente. A correção é reduzir a faixa de quantização (diminuir o bounding box de position) ou aumentar a profundidade de bits para modelos pequenos.

Arma dois: MeshOpt

MeshOpt é a extensão oficial do glTF EXT_meshopt_compression, posicionada como "compressão razoável, decodificação ultrarrápida."

O que ele faz: primeiro quantiza os atributos (igual ao acima), depois aplica uma técnica chamada codificação entrópica (sem perdas) para recomprimir lossless os inteiros quantizados. Ou seja: quantização com perdas + codificação entrópica sem perdas = tamanho menor, mesma qualidade da quantização isolada.

  • Taxa de compressão: outros 30-50% menores que a quantização isolada
  • Velocidade de decodificação: extremamente rápida, C/JS puro, dezenas de milhões de vértices por segundo em uma thread
  • Tamanho do decoder: minúsculo (~20-30KB gzipped)
  • Compatibilidade: suportado nativamente por Three.js e Babylon.js, um padrão web de facto

Quando usar MeshOpt

  • Você precisa de uma maior taxa de compressão mas não pode aceitar a decodificação mais lenta do Draco
  • Web primeiro, mobile, WebXR—velocidade de decodificação afeta diretamente a experiência do primeiro carregamento
  • O modelo é descomprimido frequentemente (por exemplo, níveis carregados dinamicamente)

Quando não usar

  • Sua plataforma alvo não reconhece EXT_meshopt_compression (raro, engines antigas)
  • Você só precisa que "funcione" e não se preocupa com uma diferença de 30%—nesse caso, a quantização simples é mais fácil e tem uma dependência a menos

Arma três: Draco

Draco é a solução de compressão da Google, posicionada para "taxa máxima de compressão."

A diferença fundamental dos outros dois: Draco altera a conectividade (topologia) dos vértices. A quantização apenas muda a representação numérica de cada vértice; MeshOpt adiciona codificação sem perdas por cima; Draco reorganiza a malha de triângulos e expressa "quais vértices formam triângulos" de forma mais compacta.

  • Taxa de compressão: a maior das três, frequentemente redução de 90%+ em modelos com muitos vértices
  • Velocidade de decodificação: a mais lenta das três, mas ainda assim rápida em termos absolutos
  • Tamanho do decoder: maior (~100-200KB, geralmente carregado como wasm separado)
  • Qualidade: ajustável, mas em taxas extremas você verá deformação visível

Quando usar Draco

  • Modelos extremamente grandes, com super alta densidade de vértices (escaneamentos com milhões de vértices, terreno)
  • Carregamento único, reutilizado por muito tempo após decodificação (decodificação mais lenta é aceitável)
  • Tamanho do pacote não é o gargalo, velocidade de download é

Quando não usar

  • Mobile + precisa de primeiro carregamento rápido—você tem que baixar tanto o decoder quanto o modelo, o que torna tudo mais lento
  • Ambientes com tamanho de pacote rígido, como Mini Programas
  • Modelos que precisam de animação esquelética, morph targets—o suporte do Draco para esses é fraco, e configuração incorreta causa problemas

Todos os três lado a lado: uma tabela de seleção

As taxas de compressão abaixo referenciam benchmarks da comunidade (testes do DeepKolos + discussões no Reddit r/threejs). Modelos diferentes variam, mas os relacionamentos relativos são estáveis:

OpçãoTaxa de compressão (vs float32)Velocidade de decodificaçãoTamanho do decoderCom perdas?Extensão glTF
Quantização simples~50%Nativo, sem decodificação0Sim (precisão)KHR_mesh_quantization
MeshOpt~25-35%Extremamente rápida~25KBSim (precisão)EXT_meshopt_compression
Draco~10-20%Rápida (mais lenta das três)~100-200KBSim (precisão + topologia)KHR_draco_mesh_compression

Decoders e compatibilidade de plataforma:

PlataformaQuantização simplesMeshOptDraco
Web desktop✅ Nativo✅ Nativo✅ Precisa de configuração do decoder
Web mobile✅ Nativo✅ Nativo⚠️ Decoder é pesado
WebXR/VR✅ Nativo✅ Recomendado⚠️ Use com cautela
Mini Programa WeChat✅ Recomendado✅ Recomendado❌ Evite se possível

Resumo em uma linha: quer algo fácil e sem dependências → quantização simples; quer equilíbrio → MeshOpt; quer taxa máxima e pode esperar → Draco.

Na prática: quantização e MeshOpt com gltfpack

gltfpack é a ferramenta oficial do glTF; um único comando cuida da quantização e MeshOpt.

Primeiro instale (binários nos releases do gltfpack):

# Quantize model.glb to 16-bit and add MeshOpt compression
gltfpack -i model.glb -o model-packed.glb -cc

# -cc = compress (adds EXT_meshopt_compression on top of default quantization)

Parâmetros comuns:

# Quantize only, no MeshOpt (lightest, zero decoder dependency)
# gltfpack quantizes vertices to 16-bit (KHR_mesh_quantization) by default,
# so no extra flag is needed
gltfpack -i model.glb -o model-quant.glb

# Quantize and enable MeshOpt
gltfpack -i model.glb -o model-meshopt.glb -cc

# With very many vertices, you can also simplify (reduces vertex count, alters the model)
gltfpack -i model.glb -o model-simplify.glb -cc -si 0.5
# -si 0.5 means simplify to roughly 50% of vertices

Sobre -cc: é o botão de "comprimir" que aplica adicionalmente EXT_meshopt_compression. Sem -cc, o gltfpack ainda quantiza por padrão—o que significa que gltfpack -i in.glb -o out.glb por si só já é "quantização simples, zero dependência de decoder." (-v é a flag de log verboso—não confunda.)

Resultados típicos (um modelo PBR de 5MB com 120k vértices, apenas para referência):

TratamentoTamanho do arquivoObservações
Original (float32)5.0MBLinha de base
Quantização simples (padrão)2.6MBMetade, sem diferença visível
MeshOpt (-cc)1.7MBMais 35% de redução, carregamento um pouco mais rápido

Nota: a simplificação com -si é uma operação com perdas que altera a geometria; não é a mesma coisa que compressão. Compressão tenta preservar a fidelidade visual; simplificação ativamente remove detalhes. As duas podem ser combinadas, mas depende se a cena permite.

Armadilhas comuns

  • Direção da normal alterada após quantização: geralmente precisão muito baixa. Use pelo menos 16 bits para normais, ou codificação octaedral de 8 bits.
  • Materiais perdidos após decodificação Draco: Draco comprime apenas malhas; materiais e texturas devem ser tratados separadamente. Na hora do carregamento, você deve configurar tanto o decoder Draco quanto as extensões KHR.
  • Draco não carrega em Mini Programas: o decoder wasm é restrito em alguns runtimes; mudar para MeshOpt geralmente resolve.
  • Modelo "desloca" após quantização: quando o modelo está longe da origem, a precisão de 16 bits não consegue expressar coordenadas grandes e detalhes pequenos ao mesmo tempo. A correção é mover o modelo para perto da origem antes de quantizar, ou aumentar a profundidade de bits.

Próximo passo

Vértices comprimidos—não comemore cedo demais. Conforme mencionado, texturas são 80% do tamanho do modelo. A seguir, mudamos de campo e examinamos por que PNG/JPG tradicional é um "voraz de VRAM" aos olhos da GPU, e como os formatos de textura nativos da GPU resolvem isso.

Apoie-nos