KTX2 na prática: o jeito certo de comprimir texturas
No artigo anterior, expliquei a relação entre formatos de textura GPU, Basis Universal e KTX2. Agora que a teoria está clara, este é todo prático: como escolher entre ETC1S e UASTC, quais ferramentas usar, quais comandos executar e como carregar nos engines.
Pode seguir passo a passo, lendo e fazendo.
A decisão mais importante primeiro: ETC1S ou UASTC
O Basis oferece duas codificações intermediárias. Escolher errado não é questão de "não ficar tão bom" — é o normal map que fica borrado de vez. Guarde esta tabela:
| ETC1S | UASTC | |
|---|---|---|
| Taxa de compressão | Altíssima (tipo JPG) | Média (tipo PNG de alta qualidade) |
| Qualidade | Suficiente para texturas de cor | Próxima à qualidade original |
| VRAM (após transcodificação) | Geralmente 4bpp (~1/8 do original) | Geralmente 8bpp (~1/4 do original) |
| Velocidade de codificação | Lenta (níveis ajustáveis) | Rápida |
| Uso ideal | albedo/difusa, emissive | normal, metalness-roughness, texturas de dados |
| Não usar para | normal, imagens que exigem valores precisos | texturas de cor (matar formiga com canhão, volume maior) |
Por que normal maps não podem usar ETC1S? Porque normal maps armazenam vetores de direção, e os três canais RGB de cada pixel são interdependentes (comprimento do vetor ≈ 1). ETC1S é uma compressão por blocos projetada para "cores que parecem corretas" — ela não é sensível à precisão de um único canal. Após a compressão, a direção do vetor fica distorcida e a iluminação fica errada na hora — especialmente em brilhos especulares e detalhes de alta frequência. UASTC preserva melhor os valores e aguenta esse nível de precisão.
Regra prática:
- Texturas de cor (albedo, emissive) → ETC1S
- Texturas de dados (normal, roughness, metallic, AO, thickness) → UASTC
- Em dúvida e quer economizar espaço → tente ETC1S primeiro; se borrar em close-ups, troque para UASTC
A mesma imagem, quatro formatos comparados
Usando uma textura albedo 2048×2048 como referência (valores típicos da comunidade, apenas para referência):
| Formato | Tamanho em disco | VRAM (com mipmaps) | Upload para GPU | Multiplataforma |
|---|---|---|---|---|
| PNG | ~5MB | ~22MB | Lento | ✅ |
| WebP | ~1MB | ~22MB | Lento | ✅ |
| KTX2 (ETC1S) | ~0,5-0,8MB | ~2,8MB | Rápido | ✅ |
| KTX2 (UASTC) | ~3-4MB | ~5,6MB | Rápido | ✅ |
Repare que o WebP ocupa a mesma VRAM que o PNG — ele só é menor no disco; ao ir para a VRAM, é descomprimido para pixels originais. O KTX2 com ETC1S reduz disco e VRAM ao mesmo tempo — é aí que está seu valor.
Ferramentas: três caminhos possíveis
Existem várias ferramentas para comprimir KTX2. Ordenadas por "facilidade de uso":
1. toktx (oficial, mais poderoso)
Ferramenta oficial do Khronos, com mais parâmetros, ideal para processar texturas individualmente.
# Converter PNG para KTX2 com codificação ETC1S
toktx --bcmp --uastc 0 albedo.ktx2 albedo.png
# Converter PNG para KTX2 com codificação UASTC
toktx --uastc 1 normal.ktx2 normal.png
Parâmetros comuns:
# ETC1S + nível de qualidade (1-255, padrão 128; quanto maior, melhor qualidade e maior volume)
toktx --bcmp --uastc 0 --qlevel 200 albedo.ktx2 albedo.png
# UASTC + supercompressão (Zstandard, reduz ainda mais o tamanho em disco)
toktx --uastc 1 --zcmp 19 normal.ktx2 normal.png
# Gerar mipmaps automaticamente (altamente recomendado)
toktx --bcmp --genmipmap albedo.ktx2 albedo.png
# Especificar espaço de cor sRGB (obrigatório para texturas de cor)
toktx --bcmp --srgb albedo.ktx2 albedo.png
--bcmpé o interruptor do modo ETC1S (modo base do Basis Universal);--uastc 1é o modo UASTC. Os dois são mutuamente exclusivos.
2. gltf-transform (mais prático, altamente recomendado)
Se você tem um modelo glTF/GLB inteiro, use gltf-transform com um único comando para converter todas as texturas para KTX2, escolhendo automaticamente ETC1S/UASTC conforme o uso de cada textura.
# Instalação
npm install -g @gltf-transform/cli
# Comprimir o modelo inteiro com um comando
gltf-transform optimize model.glb model-optimized.glb \
--texture-compress basisu
Ele detecta internamente o tipo de cada textura: cores usam ETC1S, dados usam UASTC, e escreve automaticamente a extensão KHR_texture_basisu. Esta é a melhor escolha para 90% dos casos — sem precisar ficar digitando toktx manualmente.
3. Ferramentas online (mais rápidas para começar)
Sem querer instalar nada? Use ferramentas de navegador: gltf.report (versão online do gltf-transform), KTX2 Converter, entre outras. Envie, baixe, pronto. Ideais para testes iniciais ou tarefas pontuais.
Pipeline completo: do arquivo fonte à produção
Organizando o fluxo padrão (fluxograma):
Arquivo fonte (PNG/JPG/PSD/TGA)
│
├── [Modelo inteiro] gltf-transform optimize model.glb → detecta tipo de textura automaticamente
│ └─ Textura de cor → ETC1S
│ └─ Textura de dados → UASTC
│ └─ Escreve extensão KHR_texture_basisu
│
└── [Textura individual] toktx → especifica manualmente ETC1S/UASTC + espaço de cor + mipmaps
│
▼
KTX2 / GLB comprimido
│
▼
Carregamento no engine (Three.js / Babylon.js) ── transcodificação em runtime → formato nativo da GPU
Carregando KTX2 no Three.js
O Three.js suporta KTX2 nativamente desde a r129: conecte o KTX2Loader (especificando o caminho do wasm do basis transcoder e detectando a capacidade da GPU) ao GLTFLoader. A partir daí, carregar glTF com texturas KTX2 transcodifica automaticamente.
const ktx2Loader = new KTX2Loader().setTranscoderPath("/basis/").detectSupport(renderer);
gltfLoader.setKTX2Loader(ktx2Loader); // Se o modelo também usar Draco/MeshOpt, lembre-se de conectar DRACOLoader / MeshoptDecoder também
detectSupport(renderer) é obrigatório — ele decide para qual formato nativo a transcodificação será feita em runtime. Além disso, texturas de cor precisam de texture.colorSpace = THREE.SRGBColorSpace; esquecer isso é o clássico problema de "imagem toda acinzentada" (texturas de dados como normal/roughness permanecem lineares).
Carregando KTX2 no Babylon.js
O Babylon é mais tranquilo — o GLTFFileLoader já ativa KHR_texture_basisu por padrão e baixa automaticamente o basis transcoder do CDN. Carregar glTF com KTX2 funciona out-of-the-box. Em ambientes offline/intranet, é necessário configurar manualmente o BASISFileLoader.TranscoderModule.
Ajuste de parâmetros de compressão: equilíbrio entre qualidade e volume
O parâmetro central do ETC1S é --qlevel (1-255). Como ele afeta o resultado:
| qlevel | Volume | Qualidade | Tempo de codificação | Uso ideal |
|---|---|---|---|---|
| 128 (padrão) | Pequeno | Suficiente | Médio | Maioria dos casos |
| 200-255 | Maior | Quase sem perdas | Longo (várias vezes) | Requisitos altos de qualidade |
| 60-100 | Muito pequeno | Blocos visíveis | Rápido | Cenas distantes / texturas pequenas |
O volume do UASTC é relativamente fixo; o ajuste principal é via --zcmp (supercompressão Zstandard) para reduzir o tamanho em disco, sem afetar a VRAM (após descompressão, continua 8bpp).
Ordem de ajuste sugerida:
- Comprima primeiro com os parâmetros padrão e avalie volume e qualidade
- Insatisfeito → ajuste
--qlevel(ETC1S) ou adicione--zcmp(UASTC) - Normal map borrado → confirme que está usando UASTC, não ETC1S
- Cores escuras → verifique a configuração de espaço de cor (flag sRGB, colorSpace no engine)
Solução de problemas comuns
Falha na transcodificação / erro ao carregar
- Verifique se o caminho do wasm do transcoder está correto (no Three.js, precisa de
setTranscoderPath) - Verifique se a versão do engine suporta a versão atual do KTX2 (codificações basis antigas não são suportadas por transcoders novos)
- O console geralmente mostra o erro específico; pesquise pela palavra-chave
Cores escuras ou claras demais
- Textura de cor (albedo) sem sRGB, ou configurada ao contrário
- Esqueceu o
--srgbno toktx (texturas de cor precisam dele) - Textura de dados (normal) com sRGB aplicado por engano
Mipmaps ausentes, cintilação à distância
- Não usou
--genmipmapna compressão texture.generateMipmapsdesativado no engine (no Three.js, KTX2 vem com mipmaps no arquivo, mas ominFilterdo material ainda precisa estar no modo com mipmap)
Direção do normal errada em close-ups
- Normal map usado com ETC1S; troque para UASTC
- Confirme se o normal map é estilo OpenGL (canal G apontando para cima); em alguns engines, o estilo DirectX exige inverter o canal G
Arquivo ficou maior
- Texturas pequenas (< 128×128) não compensam KTX2 — a compressão por blocos tem custo fixo e preenche blocos inteiros
- Texturas de cor sólida não devem ser comprimidas em KTX2; use diretamente o valor de cor do material
Próximos passos
Com isso, você dominou a compressão de texturas. Mas "saber usar a ferramenta" não é o mesmo que "saber usar a ferramenta certa" — no próximo artigo, vou consolidar todo o conhecimento dos 4 artigos anteriores em um framework de seleção: desktop, mobile, VR e mini-apps, qual combinação usar em cada cenário.