KTX2 in pratica: il modo giusto per comprimere le texture
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:
| ETC1S | UASTC | |
|---|---|---|
| Rapporto di compressione | Molto alto (simile a JPG) | Medio (simile a PNG di alta qualità) |
| Qualità immagine | Sufficiente per texture di colore | Vicina alla qualità originale |
| Memoria GPU (dopo transcodifica) | Tipicamente 4bpp (circa 1/8 dell'originale) | Tipicamente 8bpp (circa 1/4 dell'originale) |
| Velocità di codifica | Lenta (livelli regolabili) | Abbastanza veloce |
| Ideale per | albedo/diffuse, emissive | normal, metalness-roughness, texture dati |
| Non adatta per | normal, immagini che richiedono valori precisi | Texture 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):
| Formato | Dimensione su disco | Memoria GPU (con mipmap) | Upload su GPU | Cross-platform |
|---|---|---|---|---|
| PNG | ~5MB | ~22MB | Lento | ✅ |
| WebP | ~1MB | ~22MB | Lento | ✅ |
| KTX2 (ETC1S) | ~0.5-0.8MB | ~2.8MB | Veloce | ✅ |
| KTX2 (UASTC) | ~3-4MB | ~5.6MB | Veloce | ✅ |
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:
| qlevel | Peso | Qualità | Tempo di codifica | Ideale per |
|---|---|---|---|---|
| 128 (default) | Piccolo | Sufficiente | Medio | La maggior parte dei casi |
| 200-255 | Più grande | Quasi lossless | Lungo (più volte) | Requisiti di alta qualità |
| 60-100 | Molto piccolo | Artefatti a blocchi visibili | Veloce | Texture 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:
- Comprimi prima con i parametri di default, controlla peso e qualità
- Se non sei soddisfatto → regola
--qlevel(ETC1S) o aggiungi--zcmp(UASTC) - Normal map sfocata → verifica che sia UASTC, non ETC1S
- 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.generateMipmapsnon è attivo (in Three.js i KTX2 seguono il file, ma ilminFilterdel 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.