Any3DAny3D
·Any3D Team

Mettere a dieta un modello, lezione 1: le tre armi della compressione dei vertici

3d-compressionvertex-compressionmeshoptdracogltf

Nell'articolo precedente abbiamo aperto un file GLB e visto che le texture divorano l'80% della dimensione, mentre i vertici rappresentano solo il 10-20%. La compressione dei vertici è quindi irrilevante?

Tutt'altro. Quando le texture di un modello sono già compresse in KTX2 e i vertici sono fitti, il restante 20% sono i vertici — e quel 20% può essere dimezzato, o persino ridotto del 90%. Cosa più importante, la compressione dei vertici è una delle poche ottimizzazioni quasi gratuite e immediatamente efficaci: qualche comando, un cambio di decoder, e il file dimagrisce.

Questo articolo chiarisce tre cose: come sono fatti davvero i dati dei vertici; il temperamento di ciascuno dei tre approcci (quantizzazione, MeshOpt, Draco); e una conclusione che vi risparmierà cadute — non esiste la soluzione «migliore», solo la soluzione «più adatta».

Quanto è grande un vertice

Prima, cosa c'è dentro un vertice. In glTF, ogni vertice è composto da più attributi:

AttributoScopoPrecisione predefinitaByte per vertice
position (posizione)Coordinate del vertice nello spazio3 × float3212
normal (normale)Determina la direzione di illuminazione3 × float3212
tangent (tangente)Calcolo delle normal map4 × float3216
texcoord_0 (UV)Coordinate di campionamento texture2 × float328
color (colore vertice)Ombreggiatura per vertice4 × float3216

Un vertice con il set completo di attributi PBR occupa 48-64 byte solo per i dati geometrici. Un modello da 100.000 vertici è 5-6 MB solo di vertici.

Quasi tutto qui usa float32 (virgola mobile a 32 bit). È la configurazione predefinita e anche la superficie di attacco della compressione dei vertici — perché la stragrande maggioranza degli attributi non ha affatto bisogno di precisione a 32 bit.

Arma uno: Quantizzazione

La quantizzazione è il principio sottostante di tutta la compressione dei vertici; Draco e MeshOpt la usano internamente.

Quantizzazione (mappare float ad alta precisione verso interi a bassa precisione) si riduce a questo: per il float 3.14159265, ricordare 3.14 basta. Per un insieme di coordinate in uno spazio, invece di registrare a 32 bit ogni decimale con precisione, si usa un intero di range minore.

Originale:   position.x = 1.234567   (float32, 4 byte)
Quantizzato: position.x = 1234       (int16,   2 byte)  + uno scale/offset per ripristinare

Prima vs. dopo:

Attributobyte float32Quantizzato (16 bit)Risparmio
position12650%
normal126 (o 4, con int8 + octahedral)50-67%
tangent164-850-75%
texcoord8450%

Per quel vertice di 48-64 byte, la quantizzazione lo comprime sostanzialmente a 16-24 byte, più che dimezzandolo.

Quando usare la quantizzazione

  • Vuoi solo ridurre le dimensioni e non ti serve il massimo rapporto di compressione
  • Vuoi zero dipendenze da decoder — un glTF quantizzato usa l'estensione standard KHR_mesh_quantization, supportata nativamente dai motori principali, senza libreria decoder extra
  • La tua piattaforma target è sensibile alla dimensione del pacchetto (es. WeChat Mini Program, dove includere un decoder Draco costa decine di KB)

Quando non usarla

  • Il modello è minuscolo e il dettaglio è il punto di vendita (es. componenti industriali al millimetro). La quantizzazione si tradisce soprattutto sui modelli piccoli — le texture possono reggere, ma uno spostamento di vertice di 0,1 mm è visibile in un close-up.

Il vero costo della perdita di precisione: in una scena di gioielleria, un modello di anello è stato quantizzato a 16 bit e il bordo metallico ha mostrato aliasing nei close-up. La causa non erano pochi vertici; lo spazio mondo era troppo piccolo perché gli interi a 16 bit lo esprimessero abbastanza finemente. La soluzione è ridurre il range di quantizzazione (ridurre il bounding box di position) o aumentare la profondità di bit per i modelli piccoli.

Arma due: MeshOpt

MeshOpt è l'estensione glTF ufficiale EXT_meshopt_compression, posizionata come «buon rapporto di compressione, decodifica fulminea».

Cosa fa: prima quantizza gli attributi (come sopra), poi applica una tecnica chiamata codifica di entropia (lossless, senza perdita) per ricomprimere senza perdita gli interi quantizzati. In altre parole: quantizzazione con perdita + codifica di entropia senza perdita = dimensione minore, qualità identica alla sola quantizzazione.

  • Rapporto di compressione: ancora 30-50% più piccolo della sola quantizzazione
  • Velocità di decodifica: estremamente veloce, puro C/JS, decine di milioni di vertici al secondo su un thread
  • Dimensione del decoder: minuscola (~20-30 KB zippato)
  • Compatibilità: supporto nativo di Three.js e Babylon.js, uno standard de facto del web

Quando usare MeshOpt

  • Ti serve un rapporto di compressione più alto ma non puoi accettare la decodifica più lenta di Draco
  • Web/mobile/WebXR al centro — la velocità di decodifica influisce direttamente sulla prima paint
  • Il modello è decodificato di frequente (es. livelli caricati dinamicamente)

Quando non usarlo

  • La tua piattaforma target non riconosce nemmeno EXT_meshopt_compression (raro, motori vecchi)
  • Ti basta «che giri» e non ti importa del 30% di differenza — allora la quantizzazione pura è più semplice e ha una dipendenza in meno

Arma tre: Draco

Draco è la soluzione di compressione di Google, posizionata per «massimo rapporto di compressione».

La differenza fondamentale con gli altri due: Draco cambia la connettività (topologia) dei vertici. La quantizzazione cambia solo la rappresentazione numerica di ciascun vertice; MeshOpt aggiunge codifica senza perdita; Draco riorganizza la mesh triangolare ed esprime «quali vertici formano triangoli» in modo più compatto.

  • Rapporto di compressione: il più alto dei tre, spesso 90%+ di riduzione su modelli densi di vertici
  • Velocità di decodifica: la più lenta dei tre, ma in assoluto sempre veloce
  • Dimensione del decoder: maggiore (~100-200 KB, di solito caricato come wasm separato)
  • Qualità: regolabile, ma a rapporti estremi si vede deformazione

Quando usare Draco

  • Modelli estremamente grandi e super-densi di vertici (scansioni da milioni di vertici, terreni)
  • Caricamento una tantum, lungo riutilizzo dopo la decodifica (decodifica più lenta accettabile)
  • La dimensione del pacchetto non è il collo di bottiglia, la velocità di download sì

Quando non usarlo

  • Mobile + prima paint veloce necessaria — bisogna scaricare sia decoder che modello, il che frena
  • Ambienti severi sulla dimensione come Mini Program
  • Modelli che necessitano di animazione scheletrica, morph target — il supporto di Draco per questi è debole, la cattiva configurazione causa problemi

I tre affiancati: una tabella di selezione

I rapporti di compressione sotto referenziano benchmark di comunità (test di DeepKolos + discussioni Reddit r/threejs). I modelli variano, ma le relazioni relative sono stabili:

OpzioneRapporto compressione (vs float32)Velocità decodeDim. decoderCon perdita?Estensione glTF
Quantizzazione pura~50%Nativa, nessun decode0Sì (precisione)KHR_mesh_quantization
MeshOpt~25-35%Estremamente veloce~25 KBSì (precisione)EXT_meshopt_compression
Draco~10-20%Veloce (la più lenta dei tre)~100-200 KBSì (precisione + topologia)KHR_draco_mesh_compression

Decoder e compatibilità di piattaforma:

PiattaformaQuantizzazione puraMeshOptDraco
Web desktop✅ Nativo✅ Nativo✅ Serve config decoder
Web mobile✅ Nativo✅ Nativo⚠️ Decoder pesante
WebXR/VR✅ Nativo✅ Consigliato⚠️ Con cautela
WeChat Mini Program✅ Consigliato✅ Consigliato❌ Evitare se possibile

In una riga: facile e senza dipendenze → quantizzazione pura; equilibrato → MeshOpt; massimo rapporto e si può attendere → Draco.

Pratica: quantizzazione e MeshOpt con gltfpack

gltfpack è lo strumento glTF ufficiale; un comando gestisce quantizzazione e MeshOpt.

Prima installa (binari dalle release di gltfpack):

# Quantizzare model.glb a 16 bit e aggiungere la compressione MeshOpt
gltfpack -i model.glb -o model-packed.glb -cc

# -cc = compress (aggiunge EXT_meshopt_compression sopra la quantizzazione predefinita)

Parametri comuni:

# Solo quantizzazione, senza MeshOpt (il più leggero, zero dipendenze decoder)
# gltfpack quantizza i vertici a 16 bit (KHR_mesh_quantization) per impostazione predefinita,
# non serve flag extra
gltfpack -i model.glb -o model-quant.glb

# Quantizzare e attivare MeshOpt
gltfpack -i model.glb -o model-meshopt.glb -cc

# Con tantissimi vertici, puoi anche semplificare (riduce il n. di vertici, altera il modello)
gltfpack -i model.glb -o model-simplify.glb -cc -si 0.5
# -si 0.5 significa semplificare a circa il 50% dei vertici

Su -cc: è il interruttore «compress» che applica in aggiunta EXT_meshopt_compression.Senza -cc, gltfpack quantizza comunque per impostazione predefinita — cioè gltfpack -i in.glb -o out.glb da solo è già «quantizzazione pura, zero dipendenze decoder». (-v è il flag di log verboso — non confonderli.)

Risultati tipici (un modello PBR di 5 MB, 120k vertici, solo riferimento):

TrattamentoDim. fileNote
Originale (float32)5.0 MBBase
Quantizzazione pura (predefinito)2.6 MBDimezzato, nessuna differenza visibile
MeshOpt (-cc)1.7 MBAltro 35% risparmiato, caricamento un po' più rapido

Nota: la semplificazione -si è un operazione con perdita che altera la geometria; non è la stessa cosa della compressione. La compressione cerca di preservare la fedeltà visiva; la semplificazione rimuove attivamente dettagli. I due si possono impilare, ma dipende dallo scenario.

Trappole comuni

  • Direzione delle normali cambiata dopo la quantizzazione: di solito precisione troppo bassa. Usa almeno 16 bit per le normali, o codifica ottaedrica a 8 bit.
  • Materiali persi dopo la decodifica Draco: Draco comprime solo le mesh; i materiali e le texture vanno gestiti separatamente. Al caricamento vanno configurati sia il decoder Draco sia le estensioni KHR.
  • Draco non si carica in un Mini Program: il wasm del decoder è limitato in alcuni runtime; passare a MeshOpt di solito risolve.
  • Il modello «deriva» dopo la quantizzazione: quando il modello è lontano dall'origine, la precisione a 16 bit non può esprimere sia coordinate grandi che dettagli piccoli. Soluzione: spostare il modello vicino all'origine prima di quantizzare, o aumentare la profondità di bit.

Prossimo passo

I vertici sono compressi — non festeggiare troppo presto. Come detto, le texture sono l'80% della dimensione del modello. Ora cambiamo campo di battaglia e vediamo perché il PNG/JPG tradizionale è un «ingordo di VRAM» agli occhi della GPU, e come i formati di texture nativi GPU lo risolvono.

Supportaci