Per questo articolo usciamo un pochino dal seminato e non parleremo di cose direttamente legate con
OF:DR.
Vorrei discutere con voi di alcune strategie per il tuning degli asset, in particolare le texture, per poter avere la massima qualità in ogni occasione.
In genere, durante il rendering, si usano le seguenti texture:
Diffuse e Normal:
E poi tutta una serie di texture utili per le operazioni di rendering, come la mappa dello specular:

Tutte queste texture vanno caricate da disco e devono stare in memoria. Possiamo facilmente capire che non possiamo metterle tutte singolarmente, poiché occuperebbero troppa memoria. Non possiamo inoltre averle tutte grandi uguali, perché sarebbe uno spreco.
Inoltre sarà necessario regolare le dimensioni e gli shader usati con i vari LOD, in modo da poter stare nel budget di tempo che abbiamo a disposizione.
Supponendo, ad esempio, di avere 3ms di budget, dovremo tunare il nostro sistema tenendo in considerazione in numero di asset che andranno disegnati per ogni frame e la qualità richiesta.
In genere, ovviamente, la qualità richiesta per gli asset in primo piano è molto alta e degrada velocemente mentre si allontana dalla camera.
Prendiamo come esempio gli occhi: visti da vicino hanno le riflessioni, un po’ di bump, un bello specular e un po’ di effettini per simulare il comportamento delle varie parti dell’occhio con la luce.
Basta allontanarci un po’, però, per vedere ridurre gli occhi a pochi pixel sullo schermo. A questo punto non sarà più necessario avere la maggior parte degli effetti.
Generalmente, quando un asset è vicino alla camera, i pixel shader diventano il collo di bottiglia (poiché l’asset ha un sacco di pixel a schermo), mentre allontanandosi dalla camera il collo di bottiglia diventa il vertex shader.
È necessario sapere questo mentre si ottimizzano gli shader dei vari LOD.
Ad esempio: supponiamo che il nostro tuning ci abbia fatto scoprire che il LOD0 prende 1.5 ms, di cui il 30% sul vertex shader ed il 70% sul pixel shader. È abbastanza chiaro che andare a ottimizzare il vertex shader porterà dei guadagni limitati rispetto all’ottimizzare il pixel shader.
Vediamo qualche esempio di ottimizzazione.
Ottimizzazione della qualità
Partiamo della normal map. La normal map potrebbe essere salvata in una texture DXT1, ma così facendo avremmo gli artefatti tipici di tale formato. In genere questi artefatti non sono molto visibili su texture che rappresentano un’immagine ma siccome la normal map contiene dei dati, il problema diventa piuttosto visibile.
Inoltre l’uso di texture DXT1 obbligherebbe a rinormalizzare le normali campionate dalla texture, poiché ai cambi di mipmap i valori sarebbero dimezzati in lunghezza.
L’uso del formato non compresso è ovviamente fuori discussione: occuperebbe troppo spazio.
La soluzione sta in un formato chiamato DXT5nm, che fondamentalmente si salva le coordinate X e Y della normal map. Sarà nostro compito calcolarci la componente Z usando il buon vecchio Pitagora.
Vediamo qualche formula.
Sapendo che la lunghezza finale della normale sarà 1, possiamo scrivere:
Leviamo la radice elevando al quadrato
Quando faremo lo shader, sarà nostra premura mettere X e Y in un vettore, in modo che (X^2+Y^2) diventi “dot(vet.xy, vet.xy)”.
Il formato DXT5 occupa più spazio del formato DXT1, ma in questo caso la qualità che otteniamo e’ talmente maggiore che ne vale la pena.
Ottimizzazione nello spazio
Ecco un’idea su come ottimizzare lo spazio occupato da una texture dei vestiti.
Le texture dei vestiti hanno la peculiarità di essere composte da elementi piuttosto grossi (come i disegni davanti o dietro) ed elementi molto piccoli, come le cuciture. Se volessimo avere le cuciture con il dettaglio necessario per notarle, sarebbe necessario avere texture molto, molto grandi.
Nonostante questo, i disegni sulla maglietta sono abbastanza grandi da non richiedere una texture enorme per distinguerne i dettagli. Per questo motivo ci viene comodo usare delle texture di dettaglio.
Per un vestito, ad esempio, servirebbe una texture di dettaglio per le cuciture che andrebbe tilata ripetutamente sul vestito.
Per avere una idea precisa di quanto debba essere questo tiling, possiamo prendere il dettaglio da una texture molto grande (ad esempio 4096x4096) e poi tilare il dettaglio (ad esempio 128x128) sulla texture più piccola di conseguenza (ad esempio 512x512), in modo da non avere risultati strani. Nell’esempio, il tiling sarebbe 32.
Ci accorgiamo che però la texture di dettaglio potrebbe andare anche sui disegni della maglietta e noi questo non lo vogliamo. La soluzione, semplicemente, è quella di usare una mappa. La mappa, in bianco e nero, può anche essere molto più piccola rispetto al diffuse che utilizzeremo e potrebbe essere salvata nel canale libero di qualche altra texture.
Un altro tipo di dettaglio di cui potremmo avere bisogno è sulla normal map.
Se riteniamo che il dettaglio nel diffuse sia troppo invasivo oppure vogliamo un maggiore dettaglio nell’illuminazione, possiamo utilizzare una normal map di dettaglio. È una soluzione che ho trovato funzionare in maniera ottima utilizzando un blendig lineare tra le due normal map, in modo che ci sia molto dettaglio sullo specular e molta normal map originale su tutto il resto.
Con questo, per questo mese è tutto! Il prossimo mese continueremo questo argomento, analizzando altre soluzione per ottimizzare tempo e spazio.