Kris

KTX2 in pratica: il modo giusto per comprimere le texture

Compressione 3DCompressione textureKTX2Basis UniversalglTF

Nel post precedente abbiamo chiarito la relazione tra formati GPU, Basis Universal e KTX2. Ora che la teoria è chiara, questo è tutto pratica: come scegliere tra ETC1S e UASTC, quali strumenti usare, quali comandi digitare e come caricare il tutto nel motore.

Puoi seguire passo passo, copiando direttamente.

La scelta più importante: ETC1S o UASTC

Basis offre due codifiche intermedie. Scegliere quella sbagliata non è un problema di "non abbastanza buono", ma di normal map che si sfocano direttamente. Memorizza questa tabella:

ETC1SUASTC
Rapporto di compressioneMolto alto (simile a JPG)Medio (simile a PNG di alta qualità)
Qualità immagineSufficiente per texture di coloreVicina alla qualità originale
Memoria GPU (dopo transcodifica)Tipicamente 4bpp (circa 1/8 dell'originale)Tipicamente 8bpp (circa 1/4 dell'originale)
Velocità di codificaLenta (livelli regolabili)Abbastanza veloce
Ideale peralbedo/diffuse, emissivenormal, metalness-roughness, texture dati
Non adatta pernormal, immagini che richiedono valori precisiTexture di colore (sprecata, volume maggiore)

Perché le normal map non possono usare ETC1S? Perché una normal map memorizza vettori di direzione, dove i tre canali RGB di ogni pixel sono vincolati tra loro (lunghezza del vettore ≈ 1). ETC1S è una compressione a blocchi progettata per "far sembrare giusti i colori": non è sensibile alla precisione dei singoli canali, quindi dopo la compressione la direzione del vettore si sposta e l'illuminazione appare subito sbagliata — soprattutto nelle zone di specularità e nei dettagli ad alta frequenza. UASTC preserva meglio i valori numerici e regge questo livello di precisione.

Regola pratica:

  • Texture di colore (albedo, emissive) → ETC1S
  • Texture dati (normal, roughness, metallic, AO, thickness) → UASTC
  • Se sei indeciso e vuoi risparmiare spazio → prova prima ETC1S, se nei close-up vedi sfocature passa a UASTC

La stessa immagine, quattro formati a confronto

Usiamo una texture albedo 2048×2048 come riferimento (dati tipici dalla community, solo indicativi):

FormatoDimensione su discoMemoria GPU (con mipmap)Upload su GPUCross-platform
PNG~5MB~22MBLento
WebP~1MB~22MBLento
KTX2 (ETC1S)~0.5-0.8MB~2.8MBVeloce
KTX2 (UASTC)~3-4MB~5.6MBVeloce

Nota: WebP occupa la stessa memoria GPU del PNG — è solo più piccolo su disco, ma in VRAM viene comunque decompresso in pixel originali. KTX2 con ETC1S riduce drasticamente sia disco che VRAM: è questo il suo valore.

Toolchain: tre strade, tutte percorribili

Gli strumenti per comprimere in KTX2 sono più di uno. Ecco l'ordine per "comodità d'uso":

1. toktx (ufficiale, il più potente)

Lo strumento ufficiale di Khronos, con il maggior numero di parametri. Ideale per elaborare texture singole.

# Converti PNG in KTX2 con codifica ETC1S
toktx --bcmp --uastc 0 albedo.ktx2 albedo.png

# Converti PNG in KTX2 con codifica UASTC
toktx --uastc 1 normal.ktx2 normal.png

Parametri utili:

# ETC1S + livello di qualità (1-255, default 128, più alto = migliore qualità ma file più grande)
toktx --bcmp --uastc 0 --qlevel 200 albedo.ktx2 albedo.png

# UASTC + super-compressione (Zstandard, riduce ulteriormente il peso su disco)
toktx --uastc 1 --zcmp 19 normal.ktx2 normal.png

# Genera automaticamente i mipmap (consigliatissimo)
toktx --bcmp --genmipmap albedo.ktx2 albedo.png

# Specifica lo spazio colore sRGB (obbligatorio per texture di colore)
toktx --bcmp --srgb albedo.ktx2 albedo.png

--bcmp è l'interruttore per la modalità ETC1S (la modalità base di Basis Universal), --uastc 1 è la modalità UASTC. Si escludono a vicenda.

2. gltf-transform (il più semplice, fortemente consigliato)

Se hai un intero modello glTF/GLB, usa gltf-transform con un solo comando per convertire tutte le texture in KTX2, scegliendo automaticamente ETC1S/UASTC in base all'uso.

# Installazione
npm install -g @gltf-transform/cli

# Compressione completa del modello con un comando
gltf-transform optimize model.glb model-optimized.glb \
  --texture-compress basisu

Internamente rileva l'uso di ogni texture: quelle di colore → ETC1S, quelle dati → UASTC, e scrive automaticamente l'estensione KHR_texture_basisu. Questa è la scelta migliore nel 90% dei casi: niente comandi toktx manuali uno per uno.

3. Strumenti online (il modo più rapido per iniziare)

Se non vuoi installare nulla, usa strumenti da browser: gltf.report (versione online di gltf-transform), KTX2 Converter e simili. Carichi, scarichi, fatto. Perfetti per esperimenti iniziali o attività una tantum.

Pipeline completa: dal file sorgente alla pubblicazione

Ecco il flusso di lavoro standard (diagramma):

File sorgente (PNG/JPG/PSD/TGA)
        │
        ├── [Modello intero] gltf-transform optimize model.glb → rileva automaticamente il tipo di texture
        │            └─ Texture di colore → ETC1S
        │            └─ Texture dati → UASTC
        │            └─ Scrive estensione KHR_texture_basisu
        │
        └── [Texture singola] toktx → specifica manualmente ETC1S/UASTC + spazio colore + mipmap
        │
        ▼
KTX2 / GLB compresso
        │
        ▼
Caricamento nel motore (Three.js / Babylon.js) ── transcodifica runtime → formato nativo GPU

Caricare KTX2 in Three.js

Three.js supporta KTX2 nativamente dalla r129: collega KTX2Loader (specificando il percorso del transcoder wasm basis, rilevando le capacità GPU) al GLTFLoader, e il caricamento di glTF con texture KTX2 avverrà con transcodifica automatica.

const ktx2Loader = new KTX2Loader().setTranscoderPath("/basis/").detectSupport(renderer);
gltfLoader.setKTX2Loader(ktx2Loader); // Se il modello usa anche Draco/MeshOpt, collega anche DRACOLoader / MeshoptDecoder

detectSupport(renderer) è obbligatorio: determina in quale formato nativo avverrà la transcodifica runtime. Inoltre, per le texture di colore imposta texture.colorSpace = THREE.SRGBColorSpace; dimenticarlo è il classico errore che rende l'immagine "spenta" (le texture dati come normal/roughness restano in lineare).

Caricare KTX2 in Babylon.js

Babylon è ancora più semplice: GLTFFileLoader abilita KHR_texture_basisu di default e scarica automaticamente il transcoder basis dal CDN. Il caricamento di glTF con KTX2 funziona out-of-the-box. In ambienti offline/Intranet devi configurare manualmente BASISFileLoader.TranscoderModule.

Ottimizzazione dei parametri di compressione: equilibrio tra qualità e peso

Il parametro chiave di ETC1S è --qlevel (1-255). Ecco come influisce sul risultato:

qlevelPesoQualitàTempo di codificaIdeale per
128 (default)PiccoloSufficienteMedioLa maggior parte dei casi
200-255Più grandeQuasi losslessLungo (più volte)Requisiti di alta qualità
60-100Molto piccoloArtefatti a blocchi visibiliVeloceTexture lontane/piccole

UASTC ha un peso relativamente fisso; si regola principalmente con --zcmp (super-compressione Zstandard) per ridurre il peso su disco, senza influire sulla VRAM (dopo la decompressione resta 8bpp).

Ordine di ottimizzazione consigliato:

  1. Comprimi prima con i parametri di default, controlla peso e qualità
  2. Se non sei soddisfatto → regola --qlevel (ETC1S) o aggiungi --zcmp (UASTC)
  3. Normal map sfocata → verifica che sia UASTC, non ETC1S
  4. Colori scuri → controlla le impostazioni dello spazio colore (flag sRGB, colorSpace nel motore)

Risoluzione dei problemi comuni

Transcodifica fallita / errore di caricamento

  • Verifica che il percorso del transcoder wasm sia corretto (in Three.js serve setTranscoderPath)
  • Verifica che la versione del motore supporti la versione corrente di KTX2 (codifiche basis vecchie non sono supportate dai transcoder nuovi)
  • La console di solito mostra l'errore specifico: cerca per parola chiave

Colori troppo scuri / troppo chiari

  • La texture di colore (albedo) non ha sRGB, o è impostato al contrario
  • In toktx manca --srgb (va aggiunto per le texture di colore)
  • La texture dati (normal) ha sRGB applicato per errore

Mipmap mancanti, sfarfallio in lontananza

  • Compressione senza --genmipmap
  • Nel motore texture.generateMipmaps non è attivo (in Three.js i KTX2 seguono il file, ma il minFilter del materiale deve usare la modalità mipmap)

Direzione delle normali sbagliata nei close-up

  • La normal map usa ETC1S: passa a UASTC
  • Verifica che la normal map sia in stile OpenGL (canale verde verso l'alto); lo stile DirectX in alcuni motori richiede l'inversione del canale G

Il file è più grande di prima

  • Per texture piccole (< 128×128) KTX2 non conviene: la compressione a blocchi ha un overhead fisso che riempie l'intero blocco
  • Le texture a tinta unita non vanno compresse in KTX2: usa direttamente il valore colore del materiale

Prossimi passi

Con questo, la compressione delle texture è praticamente completata. Ma "saper usare gli strumenti" non significa "usare lo strumento giusto": nel prossimo post riuniremo tutte le conoscenze dei 4 articoli precedenti in un framework di selezione: desktop, mobile, VR, mini-app — quale combinazione usare per ogni scenario.

Supportaci