Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Game Development
  2. Shaders

Una guida per principianti alla programmazione degli Shaders Grafici

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called A Beginner's Guide to Coding Graphics Shaders.
A Beginner's Guide to Coding Graphics Shaders: Part 2

Italian (Italiano) translation by Chip (you can also view the original English article)

Imparare a programmare shader grafici significa saper sfruttare la potenza della GPU, con le sue migliaia di core tutti in parallelo. Si tratta di un tipo di programmazione che richiede una mentalità diversa, ma le difficoltà iniziali varranno la pena quando sbloccheremo tutto il suo potenziale.

Praticamente ogni simulazione grafica moderna che vediamo è alimentata in qualche modo da codice scritto per la GPU, dagli effetti di luce realistici nei giochi di livello AAA agli effetti 2D effetti di post-processing alle simulazioni di fluidi.

Una scena in Minecraft, prima e dopo l'applicazione alcuni shader.

Lo scopo di questa guida.

La programmazione degli shader è spesso fraintesa e a volte viene considerata quasi come una magia nera, enigmatica. Ci sono un sacco di esempi di codice là fuori che mostrano come creare effetti incredibili, ma spesso offrono poca o nessuna spiegazione. Questa guida si propone di colmare questa lacuna. Mi concentrerò maggiormente sulle basi della scrittura e comprensione del codice degli shader, in modo da poter facilmente modificare, combinare, o scrivere il proprio partendo da zero!

Questa è una guida generica, tutto quello che imparerete potrà essere applicato per qualsiasi cosa faccia uso degli shader.

Quindi, cosa sono gli Shader?

Uno shader è semplicemente un programma che viene eseguito nella pipeline grafica e che dice al computer come gestire ogni singolo pixel. Questi programmi sono chiamati shaders (ndt shader letteralmente ombreggiatore) perché sono spesso usati per controllare gli effetti di luce e di ombreggiatura, ma non c'è ragione per cui non possano gestire altri effetti speciali.

Gli Shaders sono scritti in un linguaggio speciale per le ombreggiature. Non preoccupatevi, non dovrete imparare un nuovo linguaggio; useremo il GLSL (OpenGL Shading Language), che è un linguaggio simile al C. Ci sono un sacco di linguaggi di shading per diverse piattaforme ma dal momento che sono tutti pensati per funzionare sulle stesse GPU, sono tutti molto simili)

Iniziamo!

Faremo uso di ShaderToy per questo tutorial. Questo tool ci consente di iniziare a programmare shader nella zona destra del browser, senza la necessità di configurare nulla! (Per il rendering viene usato WebGL, quindi avrete bisogno di un browser in grado di supportarlo.) Creare un account è opzionale ma è molto utile se vogliamo salvare il codice creato.

Nota: Al momento della stesura di questo articolo ShaderToy è in beta. Alcuni piccoli dettagli della UI / sintassi potrebbero essere leggermente diversi.

Cliccando New Shader, si dovrebbe vedere qualcosa di simile a questo:

Se non siete loggati l'interfaccia potrebbe essere leggermente diversa.

La piccola freccia nera in fondo va cliccata per compilare il codice.

Cosa sta accadendo?

Ora, in una sola frase, vi spiegherò come funzionano gli shader. Siete pronti? Ecco qua!

Il solo scopo di uno shader è di restituire quattro numeri: r, g, b, e a.

Questo è tutto ciò che fa o può fare. La funzione che vedete davanti a voi si applica per ogni singolo pixel sullo schermo. Vengono restituiti quei quattro valori di colore che diventano il colore del pixel. Questo è ciò che si chiama un Pixel Shader (a volte indicato come un Fragment Shader).

Avendo questo in mente, proviamo a colorare il nostro schermo di un rosso pieno. I valori RGBA (rosso, verde, blu, e "alpha", che definisce la trasparenza) vanno da 0 a 1, quindi tutto quello che dobbiamo fare è ritornare r, g, b, a = 1,0,0,1. ShaderToy si aspetta di ricevere il colore finale da registrare dei pixel nella variabile fragColor.

Congratulazioni! Questo è il vostro primo vero lavoro con gli shader!

Sfida: Possiamo cambiare il colore con un grigio pieno?

vec4 è solo un tipo di dato, possiamo dichiarare il nostro colore come una variabile, in questo modo:

Questo non è molto eccitante, però. Abbiamo il potere di eseguire codice su centinaia di migliaia di pixel in parallelo e stiamo impostandoli tutti allo stesso colore.

Proviamo a colorare con un gradiente lungo tutto lo schermo. Beh, non possiamo fare molto se non sappiamo alcune cose circa il pixel che stiamo trattando, come ad esempio la sua posizione sullo schermo ..

Shader Inputs

Il pixel shader passa alcune variabili per il suo uso. La variabile più utile per noi è fragCoord, che indica la coordinata x e y del pixel (e z, se si lavora in 3D). Proviamo a trasformare tutti i pixel nella metà sinistra dello schermo di nero, e tutti quelli sulla metà destra di rosso:

Nota: Per qualsiasi vec4, è possibile accedere tramite i suoi componenti obj.x, obj.y, obj.z e obj.w o via obj.r, obj.g, obj.b, obj.a. Sono equivalenti; è solo un modo conveniente di chiamarli per rendere il codice più leggibile, in modo che quando gli altri vedono obj.r, capiscono che obj rappresenta un colore.

Vedete un problema con il codice sopra? Prova a cliccare sul pulsante go fullscreen in basso a destra della finestra di anteprima.

La porzione dello schermo sarà diversa a seconda delle dimensioni dello schermo. Per garantire che esattamente la metà dello schermo si colori di rosso, abbiamo bisogno di sapere la grandezza del nostro schermo. La dimensione dello schermo non è una variabile come la posizione dei pixel, di solito la sua gestione e impostazione spettano al programmatore che sta programmando l'applicazione stessa. In questo caso, saranno gli sviluppatori in ShaderToy che imposteranno le dimensioni dello schermo.

Se qualunque valore non fosse una variabile interna dello shader, sarà possibile inviare tale informazioni dalla CPU (il programma principale) verso la GPU (shader). ShaderToy lo gestirà per noi. Potete vedere tutte le variabili che vengono passate allo shader nel tab Shader Inputs. Le variabili che vengono passate in questo modo, da CPU a GPU, sono chiamate uniform in GLSL.

Andiamo a ritoccare il nostro codice qui sopra per ottenere il centro dello schermo. Avremo bisogno di usare lo shader input iResolution:

Se, questa volta, si tenta di allargare la finestra di anteprima, i colori dovrebbero ancora perfettamente dividere lo schermo a metà.

Dallo Split al gradiente

Trasformare tutto questo in un gradiente dovrebbe essere abbastanza facile. I valori del nostro colore vanno da 0 a 1, e anche le nostre coordinate ora vanno da 0 a 1.

E voila!

Sfida: Puoi trasformare questo in un gradiente verticale? Che dire della diagonale? Che dire un gradiente con più di un colore?

Giocandoci un pò, si può dire che l'angolo in alto a sinistra ha coordinate (0,1), e non (0,0). Questo è importante da tenere a mente.

Disegno di immagini

Giocare con i colori è divertente, ma se vogliamo fare qualcosa di impressionante il nostro shader deve essere in grado di prendere l'input da un'immagine e modificarla. In questo modo possiamo fare uno shader che interessa tutta la nostra schermata di gioco (come una correzione del colore o un effetto liquido subacqueo) o influire solo su determinati oggetti in certi modi in base all'ingresso che riceviamo (come un sistema di illuminazione realistica).

Se stessimo programmando su una piattaforma normale, avremmo bisogno di inviare la nostra immagine (o texture) alla GPU come una uniform, allo stesso modo con cui avremmo inviato la risoluzione dello schermo. Sarà sempre ShaderToy ad occuparsi di questo per noi. In basso ci sono quattro canali di ingresso:

I quattro canali di ingresso dello ShaderToy.

Clicca su iChannel0 e seleziona qualsiasi texture (immagine) che ti piace.

Una volta fatto avete un'immagine che viene passato allo shader. C'è un problema però: non c'è nessuna funzione DrawImage (). Ricordate, l'unica cosa che il pixel shader può solo fare è cambiare il colore di ogni pixel.

Quindi, se siamo in grado di restituire solo un colore, come faremo a disegnare la nostra texture sullo schermo? Abbiamo bisogno di mappare in qualche modo il nostro pixel attivo dello shader nel corrispondente pixel sulla texture:

Ricordate, il pixel in alto a sinistra nel nostro schermo è (0,1), mentre il pixel in alto a sinistra della texture è (0,0), quindi abbiamo bisogno di capovolgere l'asse y.

Possiamo farlo utilizzando la funzione Texture2D (texture, coordinates), che prende una texture ed una coppia (x, y) come input di coordinate e restituisce il colore della texture a quelle coordinate come un vec4.

È possibile abbinare le coordinate allo schermo in qualsiasi modo tu voglia. Si potrebbe disegnare l'intera texture in un quarto dello schermo (saltando pixels, e ridimensionamento verso il basso) o semplicemente disegnare una sola parte della trama.

Per i nostri scopi, vogliamo solo vedere l'immagine, quindi dovremo abbinare il pixel 1: 1:

Con questo, abbiamo la nostra prima immagine!

Ora che sappiamo inserire correttamente i dati da una texture, è possibile manipolarla come più vi piace! Si può allungare e scalarla o giocare con i suoi colori.

Proviamo a modificare l'esempio con un gradiente, simile a quello che abbiamo fatto in precedenza:

Congratulazioni, avete appena fatto il vostro primo effetto post-processing (post-elaborazione)!

Sfida: Sai scrivere uno shader che trasformi l'immagine in bianco e nero?

Notare che anche se è un'immagine statica, quello che state vedendo davanti a voi sta accadendo in tempo reale. Potete vedere questo da voi, sostituendo l'immagine statica con un video: cliccate sull'input iChannel0 di nuovo e selezionare uno dei video.

Aggiungere del movimento

Finora tutti i nostri effetti sono stati statici. Siamo in grado di fare molte più cose interessanti sfruttando gli input che ShaderToy ci dà. iGlobalTime è una variabile che si incrementa costantemente; possiamo usarla come seme (come base) per fare effetti periodici. Proviamo a giocare un pò con i colori:

Le funzioni seno e coseno sono integrati in GLSL, così come molte altre funzioni utili, come per ottenere la lunghezza di un vettore o la distanza tra due vettori. I valori dei colori non sono pensati per essere negativi, quindi per assicurarsi di ottenere il valore assoluto utilizziamo la funzione abs.

Sfida: Si può fare uno shader che cicla cambiando l'immagine dal bianco e nero al colore pieno?

Una nota sul debugging degli Shaders

Potreste essere abituati a fare debugging passo passo attraverso il codice stampando tutti i valori possibili per vedere cosa sta succedendo ma questo non è proprio possibile quando si programmano shaders. Potreste trovare alcuni strumenti di debug specifici per la vostra piattaforma, ma in generale la cosa migliore è quella di impostare il valore che si sta testando con qualcosa di grafico che si può vedere a schermo.

Conclusione

Queste sono solo le basi del lavorare con gli shader, ma prendere confidenza con questi fondamentali vi permetterà di fare molto di più. Sfogliate gli effetti presenti su ShaderToy e vedete se riuscite  a capirne alcuni o a riprodurli!

Una cosa che non ho menzionato in questo tutorial sono i Vertex Shaders. Sono sempre scritti nello stesso linguaggio, tranne che agiscono su ogni singolo vertice invece che su ciascun pixel e restituiscono una posizione come avviene per il colore. I Vertex Shaders sono di solito responsabili della proiezione di una scena 3D sullo schermo (una cosa che è integrata nella maggior parte pipeline grafiche). I pixel shader sono i responsabili di molti degli effetti avanzati che si vedono ed è per questo che sono il nostro obiettivo.

Sfida finale: Sai scrivere uno shader che rimuove lo schermo verde nei video su ShaderToy e che aggiunge un altro video come sfondo per primo?

Questo è tutto per questa guida! Apprezzerei molto i vostri commenti e domande. Se c'è qualcosa di specifico che desiderate approfondire, vi prego di lasciare un commento. Le guide future potrebbero includere temi come le basi dei sistemi di illuminazione o il modo di fare una simulazione dei fluidi o la creazione di shader per una piattaforma specifica.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.