Funzioni e operatori

Come per le variabili, anche la definizione e la chiamata di funzioni hanno all'incirca la stessa sintassi del C.

Funzione main

Lo shader principale (per un certo tipo e un certo program) è lo shader che definisce la procedura void main(). Per cominciare, perciò:

void main()
{
  /* inserire codice qui */
}

Per ogni tipo di shader e per ogni program deve essere definita una sola funzione main(). Questa funzione è quella che viene chiamata dalla GPU quando lo shader comincia la sua esecuzione.

Definizione di funzione

Il tipo di dato di ritorno può essere uno qualunque dei tipi di dato fondamentale (anche i vettori e le matrici), una struttura oppure void per indicare nessun valore di ritorno.
Per ritornare un valore si usa la parola chiave return, che termina anche la funzione.

vec4 GetX()
{
  float w = 1.0;
  return vec4(1.0,0.0,0.0,w);
}

I parametri per la funzione sono definiti come in C, con la differenza che non esistono i puntatori. Perciò, per permettere di ritornare un valore tramite i parametri, sono state definite tre parole chiave. Se nessuna parola chiave è specificata, il compilatore assume in per default.

La parola chiave in indica che il parametro è un parametro di ingresso alla funzione e corrisponde al passaggio di parametri per valore. La variabile può essere modificata all'interno della funzione, ma la modifica non influisce all'esterno della funzione.

float Get2x(in float x)
{
  return 2.0 * x;
}

La parola chiave out indica che il parametro è un parametro di uscita. Può (deve) essere scritto all'interno della funzione, ma il suo valore è indeterminato quando la funzione comincia ad essere eseguita.

int Dividi(in int a,in int b,out int resto)
{
  resto = a % b;
  return a / b;
}

La parola chiave inout indica che il parametro è sia un parametro di ingresso sia un parametro di uscita. Corrisponde esattamente alla creazione di un alias per il parametro attuale, cioè al passaggio di parametri per riferimento.

La chiamata di funzione avviene esattamente come in C:

void main()
{
  int x = 5;
  int y = 2;
  int r;
  int q;
  q = Dividi(x,y,r);
}

Prototipi

I prototipi in GLSL sono dichiarati allo stesso modo del C.

int Dividi(in int a,in int b,out int resto);

Oppure, è del tutto equivalente senza i nomi dei parametri:

int Dividi(in int,in int,out int);

La finalità è simile: avvertono il compilatore che una funzione sarà definita successivamente nel codice e permettono già di usarla. L'utilità è però limitata, perché la ricorsione non è consentita in GLSL.

Invece, i prototipi servono soprattutto per usare funzioni che sono definite altrove. Se una funzione è definita in un altro shader dello stesso tipo, può essere importata copiando il suo prototipo all'inizio dello shader. La funzione viene poi collegata quando gli shader sono uniti insieme nello stesso program.

Operatori

GLSL consente di usare i normali operatori aritmetici e booleani anche su operandi costituiti da un vettore di elementi.

La somma e la differenza sono definite anche tra vettori e matrici:

vec4 a = vec4(1.0,2.0,3.0,4.0);
vec4 b = vec4(1.0,-1.0,2.0,-4.0);
vec4 piu = a + b;                   // somma di a e b
vec4 meno = a - b;                  // differenza tra a e b
vec4 neg = -b;                      // opposto di b
a += b;                             // abbreviato per a = a + b

Il prodotto * è ampliato e può essere usato anche per matrici e vettori, o vettori e scalari. La divisione è estesa tra vettori e scalari.

vec4 a = vec4(1.0,2.0,3.0,4.0);
float b = 2.0;
vec4 c = a * b;                     // moltiplica tutte le componenti di a per b
c *= b;                             // abbreviato per d = d * b

vec4 d = a / b;                     // divide tutte le componenti di a per b
d /= b;                             // abbreviato per d = d / b

mat2 matrice = mat2(0.0,1.0,0.0,1.0);
vec2 v = matrice * vec2(2.0,1.0);

Di solito un operatore binario che può agire su dati scalari può anche accettare due vettori di uguale tipo e dimensione. In questo caso, l'operatore viene applicato in parallelo a ciascuna coppia di elementi e il risultato è ancora un vettore.


vec3 f1 = vec3(2.0,3.0,4.0);
vec3 f2 = vec3(-1.0,2.0,4.0);
vec3 m = f1 * f2;                            // m contiene (-2.0,6.0,16.0)
vec3 d = f1 / f2;                            // d contiene (-2.0,1.5,1.0)

bvec4 a = bvec4(true,false,true,false);
bvec4 b = bvec4(false,false,false,true);
bvec4 o = a || b;                            // o contiene (true,false,true,true)

Funzioni predefinite

GLSL fornisce un gran numero di funzioni matematiche predefinite, che sono già definite all'interno di uno shader. Anche se sono invocate come funzioni, molte di esse sono in realtà operatori che sono implementati a livello hardware, allo stesso modo di moltiplicazione e divisione.

Seguono le più importanti. Molte di esse sono estese per vettori di ogni dimensione, quando l'operazione ha senso.

float dot(in vec4 a,in vec4 b);      // prodotto scalare (dot product)
vec3 cross(in vec3 a,in vec3 b);     // prodotto vettoriale (cross product)
vec3 normalize(in vec3 a);           // estrae il versore dal vettore, cioè lo divide per il suo modulo

float abs(in float a);               // valore assoluto del parametro. Esiste anche in versione vettoriale e per interi
float sign(in float a);              // -1.0 se a < 0.0, 0.0 se a = 0, 1.0 se a > 0.0
vec4 clamp(in vec4 a, in float min, in float max); // ritorna a, ma se contiene valori > max, essi sono posti = a max, se sono < min, vengono posti = a min

float sin(in float x);               // seno (radianti)
float cos(in float x);               // coseno (radianti)
float tan(in float x);               // tangente (radianti)

float sqrt(in float x);              // radice quadrata
float pow(in float x, in float y);   // x elevato alla y

float ceil(in float x);              // arrotondamento per eccesso
float floor(in float x);             // arrotondamento per difetto

vec3 reflect(in vec3 ray, in vec3 normal); // riflette il vettore ray rispetto a normal
vec3 mix(in vec3 x, in vec3 y,in float a); // uguale a: x * (1.0 - a) + y * a

Altre funzioni sono elencate nella reference di GLSL.


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.