Il tessellation control shader viene eseguito una volta per ogni vertice della patch di input, eccetto in alcuni casi particolari (vedere la sezione layout, più avanti). Al contrario del vertex shader può accedere a tutte le informazioni della patch corrente. Lo scopo principale è quello di istruire il Tessellator per suddividere la patch in un maggior numero di primitive e dunque renderla più precisa. Questa operazione è detta appunto tassellatura.
Una patch è una particolare primitiva che viene definita tramite la costante GL_PATCHES, che è un tipo di primitiva utilizzabile analogamente a GL_LINES e GL_TRIANGLES. Le patch possono essere composte da un qualunque numero di vertici, definito dal programmatore con la funzione C:
void glPatchParameteri(GLenum pname,GLint value);
dove pname deve essere GL_PATCH_VERTICES e value è il numero di vertici di cui la patch è composta.
È sempre necessario avere un tessellation control shader attivo per poter usare il tipo di
primitiva GL_PATCHES, e viceversa è obbligatorio usare
GL_PATCHES se c'è un tessellation shader attivo.
È inoltre obbligatoria la presenza di un vertex shader e di un tessellation evaluation shader,
ma non di un geometry shader.
La funzione principale di un tessellation control shader è quella di impostare due variabili. Esse devono essere scritte una volta da almeno una delle varie istanze, ma non è necessario che siano impostate da tutte, perché sono condivise da tutti gli shader che operano sulla stessa patch.
float gl_TessLevelInner[2]; float gl_TessLevelOuter[4];
Queste due variabili hanno la funzione di configurare il Tessellator e hanno valore minimo 1.
All'aumentare di gl_TessLevelInner[0] e gl_TessLevelInner[1], nuove primitive sono create
all'interno della patch e connesse ai suoi vertici (gl_TessLevelInner[1] ha effetto solo per i quadrilateri). I lati della
patch originali non sono modificati.
All'aumentare di gl_TessLevelOuter i lati della patch
originale sono suddivisi in segmenti e la primitiva originale è suddivisa a raggiera, con nuovi lati che vanno dal punto centrale ai nuovi vertici.
gl_TessLevelOuter ha un numero di componenti pari al
numero di lati (3 per i triangoli e 4 per i quadrati) del tipo di patch in ingresso. Se si assegnano valori diversi
alle componenti del vettore, lati diversi saranno scomposti in un numero di segmenti diverso.
In ogni caso, l'effetto della modifica di queste due variabili può cambiare significativamente a seconda dell'implementazione dell'algoritmo di tessellatura implementato in ciascun particolare modello di scheda video.
L'effetto migliore si ottiene per valori simili delle le due variabili.
Come nel caso del geometry shader, tutti gli attributi in uscita dal Vertex Shader sono raggruppati in vettori in input al tessellation control shader. In particolare, l'uso della variabile di ingresso gl_in è identico.
in vec3 invaryvar[]; out vec3 outvaryvar[];
Una variabile globale intera, gl_InvocationID, contiene l'id del vertice della patch per cui il tessellation control shader è stato allocato.
Anche il tessellation control shader richiede che, all'inizio, sia specificata una direttiva di tipo layout. La sintassi è la seguente:
layout(vertices = 3) out;
La costante intera indica il numero di vertici che vengono prodotti dal tessellation control shader e inviati al Tessellator. Per ciascuna patch, il tessellation control shader è invocato quel numero di volte. Inoltre, tutte le variabili in uscita verso il tessellation evaluation shader devono essere array di quella lunghezza. Questo spiega perché nella sezione Variabili si sia lasciato vuoto lo spazio tra le parentesi quadre: la loro lunghezza è ottenuta automaticamente.
Tipicamente, il numero di invocazioni del tessellation control shader è uguale al numero di vertici della patch in ingresso, impostato con glPatchParameteri. Tuttavia, se qualche interpolazione è eseguita già all'interno del tessellation control shader, può accadere che non sia necessario produrre un vertice per ogni vertice in ingresso. In questo caso, questo numero può essere diverso.
La funzione predefinita
void barrier();
permette di ottenere una rudimentale sincronizzazione tra le varie istanze dei tessellation control shader di una stessa patch. La sincronizzazione è necessaria perché esse possono scrivere tutte sugli stessi dati, cosa che può causare conflitti e situazioni di corsa.
barrier garantisce di essere eseguita contemporaneamente in tutte le istanze. Ciò significa che se per qualche motivo una istanza arriva ad eseguire la funzione prima delle altre, essa si deve fermare e attendere che tutte siano pronte. Se compare più volte nel codice, il suo effetto è ripetuto: prima sono sincronizzate le prime esecuzioni, poi le seconde, eccetera.
Questa funzione può essere chiamata solo all'interno della funzione main e al di fuori di qualsiasi blocco di codice condizionato.
Segue un "semplice" esempio di Tessellation Control Shader. Notare che è stato necessario reimplementare un gran numero di variabili che, in versioni di OpenGL precedenti, erano predefinite.
#version 400 layout(vertices = 3) out; in vec3 vPosition[]; in vec3 vNormal[]; in vec4 vColor[]; in float vmatshininess[]; in vec3 vmatspecular[]; out vec3 tcPosition[]; out vec3 tcNormal[]; out vec4 tcColor[]; out float tcmatshininess[]; out vec3 tcmatspecular[]; uniform float tesslevelinner; uniform float tesslevelouter; #define ID gl_InvocationID void main() { tcPosition[ID] = vPosition[ID]; tcNormal[ID] = vNormal[ID]; tcColor[ID] = vColor[ID]; tcmatshininess[ID] = vmatshininess[ID]; tcmatspecular[ID] = vmatspecular[ID]; if (ID == 0) { gl_TessLevelInner[0] = tesslevelinner; gl_TessLevelOuter[0] = tesslevelouter; gl_TessLevelOuter[1] = tesslevelouter; gl_TessLevelOuter[2] = tesslevelouter; } }
Il contenuto del listato sovrastante è derivato dal codice rilasciato nel pubblico dominio da The Little Grasshopper ed è rilasciato nuovamente da me nel pubblico dominio.
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.