Nello scorso articolo avevamo iniziato a descrivere le tecniche di antialiasing utilizzate dai produttori di chip grafici e si era visto come l’approccio iniziale avesse privilegiato le soluzioni di tipo FSAA, con l’adozione di tecniche di supersampling. Queste tecniche risultavano molto pesanti dal punto di vista computazionale, motivo per cui si è deciso di seguire un approccio diverso che potesse garantire risultati qualitativamente apprezzabili, soprattutto nei confronti di un tipo particolare di aliasing, quello definito jagged o stairstep, senza penalizzare troppo le prestazioni. La soluzione è stata quella di ricorrare all’utilizzo di multisampling che non è un tipo di FSAA, in quanto è applicato solo sui contorni delle figure.
Avevamo, altresì, visto come nelle tecniche di SSAA ogni pixel introduceva n valori di colore, altrettanti di tipo z/stencil, e almeno altrettanti facenti parte di una coverage mask utilizzata dal filtro ricostruttore del segnale. Nel MSAA si riduceva lo shader overhead, in quanto ad ogni pixel non corrispondevano n subpixel di colore differente ma n subpixel dello stesso colore.
Nell’ottica del cercare di conciliare qualità e prestazioni, avevamo anche visto un tentativo di nVIDIA di ottenere una griglia di campionamento di tipo 4x utilizzando solo 2 sample per pixel, denominato Quincunx. La tecnica consisteva nell’utilizzare i sample dei pixel vicini, anch’essi con 2 campioni ciascuno, per ottenere pattern 4x.
Se il Quincunx può essere schematizzato con la frase: fatti prestare un subpixel dai tuoi vicini, un’altra tecnica, volta ugualmente ad ottenere un numero n*2 sample utilizzando n campioni, può essere schematizzata con la frase: fatti prestare n sample dal frame successivo. Questa tecnica, adottata da ATi con la serie R4x0, è nota come Temporal AntiAliasing (TAA) e si tratta, ancora una volta, di una tecnica di antialiasing di tipo spaziale, ovvero che agisce sui pixel, da non confondere con le tecniche atte a combattere le forme di aliasing temporale.
Facciamo cenno al principio di funzionamento del TAA: in modalità 4x, nel frame 1 venivano presi 2 sample per pixel. Lo stesso si faceva nel frame 2, successivo, scegliendoli in modo tale che dalla composizione dei 4 subpixel risultasse un pattern di tipo RGMS. Anche in quuesto caso, come per il quincunx, si otteneva una griglia 4x facendo uso di 2 soli campioni per pixel. Per il TAA esisteva anche una modalità 8x ed una 12x che sfruttavano i pattern del MSAA box 4x e 6x del chip. In questo caso, non si ricorreva a griglie di tipo rotated ma di tipo fully jittered grid (FJG).
Il risultato era una cosa del tipo di quella illustrata nella successiva immagine, dove alle prime due colonne corrispondono le immagini del frame dispoari e pari rispettivamente ed alla terza quella risultante. Il caso raffigurato è relativo alla modalità MSAA 4x, ovvero TAA 8x.
A questo punto, è il caso di fare un paio di digressioni, una riguardante la tipologia di griglia adottata, l’altra il principio su cui si basa la tecnica del TAA.
Per quanto riguarda la scelta della griglia, il motivo per cui, in modalità MSAA 2x, si adotta una RGMS e non una FJG risiede nel fatto che la seconda presenta una distribuzione casuale dei campioni. Questo, con 2 soli sample per frame e con, in totale, 4 sample, può provocare un’allocazione degli stessi sample non molto efficiente né ai fini della riduzione degli artefatti e neppure ai fini dell’individuazione dei bordi dei poligoni, necessaria in caso di applicazione di MSAA. Per un motivo analogo, ovvero la scarità di sample ai fini delle operazioni di edge detect, si vedrà come, nell’applicazione del CFAA, il filtro funziona solo abbinato alle modalità di MSAA 4x o superiore e non, ad esempio, con il 2x.
Riguardo al principio di funzionamento del TAA, questo si basa sul cosiddetto potere risolvente temporale dell’occhio umano . L’occhio è in grado di vedere come distinte, due immagini che si susseguono con un intervallo di tempo superiore ad 1/10 di secondo. Quindi, ad esempio, con frame rate inferiori a 10 fps si percepiscono due immagini fisse distinte e non si ha l’illusione del movimento. Quindi, sembra sufficiente avere un frame rate di poco superiore ai 10 fps per non avere scatti. Questo significa che, in teoria, per avere immagini prive di scatti e con la riduzione dell’aliasing, facendo ricorso al TAA, è necessario avere frame rate superiori ai 20 fps. Ciò è vero in parte, in quanto la visione dell’occhio non va assimilata ad un sensore che cattura un’intera immagine ad ogni “scatto”, quanto piuttosto ad uno scanner che cattura, ad ogni scatto, parti dell’immagine che sono ricomposte dal cervello. Detto in maniera molto semplice, questo avviene perchè la parte dell’occhio che presenta la massima acuità visiva, la fovea, presenta un angolo di visuale di circa un grado a cui, però, corrisponde la maggior estensione della parte di corteccia deputata all’elaborazione delle immagini. Si tratta di una minuscola finestra di della cui esiguità non ci accorgiamo perchè l’occhio compie continui movimenti infinitesimi alternati a delle pause di fissazione. Durante queste pause, avviene il processo di cattura dell’immagine vero e proprio, mentre durante i movimenti, detti saccadi, la cui durata è pari a circa 1/4 di quella della corrispondente pausa di fissazione, la visione viene oscurata per evitare le scie tipiche che si vedono quando, ad esempio, si tenta di catturare un’immagine con un dispositivo la cui velocità di acquisizione è inferiore a quella del movimento dell’oggetto da catturare. Quindi, per circa un quarto della nostra della nostra vita da svegli, siamo di fatto ciechi senza che ce ne accorgiamo.
Un meccanismo di cattura così articolato, fa si che la qualità delle immagini percepite con un frame rate di poco superiore ai 10 fps non sia eccellente, benchè priva di scatti. Motivo per cui, ad esempio, nell’industria cinematografica si utlizza un frame rate di 24 immagini al secondo a cui si abbinano anche tecniche di motion blur.
Tornando al nostro TAA, appare evidente, a questo punto, che per avere una buona qualità d’immagine, non sia, dunque, sufficiente, tenere il frame rate poco sopra i 20 fps. Questo è il limite più evidente di questa tecnica che può funzionare bene solo a condizione di tenere frame che non scendano mai al di sotto dei 25-30 fps.
Rispetto al quincunx, questa tecnica garantisce risultati superiori, potendo, per altro, contare sulla copertura dell’intero frame e su pattern con elevato numero di sample in modalità FJG (che abbiamo visto essere qualitativamente la migliore), ma presenta condizioni di utilizzo piuttosto restrittive e non sempre applicabili.
Discorso a parte, che esula da una trattazione relativa al 3D ma che rappresenta, comunque, una forma di antialiasing, è il Glyph Antialiasing di Matrox, che opera sui caratteri, in applicazioni 2D. L’algoritmo si basa su un meccanismo di riconoscimento dei caratteri, correzione del gamma, sovracampionamento, filtraggio di tipo box e riduzione dell’immagine alle dimensioni originali, in maniera non dissimile da come agiscono gli algoritmi di MSAA con edge detect, in ambito 3D. L’accelerazione è interamente di tipo hardware
Introdotti i concetti di base sulle tecniche di filtraggio e terminate le digressioni sui tentativi di adottare tecniche che permettessero di arrivare a compromessi tra qualità e prestazioni, facciamo, ora, qualche cenno alle tecniche di antialising adottate sulle attuali gpu.
Sia ATi che nVidia fanno uso di di MSAA box con edge detect abbinate a tecniche di filtraggio delle texture trasparenti. Queste ultime, così come gli algoritmi di tipo bilineare (i TENT) di ATi, sono di tipo FSAA, mentre il MSAA, in tutte le sue varianti, è di tipo EDGE ANTIALIASING.
Sia nVidia che ATi prevedono le classiche modalità 2x, 4x e 8x del MSAA box, con le prime due che fanno uso di griglia di tipo rotated e la terza che utilizza una griglia di tipo fully jittered, come riportato di seguito
2x 4x 8x
I chip nVidia della serie G8x e GT2x0 implementano altre modalità di AA che vanno sotto il nome di Coverage Sample AntiAliasing (CSAA). La differenza rispetto al MSAA di tipo classico è che questa modalità fa uso di una griglia di tipo misto, parte della quale ricavata dai valori del campionamento effettuato per le operazioni di MSAA, che permettono di immagazzinare valori relativi ad ogni pixel, nello z e nello stencil buffer parte ricavata, in fase di ricostruzione del segnale, attraverso una coverage mask che permette di creare altri sample. Questo permette di ridurre l’accoppiamento tra z/stencil value e subsample della coverage mask, riducendo, di conseguenza, l’occupazione di banda e i costi di storage.
In pratica, ad esempio, la modalità 8x fa uso dei 4 sample del MSAA 4x più altri 4 sample disposti secondo una FJG generati in fase di ricostruzione del segnale. In questo modo, i costi di campionamento e storage sono quelli di un 4x con una qualità intermedia tra il tradizionale MSAA 4x e il MSAA 8x.
In figura, i puntini neri sono quelli della griglia del MSAA 4x, quelli blu sono quelli aggiunti dalla coverage mask del CSAA.
Tra le altre modalità segnalo la 16x e la 16xQ che è quella qualitativamente migliore proposta da nVidia.
16x 16xQ
Coeme si vede, la prima fa uso di una modalità mista 4x RGMS + 12x CS (coverage sample), mentre la seconda fa uso di 8x FJGMS + 8x CS.
In casa ATi le proposte sono analoghe e quella che cambia è il tipo di coverage mask. Le nodalità di MSAA con edge detect proposte, sono le classiche 2x, 4x, 8x MSAA e le due relative al CFAA (custom filtering antialiasing).
Come nel caso di nVidia, queste modalità funzionano solo se abbinate a MSAA box con almeno 4 sample; questo perchè 2 subpixel sono troppo pochi per poter fare efficacemente un’operazione di edge detect. La prima delle due modalità è definita 4x EDGE-DETECT AA e presenta una griglia di tipo misto con 4x RGMS + 8x CS, la seconda è la 8x EDGE-DETECT AA e presenta una griglia 8xFJGMS + 16x CS. Anche in questo caso, i sample supplementari sono aggiunti a livello di coverage mask.
Questa tecnica permette di avere un filtro ricostruttore con un numero di sample sensibilmente più elevato a costo computazionale comparabile a quello di un MSAA box 4x o 8x.
Da notare che la modalità 8x MSAA è stata inrtodotta con la generazione dx10; R5x0 e GT7x, infatti, arrivavano rispettivamente ad avere un 6x RGMS il primo e un 8xS il secondo, che era una modalità ibrida con RGMS 4x + 2x OGSS, il cui impatto sulle prestazioni era piuttosto elevato, contenendo anche una componente di SSAA che, come sappiamo, è di tipo FSAA. Questo limite derivava dal limite nel numero dei render target su cui era possibile “scrivere” i valori dei sample ottenuti nelle operazioni di MSAA.
Con la generazione DX9 era stata introdotta anche una modalità di filtraggio delle texture trasparenti, ribattezzata Adaptive AA da ATi e Transparent SSAA da nVidia. Si tratta, in entrambi i casi, di algoritmi di SSAA applicati alle sole texture “trasparenti” ovvero quelle contenenti elementi troppo minuti per poter essere correttamente rappresentati senza artefatti. Questo tipo di AA segue le regole del SS tradizionale e serve combattere, più l’effetto a “gradini” quello che si definisce polygon popping, ovvero l’effetto rappresentato microscopicamente nella seguente figura
che si riscontra quando gli elementi da rappresentare sono sensibilmente più piccoli degli stessi pixel (fogliame di un albero, rete metallica, ecc).; il risultato a livello macroscopico è quello rappresentato in figura
dove, nell’immagine di destra è applicato anche un adaptive o un transparent AA, mentre in quella di sinistra il solo MSAA 4x box.
Le DX9 hanno portato delle innovazioni a livello di filtraggio, ma presentavano anche dei limiti. Oltre quello già visto sul numero si sample utilizzabili per il MSAA, un altro limite, sempre connesso al numero di render target utilizzabile per le operazioni di z-buffering, impedisce l’uso di MSAA con engine che fanno uso di tecniche di deferred rendering. Questo perchè, come abbiamo visto, gli algoritmi di MSAA si appoggiano ai dati contenuti nello z-buffer. Le operazioni di deferred rendering prevedono la rimozione di tutte le superfici nascoste prima delle operazioni di rendring. Questo significa che solo parte dei dati relativi alle posizioni dei vertici dei poligoni lungo l’asse z, contenuti nello z-buffer, sono conservati, ovvero quelli dei poligoni che non vengono rimossi. Ciò significa che se le RBE sono in grado di applicare il filtro AA sul frame in corso di elaborazione (in quanto l’operazione avviene prima della rimozione dei poligoni stessi), non sono in grado di farlo su quelli successivi, poichè non hanno più accesso ai dati che erano contenuti nello z-buffer e eono stati cancellati in seguito alle operazioni di HSR. Questo obbliga le unità geometriche a ricalcolare le geometrie per ogni frame e rende inconciliabili le operazioni di MSAA con il deferred rendering. Il problema è stato aggirato con le DX10, grazie a due tecniche: la prima prevede che i dati contenuti nello z-buffer, prima delle operazioni di HSR, siano salvati su una texture, in modo da poter essere richiamati all’occorrenza facendo un’operazione di accesso a texture. Il secondo, più elegante ed efficace a livello di impatto sulle prestazioni, dà la possibilità di inizializzare un color buffer come z o depth buffer, in modo da utilizzarlo per conservare i dati dello z-buffer. La soluzione definitiva arriva dalle DX10.1 che prevedono l’utilizzo di tanti render target per lo z-buffer quanti se ne ha bisogno, ciascuno con la propria modalità di rendering.
Con questa frase introduco un altro dei limiti dell’attuale tecnologia basata sull’utilizzo delle RBE o ROP’s per l’applicazione del MSAA. Le RBE contengono un circuito dedicato all’applicazione del MSAA box che opera a 8 bit di tipo INTEGER, il che limita la precisione di calcolo e, se può andar bene per algoritmi di tipo BOX, non è sicuramente funzionale ad algoritmi più sofisticati che fanno uso di funzioni di ordine superiore al primo.
Infatti, sia le DX10 e, ancor più, le DX10. 1 e le future DX11, prevedono l’adozione di filtri di tipo custom con algorimi non lineari calcolati dalle unità di shading.
Un ultimo, brevissimo, appunto riguarda l’aliasing temporale che si manifesta sotto forma di strobing e flickering. Il primo si ha, ad esempio, quando si sottocampiona un oggetto che ruota sul proprio asse, come nell’esempio sottostante
dove nella prima riga appare l’oggetto che ruota, mentre nella seconda si hanno i campioni prelevati in caso si usi una frequenza pari ad 1/34 di quella di ritazione. Come si vede, la ruota appare girare in senso contrario a quello in cui effettivamente si muove.
Il secondo si manifesta con rapidi cambi di colore o piccoli oggetti oppure linee sottili che sembrano apparire e scomparire tra un frame ed il successivo.
Esistono vari metodi per combattere questi tipi di aliasing che vanno dal sovracampionamneto che, però, come nel caso dell’aliasing spaziale, non sempre è possibile o risolve il problema, all’utilizzo di operazioni di convoluzione con filtri passabasso per ottenere segnali limitati in banda.
L’approccio più comune è, però, analogo a quello seguito per l’aliasing spaziale: se le tecniche di spatial antialising applicano una sorta di blurring a livello di pixel, le tecniche di temporal antialiasing applicano il blurring a livello di frame. Esistono vari modi per ottenere il motion blur. Il più comune e anche l’unico cui farò cenno, è quello che fa uso di sovracampionamento temporale dell’immagine, alla stregua di quanto avviene con il SSAA di tipo spaziale. Ad esempio, se un oggetto si sposta di una distanza pari a 16 pixel in un secondo, sarà sufficiente campionare a 16 frame (o anche meno) al secondo e interpolare le immagini ottenute, immagazzinate all’interno di accumulation buffer.
Le tecniche di antialising spaziale e temporale sono, nella maggior parte dei casi, combinate tra loro ed utilizzate insieme ad altre tecniche di filtraggio (ad esempio quelle che interessano le texture) per ottenere la miglior qualità d’immagine possibile, sia che si tratti di giochi che di programmi di grafica 3D e, come abbiamo visto, anche di applicazioni di tipo desktop.