I contorni di rendering, a meno che non si visualizzi solo una dozzina di caratteri in totale, rimangono un "non andare" a causa del numero di vertici necessari per carattere per approssimare la curvatura. Sebbene ci siano stati approcci per valutare curve più bezier nel pixel shader, questi soffrono di non essere facilmente antialiasati, il che è banale usando un quad strutturato in una mappa di distanza, e la valutazione delle curve nello shader è ancora molto più computazionale del necessario.
Il miglior compromesso tra "veloce" e "qualità" sono ancora i quad con texture con una trama del campo a distanza firmata. È leggermente più lento rispetto all'uso di un normale quad testurizzato normale, ma non così tanto. D'altra parte, la qualità è in un campo da gioco completamente diverso. I risultati sono davvero sorprendenti, è il più veloce possibile e anche effetti come il bagliore sono banalmente facili da aggiungere. Inoltre, se necessario, è possibile eseguire il downgrade della tecnica su hardware più vecchio.
Vedi la famosa carta Valve per la tecnica.
La tecnica è concettualmente simile a come funzionano le superfici implicite (metaballs e simili), sebbene non generi poligoni. Funziona interamente nel pixel shader e prende la distanza campionata dalla trama come una funzione di distanza. Tutto al di sopra di una soglia scelta (di solito 0,5) è "in", tutto il resto è "out". Nel caso più semplice, su hardware di 10 anni non compatibile con shader, l'impostazione della soglia del test alfa su 0,5 farà esattamente ciò (anche se senza effetti speciali e antialiasing).
Se si desidera aggiungere un po 'più di peso al carattere (finto grassetto), una soglia leggermente più piccola farà il trucco senza modificare una singola riga di codice (basta cambiare l'uniforme "font_weight"). Per un effetto bagliore, si considera semplicemente tutto "al di sopra della soglia" come "in" e tutto al di sopra di un'altra soglia (più piccola) come "out, ma in bagliore" e LERP tra i due. L'antialias funziona allo stesso modo.
Utilizzando un valore di distanza con segno a 8 bit anziché un singolo bit, questa tecnica aumenta la risoluzione effettiva della mappa della trama di 16 volte in ogni dimensione (anziché in bianco e nero, vengono utilizzate tutte le sfumature possibili, quindi abbiamo 256 volte il informazioni utilizzando la stessa memoria). Ma anche se ingrandisci ben oltre il 16x, il risultato sembra ancora abbastanza accettabile. Le lunghe linee rette alla fine diventeranno un po 'irregolari, ma non ci saranno manufatti di campionamento "blocchi".
È possibile utilizzare uno shader di geometria per generare i quad da punti (ridurre la larghezza di banda del bus), ma onestamente i guadagni sono piuttosto marginali. Lo stesso vale per il rendering dei personaggi istanziati come descritto in GPG8. Il sovraccarico dell'istanziamento viene ammortizzato solo se hai molto testo da disegnare. I guadagni non sono, secondo me, in relazione alla complessità aggiunta e alla non declassabilità. Inoltre, sei limitato dalla quantità di registri costanti o devi leggere da un oggetto buffer di trama, che non è ottimale per la coerenza della cache (e l'intento era di ottimizzare per cominciare!).
Un semplice, semplice vecchio buffer di vertici è altrettanto veloce (forse più veloce) se pianifichi l'upload un po 'in anticipo nel tempo e funzionerà su ogni hardware costruito negli ultimi 15 anni. Inoltre, non è limitato a un determinato numero di caratteri nel font, né a un determinato numero di caratteri da visualizzare.
Se sei sicuro di non avere più di 256 caratteri nel tuo carattere, le matrici di trama possono valere la pena di prendere in considerazione la possibilità di eliminare la larghezza di banda del bus in un modo simile alla generazione di quad da punti nello shader della geometria. Quando si utilizza una trama array, le coordinate della trama di tutti i quadricipiti hanno identiche, costanti s
e t
coordinate e differiscono solo nella r
coordinata, che è uguale all'indice dei caratteri da renderizzare.
Ma come con le altre tecniche, i guadagni previsti sono marginali a costo di incompatibilità con l'hardware della generazione precedente.
C'è uno strumento utile di Jonathan Dummer per generare trame a distanza: pagina di descrizione
Aggiornamento:
come più recentemente sottolineato in Programmable Vertex Pulling (D. Rákos, "OpenGL Insights", pagg. 239), non vi è alcuna latenza o sovraccarico significativo associato all'estrazione di dati di vertici a livello di codice dallo shader sulle nuove generazioni di GPU, rispetto a fare lo stesso usando la funzione fissa standard.
Inoltre, le ultime generazioni di GPU hanno cache L2 per scopi generali sempre più ragionevolmente dimensionate (ad esempio 1536 kB su NVIDIA Kepler), quindi ci si può aspettare il problema di accesso incoerente quando si eseguono offset casuali per gli angoli quad da una trama buffer essendo meno di un problema.
Ciò rende più attraente l'idea di estrarre dati costanti (come le dimensioni quadruple) da una trama buffer. Un'implementazione ipotetica potrebbe quindi ridurre al minimo i trasferimenti di memoria e PCIe, nonché la memoria GPU, con un approccio come questo:
- Carica solo un indice di caratteri (uno per carattere da visualizzare) come unico input per uno shader di vertice che passa su questo indice e
gl_VertexID
, e amplificalo a 4 punti nello shader di geometria, avendo ancora l'indice di caratteri e l'id del vertice (questo sarà "gl_primitiveID reso disponibile nel vertex shader") come unico attributo e acquisirà questo tramite feedback di trasformazione.
- Questo sarà veloce, perché ci sono solo due attributi di output (collo di bottiglia principale in GS), ed è vicino a "no-op" altrimenti in entrambe le fasi.
- Associa una trama buffer che contiene, per ciascun carattere del font, le posizioni dei vertici del quad testurizzato rispetto al punto base (queste sono fondamentalmente le "metriche del font"). Questi dati possono essere compressi in 4 numeri per quadruplo memorizzando solo l'offset del vertice in basso a sinistra e codificando la larghezza e l'altezza del riquadro allineato all'asse (supponendo che mezzo float, saranno 8 byte di buffer costante per carattere - un tipico carattere di 256 caratteri potrebbe adattarsi completamente a 2 kB di cache L1).
- Imposta un'uniforme per la linea di base
- Associare una trama buffer con offset orizzontali. Probabilmente questi potrebbero anche essere calcolati sulla GPU, ma è molto più semplice ed efficiente per quel tipo di cose sulla CPU, in quanto si tratta di un'operazione strettamente sequenziale e per nulla banale (si pensi alla crenatura). Inoltre, sarebbe necessario un altro pass di feedback, che sarebbe un altro punto di sincronizzazione.
- Rendering dei dati precedentemente generati dal buffer di feedback, lo shader di vertici estrae l'offset orizzontale del punto base e gli offset dei vertici degli angoli dagli oggetti buffer (usando l'id primitivo e l'indice dei caratteri). L'ID vertice originale dei vertici inviati è ora il nostro "ID primitivo" (ricorda che la GS ha trasformato i vertici in quad).
In questo modo, si potrebbe ridurre idealmente la banda di vertici richiesta del 75% (ammortizzata), sebbene sarebbe in grado di eseguire il rendering di una sola riga. Se si desidera essere in grado di eseguire il rendering di più righe in una chiamata di disegno, è necessario aggiungere la linea di base alla trama del buffer, anziché utilizzare un'uniforme (riducendo i guadagni della larghezza di banda).
Tuttavia, anche ipotizzando una riduzione del 75%, poiché i dati dei vertici per visualizzare quantità "ragionevoli" di testo sono solo di circa 50-100 kB (che è praticamente zeroa una GPU o un bus PCIe) - dubito ancora che la complessità aggiunta e la perdita di compatibilità con le versioni precedenti valga davvero la pena. Ridurre lo zero del 75% è ancora solo zero. Devo ammettere che non ho provato l'approccio di cui sopra e sarebbero necessarie ulteriori ricerche per fare una dichiarazione veramente qualificata. Tuttavia, a meno che qualcuno non possa dimostrare una differenza di prestazioni davvero sbalorditiva (usando quantità "normali" di testo, non miliardi di caratteri!), Il mio punto di vista rimane che per i dati dei vertici, un semplice e semplice vecchio buffer di vertici è giustamente abbastanza buono essere considerato parte di una "soluzione all'avanguardia". È semplice e diretto, funziona e funziona bene.
Dopo aver già fatto riferimento a " OpenGL Insights " sopra, vale la pena sottolineare anche il capitolo "Rendering di forme 2D per campi di distanza" di Stefan Gustavson che spiega in dettaglio i rendering di campi di distanza.
Aggiornamento 2016:
Nel frattempo, esistono diverse tecniche aggiuntive che mirano a rimuovere gli artefatti di arrotondamento degli angoli che diventano inquietanti a ingrandimenti estremi.
Un approccio utilizza semplicemente i campi di pseudo-distanza anziché i campi di distanza (la differenza è che la distanza è la distanza più breve non rispetto al contorno reale, ma al contorno o a una linea immaginaria che sporge sul bordo). Questo è un po 'meglio e funziona alla stessa velocità (identico shader), usando la stessa quantità di memoria di trama.
Un altro approccio utilizza la mediana dei tre in una trama a tre canali dettagli e implementazione disponibili su github . L'obiettivo è quello di migliorare gli e-o gli hack utilizzati in precedenza per risolvere il problema. Di buona qualità, leggermente, quasi in modo evidente, più lento, ma utilizza una memoria texture tre volte superiore. Inoltre, gli effetti extra (ad es. Bagliore) sono più difficili da ottenere.
Infine, la memorizzazione delle curve di Bezier reali che compongono i personaggi e la loro valutazione in uno shader di frammenti è diventata pratica , con prestazioni leggermente inferiori (ma non tanto che è un problema) e risultati sorprendenti anche ai massimi ingrandimenti.
Demo WebGL che rende un grande PDF con questa tecnica in tempo reale disponibile qui .