Impacchettamento atlanti sprite: margini, potenze di due e contaminazione
Un atlante di sprite sembra un’idea semplice: mettere tutti gli sprite su una grande texture anziché in tanti file piccoli. Ma i dettagli del loro impacchettamento determinano se il gioco verrà renderizzato correttamente o svilupperà sottili aloni colorati su ogni bordo. Ecco cosa fa davvero un atlante, perché i bordi contaminano quelli vicini e quali impostazioni specifiche impediscono il problema.
Perché esistono gli atlanti: chiamate di disegno
Ogni volta che una GPU passa dal disegnare con una texture a un’altra, il passaggio ha un costo: concettualmente una nuova “chiamata di disegno”. Una scena con cinquanta sprite dotati ciascuno di una texture, disegnati in modo ingenuo, può significare cinquanta chiamate separate anche se ogni sprite è minuscolo. Soprattutto sull’hardware mobile, il costo delle chiamate cresce abbastanza rapidamente da influenzare la frequenza dei fotogrammi molto prima che la GPU svolga una quantità significativa di lavoro effettivo sui pixel.
Un atlante di sprite evita il problema inserendo molti sprite su una texture condivisa. Finché tutto ciò che viene disegnato in un lotto campiona la stessa texture, il renderer può combinare quei disegni in molte meno chiamate: nel caso migliore, una chiamata di disegno per tutti gli sprite che condividono atlante e materiale. Per questo gli atlanti contano più nei giochi con molti sprite piccoli ridisegnati spesso, come effetti particellari, icone dell’interfaccia e set di tasselli, che per poche grandi immagini di sfondo, dove il numero di chiamate non era mai il collo di bottiglia.
Impacchettamento: inserire gli sprite in una texture
Dato un insieme di sprite di dimensioni diverse, disporli in una texture unica sprecando meno spazio possibile è una versione del classico problema del bin packing. La maggior parte degli strumenti per atlanti usa una variante di uno di due approcci:
- Impacchettamento per righe: gli sprite vengono collocati da sinistra a destra lungo una riga («scaffale») finché uno non entra, poi inizia un nuovo scaffale sotto. Semplice e rapido, ma spreca spazio quando le altezze degli sprite variano molto nello stesso scaffale.
- Rettangoli massimali / impacchettamento a ghigliottina: lo strumento tiene traccia delle regioni rettangolari libere dopo ogni inserimento e colloca i nuovi sprite nello spazio residuo migliore, dividendo i rettangoli man mano. Offre un impacchettamento più denso, richiede più calcoli per ogni sprite ed è il metodo standard della maggior parte degli strumenti moderni per atlanti — TexturePacker, Sprite Atlas di Unity, esportatore di Godot — quando il numero di sprite non è trascurabile.
La rotazione è un’ottimizzazione aggiuntiva di alcuni impacchettatori: ruotare uno sprite di 90 gradi può farlo entrare in una striscia residua dove non entrerebbe diritto. Impacchetta meglio ma complica il rendering, perché le coordinate UV devono considerarla, quindi di solito è un’opzione da attivare e non predefinita.
Non devi implementare personalmente nulla di questo: se ne occupano strumenti del motore e impacchettatori dedicati. Vale la pena capire che densità di impacchettamento e margini trattati dopo sono in tensione: un impacchettamento più stretto spreca meno texture, ma i margini impediscono la contaminazione descritta sotto, quindi un impacchettatore troppo aggressivo può reintrodurre difetti visivi per risparmiare pochi punti percentuali di area.
Contaminazione delle texture: cos’è e perché avviene
Contaminazione della texture si verifica quando il bordo renderizzato di uno sprite mostra una striscia di colore dello sprite impacchettato accanto nell’atlante: una sottile linea colorata lungo uno o più lati che non ha nulla a che fare con la grafica dello sprite stesso. È uno dei problemi più comuni specifici degli atlanti e ha due cause distinte spesso confuse tra loro.
Contaminazione dovuta al filtraggio. Quando uno sprite viene disegnato a una scala diversa da 1:1 o la telecamera lo sposta in una posizione non allineata ai pixel, il filtro della texture della GPU campiona un piccolo insieme di texel intorno a ogni punto di campionamento, non soltanto quello più vicino. Anche con un filtro point/nearest impostato sulla texture, filtraggio e mipmapping durante il campionamento possono raggiungere leggermente l’esterno del rettangolo UV dello sprite. Se la regione adiacente dell’atlante contiene un altro sprite, quel colore vicino si infiltra.
Contaminazione nelle mipmap. Le mipmap sono versioni ridotte della texture completa, usate quando uno sprite appare piccolo sullo schermo. Generarne una mescola blocchi della texture a piena risoluzione, quindi i colori di uno sprite possono mescolarsi nel livello mip di uno vicino prima ancora di disegnare uno sprite singolo. Per questo la contaminazione appare talvolta solo a distanza o a scala piccola, mentre ingrandita sembra corretta.
Entrambe le cause risalgono alla stessa condizione di fondo: due sprite non correlati immediatamente adiacenti nell’atlante, senza nulla tra loro che assorba l’errore di campionamento.

Correggere la contaminazione: margini ed estrusione
La soluzione standard è spaziatura — lasciare uno spazio di alcuni pixel trasparenti, o con bordi duplicati, tra ogni sprite nell’atlante, così qualsiasi campionamento fuori posto cade nello spazio anziché sul contenuto reale dello sprite vicino. Uno o due pixel di spaziatura bastano nella maggior parte dei casi alla risoluzione nativa; se lo sprite viene ingrandito molto durante il rendering o sono attive le mipmap, una spaziatura maggiore offre più margine d’errore.
Un semplice margine trasparente risolve la contaminazione degli sprite vicini, ma introduce un problema minore: all’estremo bordo, il filtraggio può mescolare il colore dello sprite con il margine trasparente, producendo un lieve scurimento o alone proprio sul confine anziché un bordo completamente pulito. Estrusione (talvolta chiamata estensione dei bordi) risolve il problema riempiendo il margine con una copia dei pixel del bordo dello sprite, estesa verso l’esterno, invece di lasciarlo trasparente. Il problema degli sprite vicini viene risolto allo stesso modo: rimane uno spazio tra sprite distinti, ma il bordo dello sprite si fonde con altri pixel dello stesso sprite anziché con la trasparenza vuota.
La maggior parte degli strumenti per atlanti, come TexturePacker, l’impacchettatore Sprite Atlas di Unity e l’esportazione atlanti di Godot, espone direttamente dimensioni del margine e interruttore di estrusione nelle impostazioni. Raramente c’è motivo di lasciare il margine a zero: il costo di uno o due pixel per bordo è piccolo rispetto alla diagnosi di artefatti intermittenti che compaiono solo a certi livelli di zoom.
Le potenze di due contano ancora?
Storicamente, le GPU richiedevano dimensioni delle texture potenze di due (256, 512, 1024, 2048…) per supportare in modo efficiente mipmap e alcune modalità di ripetizione; le texture con altre dimensioni non funzionavano oppure ricorrevano a un rendering più lento. GPU e API moderne (OpenGL ES 3+, Metal, Vulkan, DirectX 11+) supportano nativamente dimensioni non potenze di due senza penalità significative nel rendering 2D di base, quindi il requisito rigido è in gran parte scomparso sulle destinazioni attuali.
Conta ancora in situazioni specifiche:
- Uso delle mipmap. Alcune piattaforme e vecchie API richiedono ancora potenze di due per creare correttamente tutta la catena mip. Se l’atlante usa mipmap, controlla il vincolo reale della destinazione, non darlo per scontato.
- Hardware vecchio o limitato. Dispositivi mobili economici, contesti WebGL 1 e alcune destinazioni integrate beneficiano ancora di dimensioni potenze di due o le richiedono.
- Allineamento della memoria e formati di compressione. I formati di compressione a blocchi, molto usati sui dispositivi mobili, comprimono in blocchi di dimensioni fisse e talvolta completano comunque le dimensioni non conformi fino al valore valido successivo. Scegliere fin dall’inizio una potenza di due evita che quei margini aggiunti silenziosamente consumino memoria inutilmente.
In pratica: per desktop attuali, console o hardware mobile moderno con flusso 2D standard, atlanti non potenze di due vanno bene. Per WebGL 1, vecchi dispositivi mobili o sistemi integrati, potenze di due resta il valore predefinito più sicuro e costa poco: molti impacchettatori arrotondano automaticamente alla potenza successiva quando l’opzione è attiva.
Rifilatura e perni
Rifilatura significa che lo strumento di impacchettamento ritaglia ogni sprite al suo contenuto realmente opaco prima di collocarlo nell’atlante, eliminando il margine trasparente circostante. È quasi sempre un vantaggio netto per la densità di impacchettamento: uno sprite con molta spaziatura trasparente intorno a un piccolo personaggio occupa molto meno spazio nell’atlante se rifilato.
La rifilatura cambia dimensioni e scostamento effettivi, importanti se logica o animazione richiedono dimensioni e pivot uguali rispetto alla tela originale. Una posa inclinata a sinistra ha confini opachi diversi da una a destra; rifilate separatamente hanno dimensioni diverse e alternarle ingenuamente sposta la posizione apparente.
Ogni strumento per atlanti che supporta la rifilatura registra anche dimensioni originali non rifilate e scostamenti insieme ai dati rifilati proprio per risolvere il problema: il motore ridisegna lo sprite rifilato nella sua posizione corretta entro i confini originali del fotogramma, così il pivot rimane visivamente coerente pur usando meno spazio di texture per pixel trasparenti. Sprite Atlas di Unity e l’importatore di Godot lo gestiscono automaticamente se la rifilatura è attivata nello strumento e non fatta a mano prima dell’impacchettamento; rifilare manualmente gli sprite prima di impacchettarli elimina i metadati di scostamento necessari al motore per riposizionarli correttamente.
Un atlante o più?
Infilare tutto in un unico mega-atlante non è automaticamente meglio. Alcuni motivi pratici per dividere invece in più atlanti:
- Permanenza in memoria. Se un livello usa solo un sottoinsieme degli sprite, caricare un atlante per livello o scena evita di mantenere in memoria GPU dati inutilizzati per contenuti lontani dal giocatore.
- Limiti delle dimensioni delle texture. L’hardware impone una dimensione massima della texture, comunemente 4096 o 8192 sulle destinazioni moderne e inferiore sui vecchi dispositivi mobili. Un set di sprite abbastanza grande non entrerà in un solo atlante indipendentemente dall’efficienza dell’impacchettamento.
- Frequenza di aggiornamento. Sprite modificati spesso, come un’interfaccia in rapida evoluzione, beneficiano di un atlante proprio separato dalla grafica stabile, così una piccola modifica non impone ricostruzione e caricamento dell’atlante completo.
- Differenze nei materiali. Sprite con shader o modalità di fusione diversi non possono essere raggruppati comunque, quindi combinarli non offre vantaggi di impacchettamento.
Una buona impostazione predefinita è raggruppare per contesto d’uso: un atlante per i tasselli dell’ambiente di un livello, uno per i personaggi e uno per l’interfaccia. Unisci ulteriormente solo se la profilazione mostra che le chiamate di disegno sono davvero un collo di bottiglia sull’hardware di destinazione.
Domande frequenti
Quanto margine serve davvero?
Uno o due pixel gestiscono molti casi alla scala nativa con mipmap disattivate. Se gli sprite vengono molto ingranditi durante l’esecuzione o le mipmap sono attive, aumenta: non c’è un numero universale corretto e vale la pena provare l’atlante alle scale e agli zoom effettivi del gioco.
Perché la contaminazione dei bordi compare solo ad alcuni livelli di zoom?
Quel comportamento indica contaminazione mipmap anziché filtraggio alla risoluzione base: il livello campionato a distanza è stato creato con una miscela oltre il confine. Estrusione e margini adeguati risolvono entrambe le cause, ma verifica che i margini sopravvivano alla riduzione attraverso diversi livelli mip, non solo alla texture base.
Devo preoccuparmi di tutto questo se non uso mipmap né ridimensiono gli sprite?
Meno, ma non zero. Anche con rendering perfetto 1:1 e filtro puntuale, posizioni subpixel della fotocamera o posizionamenti non interi degli sprite possono indurre la GPU a campionare un pixel frazionario a cavallo di un confine. Un piccolo margine è una precauzione economica anche in una configurazione 2D interamente precisa al pixel.
La rifilatura influenza le hitbox o le forme di collisione?
Non se le forme di collisione sono definite indipendentemente, la configurazione comune. Se il progetto deriva automaticamente i confini dalle dimensioni degli sprite, verifica se legge la dimensione rifilata o quella originale: usare direttamente la rifilata può restringere inaspettatamente la hitbox nei fotogrammi con molti margini trasparenti.
Stai creando sprite da impacchettare in un atlante?
Generatore di sprite unisce immagini singole in un foglio per righe con il margine scelto: l’impacchettamento per righe descritto sopra anziché rettangoli massimali, sufficiente per molti set di icone e fotogrammi. Se sono ancora in un foglio da separare, Ritagliatore di fogli di sprite crea fotogrammi singoli: entrambi interamente nel browser senza caricamenti.
Apri Generatore di sprite