Any3DAny3D
·Any3D Team

KTX2 in pratica: come usare correttamente la compressione delle texture

3d-compressiontexture-compressionktx2basis-universalgltf

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:

ETC1SUASTC
Rapporto di compressioneMolto alto (tipo JPG)Medio (tipo PNG alta qualità)
QualitàSufficiente per color mapQuasi qualità originale
VRAM (dopo transcode)Di solito 4bpp (~1/8 originale)Di solito 8bpp (~1/4 originale)
Velocità di codificaLenta (livello regolabile)Più veloce
Usoalbedo/diffuse, emissivenormal, metalness-roughness, data map
Non usarenormal, immagini con valori esattiColor 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):

FormatoDim. su discoUso VRAM (con mipmap)Upload alla GPUMultipiattaforma
PNG~5 MB~22 MBLento
WebP~1 MB~22 MBLento
KTX2 (ETC1S)~0.5-0.8 MB~2.8 MBVeloce
KTX2 (UASTC)~3-4 MB~5.6 MBVeloce

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 1 la 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 runtime
  • setTranscoderPath punta 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 = SRGBColorSpace su una color map rende tutta l'immagine grigia e scura. Le data map (normal/roughness) invece restano NoColorSpace (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:

qlevelDimensioneQualitàTempo di codificaUso
128 (predefinito)PiccolaSufficienteMedioLa maggior parte dei casi
200-255Più grandeQuasi senza perditaLungo (più volte)Requisiti di alta qualità
60-100Molto piccolaBlocchi visibiliVeloceDistanza/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:

  1. Prima comprimi una volta con i valori predefiniti e controlla dimensione e qualità
  2. Insoddisfatto → regola --qlevel (ETC1S) o aggiungi --zcmp (UASTC)
  3. Normal map papposa → conferma di usare UASTC, non ETC1S
  4. 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.generateMipmaps non è attivato (in Three.js, KTX2 segue il file per impostazione predefinita, ma il minFilter del 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.

Supportaci