Rosario 3D

My developer blog

Lezione su gli shader, parte prima ITA

Questo post è in italiano e serve per spiegare la lezione che ho fatto oggi (4 Maggio) al corso di realtà virtuale. Appena ho un po’ di tempo la tradurrò in inglese.

I primi shaders

Tramite vertex shader potete leggere i dati provenienti da openGL manipolarli e inviare altri dati agli stadi successivi della pipeline. Il vertex shader deve scrivere sulla variabile gl_Position, in questo prendo la posizione del vertice (non trasformata) e la invio alla pipeline

void main(){
  gl_Position = gl_Vertex;
}

La pileline da i vertici genererà i poligoni che poi verranno renderizzati tramite fragment shader. Il fragment shader deve scrivere su la variabile gl_FragColor

Attenzione!! gl_FragColor e’ stata deprecata in openGL 3.0 e viene sostituita da variabili custom di output. Per ulterio dettagli potete leggere la sezione 3.9.2 delle specifiche openGL (Shader output)

void main(){
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

Le trasformazioni possono essere applicate tramite le matrici si puo’ accedere alle matrici tramite le variabile gl_ProjectionMatrix e gl_ModelViewMatrix. Queste due variabili sono invariati per tutti i vertici della geometria, questo tipo di variabili sono dette uniform.

void main(){
  gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;
}

È possibile specificare dei parametri agli shader tramite degli uniform personalizzate. Il nome non può iniziare con gl_, visto che questi nomi sono riservati alle variabili predefinite. In questo caso si e’ passato un colore di riempimento.

uniform vec3 fillColor;

void main(){
  gl_FragColor = vec4(fillColor, 1.0);
}

Dal programma che usa lo shader si dovrà specificare il valore dell’uniform, sia il nome che il tipo dovrà corrispondere a quello della variabile

Applichiamo una texture

Per applicare le texture abbiamo bisogno delle coordinate UV, coordinate che possiamo prendere dalla variabile gl_MultiTexCoord0 (si possono usare fino a 8 texture coordinate)
Siccome useremo le UV map nel fragment shader dobbiamo usare una variabile di passaggio. Le variabili di tipo varying permettono di passare i valori i diversi stage della pipeline. Tra vertex shader e fragment i valori verranno
interpolati linearmente.

Attenzione!! Con l’aumentare degli stage della pipeline la keyword varying e’ stata deprecata, ed e’ stata sostituita dalle piu’ esplicite in e out. In questo caso nel vertex shader avremmo una variabile di tipo out e nel fragment una variabile di tipo in.

varying vec2 uvMap;

void main(){
  uvMap = gl_MultiTexCoord0.xy;
  gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;
}

La variabile di tipo varying deve essere presente anche nel fragment shader altrimenti la fase di linking genererà un errore. Per leggere la texture bisogna usare un tipo particolare di dato, il sampler2D. Il sampler2D è un intero che può essere usato solo per specificare quale texture usare. In particolare indica quale texture unit usare tra le texture 2D. Ci sono sampler anche per le texture 1D, 3D, cubeMap e altri tipi particolari come per le shadow, nelle versioni più recenti di openGL ci sono ulteriori tipi di sampler per accedere ai moderni textureBuffer e multi sample texture.

Tramite il sampler e le coordinate uv possiamo leggere il valore della texture (texel) usando la funzione texture2D. Di questa funzione esistono diverse varianti, questa è la più semplice che calcola in automatico il livello mipmap da usare.

uniform sampler2D diffuse;
varying vec2 uvMap;

void main(){
  gl_FragColor = texture2D(diffuse, uvMap);
}

Possiamo usare due texture e combinarle assieme, nel fragment shader dovremmo dichiarare un’altra variabile varying e leggere un’altra texCoord

varying vec2 uvMap;
varying vec2 uvLightmap;

void main(){
  uvMap       = gl_MultiTexCoord0.xy;
  uvLightmap  = gl_MultiTexCoord1.xy;
  gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;
}

Nel fragment shader dobbiamo accedere alle due texture e moltiplicare una per l’altra. Attenzione, la lightmap potrebbe contenere delle informazioni sull’alpha. Se vogliamo usare solo l’alpha delle texture diffusiva dobbiamo separare i calcoli tra colore e alpha.

uniform sampler2D diffuse;
uniform sampler2D lightmap;

varying vec2 uvMap;
varying vec2 uvLightmap;

void main(){
  vec4 diff = texture2D(diffuse, uvMap);
  vec3 light= texture2D(lightmap, uvLightmap).xyz;
  gl_FragColor = vec4(diff.xyz * light , diff.a);
}

Vertex Color

Le informazioni sull’illuminazione posso essere lette anche da vertice. In particolare possiamo leggere il colore per vertice nella variabile gl_Color (e gl_SecondaryColor). Supponiamo di aver memorizzato le informazioni dell’ambient occlusion ne il vertex color.

varying vec4 occlusion;

void main(){
  occlusion = gl_Color;
  gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;
}

nel fragment possiamo banalmente copiare il colore per vertice nella variabile di output.

varying vec4 occlusion;

void main(){
  gl_FragColor = vec4(occlusion);
}

Questa tecnica si presta bene per mesh molto dense altrimenti si potranno notare artefatti dovuti all’interpolazione tra i vertici.

Un altra cosa che possiamo fare con i vertex shader è animare la mesh, possiamo spostare i vertici a piacimento con la tecnica che preferiamo. Le animazioni tramite bones vengono eseguite nel vertex shader. In questo caso vediamo un animazione procedurale tramite due sinusoidi. Da programma possiamo modificare l’uniform phase per eseguire l’animazione.

uniform float phase;
varying vec4 occlusion;

void main(){
  occlusion = gl_Color;
  vec4 pos = gl_Vertex;
  pos.y = gl_Vertex.y + 0.2*cos(gl_Vertex.y*5.0+phase);
  gl_Position = gl_ModelViewProjectionMatrix * pos;
}

Oltre alle variabili gl_ModelViewMatrix e gl_ProjectionMatrix abbiamo a disposizione la matrice gl_ModelViewProjectionMatrix in cui le due sono già moltiplicate a priori.

Nel caso il vertice non debba essere spostato possiamo usare la funzione ftransform() che trasformerà in maniera efficiente e accurata usando le matrici modelview e projection.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: