KTX2 in pratica: come usare correttamente la compressione delle texture
L'articolo precedente ha districato i formati di texture GPU, Basis Universal e KTX2. La teoria è chiara; questo è tutto pratica: come scegliere ETC1S vs UASTC, quali strumenti, quali comandi, come caricarli in un motore.
Puoi seguire e fare mentre leggi.
Prima, la scelta più importante: ETC1S o UASTC
Basis offre due codifiche intermedie. Scegliere sbagliato non è «non abbastanza buono» — la tua normal map diventa pappa. Memorizza questa tabella:
| ETC1S | UASTC | |
|---|---|---|
| Rapporto di compressione | Molto alto (tipo JPG) | Medio (tipo PNG alta qualità) |
| Qualità | Sufficiente per color map | Quasi qualità originale |
| VRAM (dopo transcode) | Di solito 4bpp (~1/8 originale) | Di solito 8bpp (~1/4 originale) |
| Velocità di codifica | Lenta (livello regolabile) | Più veloce |
| Uso | albedo/diffuse, emissive | normal, metalness-roughness, data map |
| Non usare | normal, immagini con valori esatti | Color map (spreco, dimensioni grandi) |
Perché le normal map non possono usare ETC1S? Perché una normal map memorizza vettori di direzione, e i canali RGB di ciascun pixel si vincolano a vicenda (lunghezza vettore ≈ 1). ETC1S è una compressione a blocchi progettata affinché «i colori sembrino giusti»; è insensibile alla precisione del singolo canale, quindi dopo la compressione la direzione del vettore deriva e l'illuminazione appare subito sbagliata — soprattutto le posizioni dei riflessi e i dettagli ad alta frequenza. UASTC preserva meglio i valori numerici e regge a questa esigenza di precisione.
Regole pratiche:
- Color map (albedo, emissive) → ETC1S
- Data map (normal, roughness, metallic, AO, thickness) → UASTC
- Incerto e vuoi risparmiare dimensione → prova prima ETC1S; se un close-up diventa papposo, passa a UASTC
Una stessa immagine, quattro formati confrontati
Usando una map albedo 2048×2048 come base (valori tipici della comunità, solo riferimento):
| Formato | Dim. su disco | Uso VRAM (con mipmap) | Upload alla GPU | Multipiattaforma |
|---|---|---|---|---|
| PNG | ~5 MB | ~22 MB | Lento | ✅ |
| WebP | ~1 MB | ~22 MB | Lento | ✅ |
| KTX2 (ETC1S) | ~0.5-0.8 MB | ~2.8 MB | Veloce | ✅ |
| KTX2 (UASTC) | ~3-4 MB | ~5.6 MB | Veloce | ✅ |
Nota che l'uso VRAM di WebP è identico a quello di PNG — è solo piccolo su disco; nella VRAM si decomprime comunque in pixel grezzi. L'ETC1S di KTX2 porta disco e VRAM simultaneamente molto in basso — ecco cosa lo rende prezioso.
Toolchain: tre strade portano al risultato
C'è più di uno strumento per comprimere KTX2, ordinati per «facilità di presa in mano»:
1. toktx (ufficiale, il più potente)
Lo strumento ufficiale di Khronos, con i parametri più completi, adatto a texture singole.
# Convertire PNG in un KTX2 con codifica ETC1S
toktx --bcmp --uastc 0 albedo.ktx2 albedo.png
# Convertire PNG in un KTX2 con codifica UASTC
toktx --uastc 1 normal.ktx2 normal.png
Parametri comuni:
# ETC1S + livello di qualità (1-255, predefinito 128; più alto = qualità migliore e dimensione maggiore)
toktx --bcmp --uastc 0 --qlevel 200 albedo.ktx2 albedo.png
# UASTC + supercompressione (Zstandard, riduce ulteriormente la dimensione su disco)
toktx --uastc 1 --zcmp 19 normal.ktx2 normal.png
# Generare mipmap automaticamente (fortemente consigliato)
toktx --bcmp --genmipmap albedo.ktx2 albedo.png
# Specificare lo spazio colore sRGB (obbligatorio per color map)
toktx --bcmp --srgb albedo.ktx2 albedo.png
--bcmpè l'interruttore della modalità ETC1S (modalità base di Basis Universal), e--uastc 1la modalità UASTC. Si escludono a vicenda.
2. gltf-transform (il più comodo, fortemente consigliato)
Se hai un intero modello glTF/GLB, usa gltf-transform per sostituire tutte le sue texture con KTX2 con un comando, scegliendo automaticamente ETC1S/UASTC in base allo scopo della texture.
# Installare
npm install -g @gltf-transform/cli
# Comprimere l'intero modello in un colpo
gltf-transform optimize model.glb model-optimized.glb \
--texture-compress basisu
Valuta internamente lo scopo della texture: tipi colore → ETC1S, tipi dati → UASTC, e scrive l'estensione KHR_texture_basisu automaticamente. Questa è la scelta migliore per il 90% dei casi — niente toktx battuto uno a uno.
3. Strumenti online (avvio più rapido)
Quando non vuoi configurare l'ambiente, usa uno strumento del browser: gltf.report (gltf-transform online), KTX2 Converter, ecc. Carica, scarica, fatto. Buono per esperimenti precoci o attività una tantum.
Pipeline completa: dal sorgente alla produzione
Ecco il flusso standard (diagramma):
File sorgente (PNG/JPG/PSD/TGA)
│
├── [intero modello] gltf-transform optimize model.glb → rileva tipo di texture
│ └─ color map → ETC1S
│ └─ data map → UASTC
│ └─ scrive l'estensione KHR_texture_basisu
│
└── [texture singola] toktx → specificare ETC1S/UASTC + spazio colore + mipmap manualmente
│
▼
KTX2 / GLB compresso
│
▼
Caricamento motore (Three.js / Babylon.js) ── transcodifica runtime → formato nativo GPU
Caricare KTX2 in Three.js
Three.js supporta KTX2 nativamente da r129, ma devi fornire un KTX2Loader e configurare il transcoder (wasm del basis transcoder).
import * as THREE from "three";
import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
import { MeshoptDecoder } from "three/examples/jsm/libs/meshopt_decoder.module.js";
// 1. Inizializzare il transcoder KTX2 e rilevare quale formato nativo supporta la GPU attuale
const ktx2Loader = new KTX2Loader()
.setTranscoderPath("/basis/") // directory del wasm del basis transcoder
.detectSupport(renderer); // va passato il renderer per rilevare le capacità
// 2. Configurare GLTFLoader, attaccando KTX2/Draco/MeshOpt
const gltfLoader = new GLTFLoader();
gltfLoader.setKTX2Loader(ktx2Loader);
gltfLoader.setDRACOLoader(
new DRACOLoader().setDecoderPath("/draco/")
);
gltfLoader.setMeshoptDecoder(MeshoptDecoder);
// 3. Caricare il modello; le texture vengono transcodificate automaticamente
gltfLoader.load("/models/model-optimized.glb", (gltf) => {
scene.add(gltf.scene);
});
Alcuni punti chiave:
detectSupport(renderer)è obbligatorio — decide in quale formato nativo transcodificare a runtimesetTranscoderPathpunta ai file wasm del basis transcoder (copiali dalla release basis_transcoder alla tua directory public)- Se il modello usa anche Draco, ricordati di attaccare anche il DRACOLoader; se usa MeshOpt, attacca MeshoptDecoder
Caricare una singola texture KTX2:
const texture = await ktx2Loader.loadAsync("/textures/albedo.ktx2");
texture.colorSpace = THREE.SRGBColorSpace; // imposta le color map su sRGB
material.map = texture;
La trappola più comune è proprio qui: dimenticare di impostare
colorSpace = SRGBColorSpacesu una color map rende tutta l'immagine grigia e scura. Le data map (normal/roughness) invece restanoNoColorSpace(lineare) — non invertirle.
Caricare KTX2 in Babylon.js
L'approccio di Babylon è simile ma più automatico — KHR_texture_basisu è attivato di default in GLTFFileLoader, purché i file del transcoder possano essere trovati.
import { Scene } from "@babylonjs/core/scene";
import { Engine } from "@babylonjs/core/Engines/engine";
import { SceneLoader } from "@babylonjs/core/Loading/sceneLoader";
import { GLTFFileLoader } from "@babylonjs/loaders/glTF/glTFFileLoader";
import "@babylonjs/loaders/glTF/2.0/Extensions/KHR_texture_basisu";
const engine = new Engine(canvas);
const scene = new Scene(engine);
SceneLoader.ImportMesh(
"",
"/models/",
"model-optimized.glb",
scene,
(meshes) => {
// Modello caricato; KTX2 transcodificato automaticamente
}
);
Babylon recupera automaticamente il basis transcoder da un CDN; per ambienti offline/intranet devi configurare manualmente BASISFileLoader.TranscoderModule.
Regolazione dei parametri di compressione: equilibrio qualità e dimensione
Il parametro centrale di ETC1S è --qlevel (1-255). Come influisce sul risultato:
| qlevel | Dimensione | Qualità | Tempo di codifica | Uso |
|---|---|---|---|---|
| 128 (predefinito) | Piccola | Sufficiente | Medio | La maggior parte dei casi |
| 200-255 | Più grande | Quasi senza perdita | Lungo (più volte) | Requisiti di alta qualità |
| 60-100 | Molto piccola | Blocchi visibili | Veloce | Distanza/texture piccole |
La dimensione di UASTC è relativamente fissa; regoli soprattutto la dimensione su disco con --zcmp (supercompressione Zstandard), che non influisce sulla VRAM (sempre 8bpp dopo decompressione).
Ordine di regolazione consigliato:
- Prima comprimi una volta con i valori predefiniti e controlla dimensione e qualità
- Insoddisfatto → regola
--qlevel(ETC1S) o aggiungi--zcmp(UASTC) - Normal map papposa → conferma di usare UASTC, non ETC1S
- Colori scuri → controlla le impostazioni dello spazio colore (flag sRGB, colorSpace nel motore)
Risoluzione dei problemi comuni
Transcode fallito / errore di caricamento
- Controlla che il percorso del wasm del transcoder sia corretto (Three.js ha bisogno di
setTranscoderPath) - Controlla se la versione del motore supporta l'attuale versione KTX2 (le codifiche Basis più vecchie non sono supportate dai transcoder più nuovi)
- La console di solito ha un errore concreto; cerca per parola chiave
Colori troppo scuri / troppo luminosi
- Una color map (albedo) non è stata impostata su sRGB, o è stata invertita
- In toktx omissione di
--srgb(le color map ne hanno bisogno) - A una data map (normal) è stato dato sRGB per errore
Mipmap mancanti, sfarfallio in lontananza
- La compressione non ha aggiunto
--genmipmap - Nel motore
texture.generateMipmapsnon è attivato (in Three.js, KTX2 segue il file per impostazione predefinita, ma ilminFilterdel materiale necessita comunque di una modalità mipmap)
Direzione della normale errata nel close-up
- La normal map ha usato ETC1S; passa a UASTC
- Conferma che la normal map sia di stile OpenGL (canale verde verso l'alto); lo stile DirectX richiede di invertire il canale G in alcuni motori
Il file è diventato più grande
- Texture piccole (< 128×128) non valgono KTX2 — la compressione a blocchi ha un costo fisso e riempie un intero blocco
- Non comprimere in KTX2 una texture a tinta unita; usare un valore colore del materiale è più economico
Prossimo passo
Con questo la compressione delle texture è sostanzialmente superata. Ma «sapere usare gli strumenti» non è «usare gli strumenti giusti» — il prossimo articolo raccoglie tutta la conoscenza dei quattro precedenti in un quadro di selezione: desktop, mobile, VR, Mini Program — quale combinazione esatta in ogni scenario.