Kris

KTX2 na prática: o jeito certo de comprimir texturas

Compressão 3DCompressão de texturaKTX2Basis UniversalglTF

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:

ETC1SUASTC
Taxa de compressãoAltíssima (tipo JPG)Média (tipo PNG de alta qualidade)
QualidadeSuficiente para texturas de corPróxima à qualidade original
VRAM (após transcodificação)Geralmente 4bpp (~1/8 do original)Geralmente 8bpp (~1/4 do original)
Velocidade de codificaçãoLenta (níveis ajustáveis)Rápida
Uso idealalbedo/difusa, emissivenormal, metalness-roughness, texturas de dados
Não usar paranormal, imagens que exigem valores precisostexturas 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):

FormatoTamanho em discoVRAM (com mipmaps)Upload para GPUMultiplataforma
PNG~5MB~22MBLento
WebP~1MB~22MBLento
KTX2 (ETC1S)~0,5-0,8MB~2,8MBRápido
KTX2 (UASTC)~3-4MB~5,6MBRá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:

qlevelVolumeQualidadeTempo de codificaçãoUso ideal
128 (padrão)PequenoSuficienteMédioMaioria dos casos
200-255MaiorQuase sem perdasLongo (várias vezes)Requisitos altos de qualidade
60-100Muito pequenoBlocos visíveisRápidoCenas 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:

  1. Comprima primeiro com os parâmetros padrão e avalie volume e qualidade
  2. Insatisfeito → ajuste --qlevel (ETC1S) ou adicione --zcmp (UASTC)
  3. Normal map borrado → confirme que está usando UASTC, não ETC1S
  4. 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 --srgb no toktx (texturas de cor precisam dele)
  • Textura de dados (normal) com sRGB aplicado por engano

Mipmaps ausentes, cintilação à distância

  • Não usou --genmipmap na compressão
  • texture.generateMipmaps desativado no engine (no Three.js, KTX2 vem com mipmaps no arquivo, mas o minFilter do 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.

Apoie-nos