Nell’ultima puntata di questa rubrica, si era arrivati al termine delle operazioni di tipo geometrico e si era riempito lo z-buffer. In questa parte, saltiamo le operazioni di rasterizzazione su cui si tornerà in seguito, per introdurre l’argomento FILTRI. Per farlo, inizieremo dal cosiddetto antialiasing.
La motivazione della scelta è presto detta: il filtro antialiasing classico, noto anche come box filter, è applicato, nelle ROP’s, sui soli bordi dei poligoni, i cui valori sono contenuti all’interno del depth buffer o z-buffer; per questo motivo, è noto anche come EDGE ANTIALIASING. Quindi, di fatto, ripartiamo dallo z-buffer e cerchiamo, innanzitutto, di capire cosa è l’aliasing e qual è la sua origine.
Quando si passa da uno spazio continuo, come ad esempio, gli spazi oggetto che abbiamo visto nei precedenti capitoli, ad uno discreto, come il monitor di un pc o un televisore, oppure si tenta di catturare un’immagine del mondo reale attraverso un mezzo di acquisizione digitale come il sensore di una fotocamera, di una videocamera o qualunque altra cosa che possa assimilarsi ad una matrice discreta di punti, si compiono alcune ineludibili operazioni: l’immagine reale, appartenente allo spazio continuo, viene campionata, ovvero si individuano alcuni punti di essa che potremmo definire “significativi”, in quanto ci permettono, una volta salvati i loro valori, di poter ricostruire l’immagine dell’oggetto con una buona approssimazione.
Questa operazione è regolamentata da alcuni teoremi e formule (si potrebbero citare, sopra tutti, Shannon e Nyquist) che stabiliscono quali sono i criteri di scelta del numero e della posizione dei punti da utilizzare per CAMPIONARE l’oggetto o l’immagine. Come dice il nome stesso, un’operazione di campionamento non è altro che l’individuazione di una serie di campioni (o punti) dell’oggetto che siano in numero ed in posizione tali da fornirci tutte le informazioni necessarie sull’oggetto che vogliamo digitalizzare. Il campionamento può essere spaziale o temporale.
In figura, un esempio di campionamento temporale di un segnale in uno spazio bidimensionale
Se, nelle operazioni di campionamento si riesce a rispettare il teorema di Shannon che prevede l’utilizzo di una frequenza di campionamento almeno doppia rispetto alla massima frequenza del segnale da campionar, la situazione che si viene a presentare è la seguente
con campioni dello spettro sufficientemente spaziati da non avere interferenze reciproche.
La situazione di gran lunga più comune è, però, questa
in cui non è possibile rispettare il teorema di Shannon; questo dà luogo a reciproche interferenze che originano una serie di disturbi.
La seconda operazione si chiama QUANTIZZAZIONE: In questa fase si sceglie il numero di bit con cui si vuole rappresentare ogni campione del segnale o dell’oggetto. Anche in questo caso, ci sono dei vincoli, in quanto, teoricamente dovrei utilizzare un numero infinito di livello per fornire l’esatta rappresentazione di un segnale continuo, mentre invece dovrò, per forza di cose, utilizzarne un numero finito.
nell’immagine, un esempio di quantizzazione con intervallo di ampiezza costante; l’errore di quantizzazione, ovvero l’errore che si commette nell’approssimare una grandezza continua con un algoritmo che fa uso di un numero finito di livelli, diminuisce, ovviamente, al crescere del numero di livelli.
Tale numero sarà, però, limitato da altri fattori, ad esempio, i costi di un dispositivo che faccia quantizzazione e ricostruzione utilizzando un numero molto elevato di bit, oppure gli errori che potrebbero essere indotti dall’utilizzo di un numero elevato di livello in presenza di rumore che può provocare un’errata interpretazione dei valori di soglia (più sono i livelli, minore è la loro distanza reciproca, più è probabile un errore di “lettura” della soglia posta tra due livelli contigui.
Finora ho fatto cenno ad operazioni ideali, con campionamento di tipo puntiforme e quantizzazione ideale. In pratica, nelle operazioni di campionamento in CG, non si utilizzano delle funzioni delta di Dirac ma dei campioni solitamente di forma quadrata (i pixel) e la quantizzazione introduce, comunque, un errore, qualunque sia il numero di livelli e, di conseguenza, di bit scelti per rappresentare i campioni. Questi errori producono un rumore addizionale che dà origine a disturbi di varia natura.
In seguito alle operazioni di campionamento e quantizzazione, l’oggetto o il segnale, è stato trasformato in una sequenza di bit che è inviata ad un filtro ricostruttore che ha il compito di tradurre di nuovo questa serie di numeri in un qualcosa di comprensibile ai nostri sensi (un’immagine, un suono, ecc).
Per quanto detto (rumore introdotto dalla digitalizzazione, utilizzo di un numero limitato di campioni e di livelli per rappresentare oggetti di uno spazio continuo), ciò che viene ricostruito non è l’oggetto reale ma una sua “immagine distorta” (ovviamente il meno possibile), una sua “falsa rappresentazione”; insomma un suo ALIAS.
In CG, col termine aliasing si indicano tutta quella serie di fenomeni che disturbano l’immagine ricostruita, introducendo artefatti di varia tipo.
Una delle forme più comuni di alisasing spaziale e di cui si è già fatto cenno è il MOIRE, che ha origine quando si campiona un oggetto che presenta una distribuzione periodica attraverso uno strumento dotato di un reticolo di tipo periodico con frequenza o angolo differente rispetto all’oggetto da rappresentare.
Un’altra è nota come STAIRSTEPPING o JAGGIES ed è quella che si verifica quando si tenta di rappresentare una linea retta obliqua su una matrice di pixel; il risultato è che la linea viene fuori a gradini (le famigerate scalettature).
Altre forme derivano dalle operazioni di point sampling su texture o su triangoli, quando le dimensioni di questi elementi diventano comparabili con quelli dei singoli pixel. In tal caso si possono verificare problemi i flickering, ovvero drastici cambi di colore, al muoversi della camera virtuale che riprende la scena, del pixel ricoperto da un determinato texel o di quello su cui si trova un determinato triangolo.
Una forma di aliasing temporale, invece, anch’essa molto comune, si incontra quando si renderizza un’animazione di una ruota a 30 fps. Se la velocità di rotazione dell’oggetto è un sottomultiplo della frequenza di campionamento, allora lo si vedrà ruotare nella giusta direzione; se l’oggetto compie 30 rotazioni al secondo, allora, ad ogni frame, avrà compiuto 360° e lo si vedrà apparentemente fermo. Qualora, invece, ruotasse, ad esempio, a 29 giri al secondo, ad ogni frame sarebbe in ritardo di 12° rispetto alla frequenza di campionamento e l’oggetto sembrerebbe ruotare al contrario (ad esempio un’auto che procede in avanti con le ruote che girano in senso antiorario). Questo fenomeno va sotto il nome di STROBING.
E’ possibile, in qualche modo, eliminare l’aliasing? In teoria si, campionando il segnale ad una frequenza che è almeno doppia della massima frequenza del segnale stesso. In pratica, no, in quanto il segnale potrebbe non essere limitato in banda (che è poi quello che succede comunemente in CG, motivo per cui non si ricorre alla semplice operazione di point sampling).
Esistono, però, dei modi per limitarne gli effetti, ricorrendo anche alla funzione di filtro esercitata dall’occhio umano, che in alcuni casi può essere ingannato.
Esistono diversi tipi di algoritmi di antialiasing ed il loro utilizzo è determinato sia dal tipo di disturbo su cui si vuole intervenire, sia sulla loro facilità di implementazione in hardware.
Il filtro ideale è un filtro passabasso di tipo sinc(x) che, però, presenta alcuni problemi: ha estensione spaziale infinita ed ha una complessità tale da renderlo inapplicabile in concreto.
Una sua discreta approssimazione è quella con funzione di tipo bicubico; anche questa, però, piuttosto costosa dal punto di vista computazionale.
Le tipologie più facilmente implementabili sono quelle che fanno uso di funzioni lineari. Il filtro più semplice, usato anche a livello di ROP’s, nelle GPU, è il cosiddetto BOX filter; fa uso di un’interpolazione lineare e si può schematizzare con un parallelepipedo in 3D e con un rettangolo in 2D, di dimensioni di base pari a quelle del pixel. Un filtro di tipo BOX individua, per ogni triangolo, la sua porzione che cade all’interno di un pixel e ne prende il valore della funzione colore; fa lo stesso per tutti i triangoli che hanno porzioni che cadono all’interno di quel pixel e, quindi, attribuisce al pixel il valore ottenuto facendo una media non pesata di tutti i colori risultanti. Si tratta del filtro più semplice da implementare ma anche del meno efficace.
Un’alternativa è il TENT FILTER che fa uso di interpolazione bilineare. In questo caso si fa una media che usa come funzione peso la distanza di ogni triangolo dal centro del pixel. La qualità è migliore di quella del BOX, ma si è ancora distanti da risultati ottimali.
Fatta questa premessa di carattere molto generale, nella prossima puntata vedremo come si è pensato di risolvere, in hardware, il problema dell’aliasing nei chip grafici; in particolare, si vedrà come la soluzione adottata per le operazioni di campionamento e ricostruzione, è quella di fare oversampling operando a livello di screen space e sullo z-buffer, e utilizzando interpolazioni di tipo lineare (box) o bilineare (tent), con diverse modalità che vanno dal FSAA (supersampling o il tent usato da ATi) all’EDGE antialiasing di tipo multisampling, comunemente usato dagli attuali chip e le cui operazioni sono a carico delle ROP’s. Si vedrà anche cosa cambia a livello di efficienza al variare del tipo di griglia scelta per le operazioni di sovracampionamento e ricostruzione e si farà un cenno ai motivi che rendono impossibile l’applicazione del MSAA box filter in caso di engine che fanno uso di deferred renderin, qualora non si faccia uso di più z-buffer (in pratica fino alle dx9 comprese)