2013-01-07 16 views
5

Ho una scena con diversi modelli con posizioni e rotazioni individuali. Date le normali, gli shader applicano una luce bidirezionale semplice a ciascun pixel.Ruota Normale in Shader

Questo è il mio vertex shader.

#version 150 

in vec3 position; 
in vec3 normal; 
in vec2 texcoord; 

out vec3 f_normal; 
out vec2 f_texcoord; 

uniform mat4 model; 
uniform mat4 view; 
uniform mat4 proj; 

void main() 
{ 
    mat4 mvp = proj * view * model; 

    f_normal = normal; 
    f_texcoord = texcoord; 

    gl_Position = mvp * vec4(position, 1.0); 
} 

Ed ecco il frammento shader.

#version 150 

in vec3 f_normal; 
in vec2 f_texcoord; 

uniform sampler2D tex; 

vec3 sun = vec3(0.5, 1.0, 1.5); 

void main() 
{ 
    vec3 light = max(0.0, dot(normalize(f_normal), normalize(sun))); 

    gl_FragColor = texture(tex, f_texcoord) * vec4(light, 1.0); 
} 

Per oggetti senza rotazione funziona correttamente. Ma per i modelli ruotati viene anche ruotata l'illuminazione, il che, naturalmente, non dovrebbe essere il caso.

Il motivo è che le normali non vengono ruotate. Ho già provato f_normal = model * normal; ma questo applica sia la rotazione che la trasformazione alle normali.

Quindi, come posso ruotare le normali nel vertex shader prima di inviarle allo shader di frammenti per l'illuminazione? Qual è un approccio comune?

risposta

4

È necessario trasformare le normali, mediante le 3 righe/colonne superiori della matrice di visualizzazione modello-vista. (Se si sta eseguendo un ridimensionamento, è necessario utilizzare la trasposizione inversa di questa matrice. See this article).

mat3 normalMatrix = mat3(mvp); 
normalMatrix = inverse(normalMatrix); 
normalMatrix = transpose(normalMatrix); 
f_normal = normalize(normal * normalMatrix); 
// You should also send your tranformed position to the fragment shader 
f_position = vec3(mvp * vec4(position, 1.0)); 

Nel proprio frammento di ombreggiatura, è necessario calcolare la distanza dalla sorgente luminosa al frammento e normalizzarla. Trova il prodotto punto del normale e il vettore della luce e moltiplicalo per il colore della luce.

vec3 light = normalize(sun - f_position); 
light = max(dot(f_normal, light), 0.0) * vec3(1.0, 1.0, 1.0); 
gl_FragColor = texture(tex, f_texcoord) * vec4(light, 1.0); 

C'è sicuramente spazio per l'ottimizzazione nel mio codice.

Raccomando questo libro OpenGL 4.0 Shading Language Cookbook.

+0

Grazie. Mi consiglierebbe di inviare allo shader una matrice normale separata (inclusa solo la rotazione del modello ma non la ridimensionamento o la trasformazione)? Ho visto questa pratica da qualche parte e mi chiedo se sia meglio che ri-estrarre la rotazione dalla matrice del modello nello shader. – danijar

+0

Mi dispiace, il mio tempo è scaduto durante la modifica del commento precedente ... Penso che il ridimensionamento sia necessario, perché può modificare la distanza di una superficie dalla sorgente di luce. E poiché hai bisogno della scala, devi correggere la scala usando la trasposizione inversa della matrice 3x3 superiore. Io mando la mia matrice normale allo shader come un'uniforme però, in modo che venga calcolata una volta sola invece di una volta per vertice.Potrebbe tuttavia essere necessaria una libreria matematica, poiché la piattaforma potrebbe non fornire funzioni per eseguire questi calcoli a matrice. – bwroga

+0

Questo ha senso. A proposito, io uso GLM. – danijar

0

La seguente soluzione funziona con i miei modelli, ma non ho una conoscenza approfondita della matematica che ci sta dietro. Questa è la mia fonte: Adding Depth and Realism - Surface Normals

la trasformazione è necessario applicare ai vettori normali è indicato con: N'= N * (M -1) T

In pratica, questo significa che si moltiplica le tue normali (N) per la trasposizione inversa della tua matrice ModelView (M). Se la tua matrice M è 4x4, dovresti semplicemente usare il risultante quadrante 3x3 in alto a sinistra (M -1) T per la normale moltiplicazione.

Ancora una volta, questo funziona per me ma non riesco a spiegare troppo bene la matematica.

+0

Grazie, ho capito. – danijar