A partire da OpenGL 3.0, la maggior parte delle funzionalità che per un qualche motivo sono state giudicate incompatibili o limitative per gli shader sono state gradualmente dichiarate deprecate o eliminate del tutto. Rimangono disponibili per quelle schede video che hanno deciso di mantenere la retrocompatibilità, che fortunatamente sono, per ora, la maggior parte.
La giustificazione di ciò riguarda la complessità crescente di dover mantenere sia gli shader che la vecchia Fixed Function Pipeline e le innumerevoli funzioni (più di 500) per modificare i suoi parametri. Questa complessità ricade quasi completamente sulle spalle dei produttori delle schede video e dei loro driver, che devono implementare quelle funzioni.
In particolare, l'architettura degli shader impone che il compilatore sia implementato all'interno del driver stesso, siccome ciascuna scheda video usa un diverso linguaggio macchina proprietario e deve poter compilare e ottimizzare il codice GLSL in quel linguaggio.
Per evitare di appesantire l'API, e siccome attraverso gli shader tutte quelle funzionalità possono essere reimplementate comunque in modo molto più preciso ed adeguato alle esigenze, OpenGL ha scelto di eliminarne gran parte. In futuro, si ipotizza che la stessa Fixed Function Pipeline possa essere abolita e che l'uso degli shader divenga obbligatorio.
Purtroppo, ciò ha fatto sì che fosse molto più lungo e complicato partire da zero e avere un programma OpenGL funzionante, a causa del gran numero di funzionalità da reimplementare. Così sono nate un gran numero di librerie di alto livello, al di sopra di OpenGL, che tentano di proporre al programmatore un'API più amichevole.
La maggior parte delle variabili di stato di OpenGL sono state abolite. Questo include:
Con l'abolizione di glVertex, diventa impossibile definire vertici con il metodo tradizionale. Perciò sono aboliti anche glBegin e glEnd.
Se non è più possibile definire singoli vertici, diventa obbligatorio usare le funzioni che permettono di disegnare array di vertici. Prima si devono allocare tutti i vertici in memoria e poi essi sono inviati alla scheda video in blocco con la funzione glDrawElements o simili (vedere la reference OpenGL). L'attributo gl_VertexID rappresenta infatti l'indice del vertice all'interno dell'array.
Questo metodo è più efficiente per sfruttare il parallelismo degli shader e per grandi quantità di dati, ma un po' più lungo da implementare a mano da zero.
Le display list sono state abolite. Per aumentare l'efficienza, è possibile allocare i Buffer Object direttamente sulla memoria video, in modo che non debbano essere trasferiti su di essa ogni volta. Dopo averli configurati, il loro uso diventa equivalente a array allocati in RAM, anche se per ogni lettura o modifica è necessario passare attraverso funzioni OpenGL.
Come già detto, il vertex shader perde tutti gli attributi predefiniti, eccetto gl_VertexID e, in alcuni casi, gl_InstanceID.
La parola chiave attribute viene abolita e, al suo posto, si usa la parola chiave in, come se gli attributi arrivassero al vertex shader da uno shader precedente. Ad esempio:
in vec4 Position; in vec3 Normal; in float shininess;
Le variabili di uscita verso lo shader successivo sono definite con la parola chiave out. La parola chiave varying viene abolita.
Ci sono tre variabili di uscita predefinite per il vertex shader, che sono obbligatorie solo se lo shader è l'ultimo prima della rasterizzazione. Altrimenti, impostarle o usare variabili varying personalizzate non fa alcuna differenza.
out vec4 gl_Position; // posizione del vertice out float gl_PointSize; // dimensione del punto, //serve solo se si stanno disegnando punti (GL_POINTS) out float gl_ClipDistance[];
L'ultima variabile ha un elemento per ogni piano di clipping addizionale (definito con glClipPlane) e indica la distanza del vertice da quel piano. Se la distanza è negativa, il vertice viene eliminato in fase di clipping e il valore viene interpolato nel resto della primitiva, per eliminare tutte le parti dove risulti negativo. Se la distanza è positiva, il vertice viene mantenuto.
Il fragment shader riceve in ingresso tutte le variabili in uscita dallo shader precedente, opportunamente interpolate. Anche qui, la parola chiave varying è stata abolita.
in vec4 Color; in float matshininess; in vec3 matspecular;
Il fragment shader deve produrre in uscita almeno una variabile. Il nome di questa variabile non importa, ma deve essere di tipo vec4 e deve essere scritta con il colore che il fragment shader ha prodotto per il frammento.
out vec4 ffragColor;
Questa funzione:
void glBindFragDataLocation(GLuint program, GLuint colorNumber, const char * name);
può essere usata per indicare a GLSL quale variabile di uscita del fragment shader contiene il colore, se ce n'è più d'una. program è l'id del programma, name è il nome della variabile e colorNumber di solito è zero, a meno che non si stia eseguendo il rendering a più buffer contemporaneamente.
Delle altre variabili predefinite di GLSL per il fragment shader, sopravvivono soltanto gl_FragCoord e gl_FragDepth.
A parte la già diffusamente citata abolizione della parola chiave varying, la maggiore differenza che riguarda i geometry shader in GLSL 4.00 è la possibilità di usare direttive layout.
Invece di usare la funzione glProgramParameteriEXT per impostare il tipo di primitive di input e di output, essi vengono impostati con la seguente sintassi, da includere all'inizio del Geometry Shader, dopo la dichiarazione di versione:
layout(triangles) in; layout(triangle_strip, max_vertices = 3) out;
Come si può immaginare, la prima direttiva definisce il tipo di primitiva di input e accetta come argomento:
points da GL_POINTS lines da GL_LINES triangles da GL_TRIANGLES lines_adjacency da GL_LINES_ADJACENCY triangles_adjacency da GL_TRIANGLES_ADJACENCY
La seconda direttiva definisce nel secondo parametro il numero massimo di vertici che il geometry shader può produrre. Il primo parametro indica il tipo di primitiva in uscita e può essere:
points da GL_POINTS line_strip da GL_LINE_STRIP triangle_strip da GL_TRIANGLE_STRIP
Un parametro opzionale della direttiva layout permette di instanziare il geometry shader più volte per ogni singola primitiva. Le istanze sono identiche, ma differiscono tra loro per il valore della variabile predefinita intera gl_InstanceID. Questo è utile, per esempio, se il geometry shader deve elaborare allo stesso modo ciascuno dei vertici in ingresso.
layout(triangles, invocations = 3) in;
gl_InstanceID varia tra 0 e invocations-1, cioè, in questo caso, tra 0 e 2.
Siccome il Geometry Shader, se presente, è l'ultimo shader prima della rasterizzazione, deve sempre impostare gl_Position prima di emettere ogni vertice, nonché gl_ClipDistance e gl_PointSize se necessario.
Eventuali gl_Position, gl_ClipDistance o gl_PointSize impostati da shader precedenti sono disponibili in un array, chiamato gl_in. Ad esempio:
gl_in[0].gl_Position
accede al valore di gl_Position emesso dal vertex shader che ha elaborato il primo vertice della primitiva.
Copyright © 2011 Riccardo Monica
Eccetto ove espressamente indicato altrimenti, il contenuto di questa pagina è disponibile secondo la licenza Creative Commons Attribuzione - Condividi allo stesso modo 3.0.