Chapitre VIII

Transcription

Chapitre VIII
Chapitre VIII : Vertex et Fragment Shaders
Programmation 3D
Fabrice Aubert
[email protected]
Licence/Master
USTL - IEEA
2008-2009
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
1 / 28
Introduction
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
2 / 28
Pipeline
Les shaders sont des programmes qui se substituent à des phases du
pipeline de rendu.
Vertex shader : programme se substituant à la phase de
transformation des sommets et au calcul d’éclairement/coordonnées
de texture aux sommets.
Pixel shader : programme qui se substitue à la phase d’attribution de
la couleur et des coordonnées de texture aux fragments (lors de la
rasterization).
But : donner une grande liberté à la manipulation des sommets et des
fragments... par ailleurs, les shaders sont exécutés par la carte
graphique.
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
3 / 28
Langage
Langage de haut niveau : Cg (nVidia, interfaçable avec Direct 3D et
OpenGL), HLSL (direct3D), GLSL (OpenGL).
Choix : GLSL (OpenGL 2.1/GLSL 1.2).
Syntaxe très similaire au C.
Manuel de référence :
http://www.opengl.org/sdk/libs/OpenSceneGraph/glsl_quickref.pdf
IDE pour shaders : OpenGL shader designer
(http://www.opengl.org/sdk/tools/ShaderDesigner/),
RenderMonkey (http://developer.amd.com/gpu/rendermonkey).
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
4 / 28
Vertex shader
Un vertex shader possède en entrée des attributs de sommets (i.e. tout ce qui concerne un
sommet : position, couleur, coordonnée de texture, etc). L’utilisateur peut spécifier des
attributs et/ou utiliser ceux prédéfinis par OpenGL (« built-in attributes » : gl_Vertex,
gl_Normal, ...)
Exemple : le code suivant affecte la position gl_Vertex et la couleur gl_Color du vertex
shader :
g l B e g i n (GL POLYGON ) ;
glColor3f (0.2 ,0.4 ,0.6);
glVertex3f ( −1.0 ,1.0 ,2.0);
...
glEnd ( ) ;
Pour paramétrer le shader, on peut également utiliser et définir des variables qui seront
constantes lors du tracé d’une primitive donnée : ce sont les variables dites uniform. Les
états OpenGL sont par exemple accessibles grâce à des uniforms prédéfinis :
gl_ModelViewMatrix, gl_Light, ...
Les sorties d’un vertex shader (i.e. mise à jour de variables) sont qualifiées de varying :
leurs valeurs vont être interpolées (moyenne pondérée entre les sommets) et passées au
fragment shader.
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
5 / 28
Vertex Shader : exemple simple
Fichier exemple_vertex.vert :
v o i d main ( ) {
g l P o s i t i o n = g l P r o j e c t i o n M a t r i x ∗ gl ModelViewMatrix ∗ gl Vertex ;
}
On se contente ici de faire ce qui est fait par défaut par le pipeline
OpenGL : Pproj = PROJECTION ∗ MODELVIEW ∗ Plocal .
Les gl_ sont des « variables »GLSL prédéfinies.
gl_Position doit obligatoirement être affecté lors d’un vertex
shader (sortie spéciale).
Types et qualifications de ces variables prédéfinies (donc inutile de les
déclarer) :
uniform mat4 gl_ModelViewMatrix;
uniform mat4 gl_ProjectionMatrix;
attribute vec4 gl_Vertex;
vec4 gl_Position; // variable spéciale
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
6 / 28
Pipeline
Problème : toutes les opérations par défaut ne sont plus faites
(substitution totale). Par exemple, si vous voulez simplement changer
la génération des coordonnées de texture, il vous faut programmer les
transformations, le calcul d’éclairement, ...
Pas de connaissance des autres sommets dans un vertex shader.
Cependant : accès à tous les états d’OpenGL (material, texture, ...)
grâce aux uniform.
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
7 / 28
Fragment Shader
Un fragment shader possède en entrée les valeurs interpolées des
sommets : position, couleur, coordonnée de texture, depth. Ces
variables sont qualifiées de varying.
Les sorties du fragment shader consiste principalement à écrire sa
couleur gl_FragColor.
Pas de connaissance des autres fragments (pixels voisins par exemple).
Accès aux états d’OpenGL (variables uniforms prédéfinies).
Remarque : on manipule bien un fragment et non un pixel (par
exemple, un fragment shader qui déplacerait un pixel sur l’écran n’a
aucun sens).
Pas de blending (opération faite après l’exécution du pixel shader).
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
8 / 28
Fragment Shader : exemple
Exemple fichier : exemple_pixel.frag
v o i d main ( ) {
g l F r a g C o l o r = vec4 ( 0 . 4 , 0 . 4 , 0 . 8 , 1 . 0 ) ;
}
La variable gl_FragColor doit être obligatoirement affectée dans un
fragment shader.
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
9 / 28
Intégration des shaders en OpenGL
Dans ce cours : spécification OpenGL 2.1 (version actuelle 3.1 : pas
de modification pour l’intégration).
Etapes pour la définition des shaders :
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
10 / 28
Exemple
v o i d myShader ( ) {
GLchar ∗ v s o u r c e , ∗ f s o u r c e ;
GLuint v , f , p ;
// c r e a t i o n h a n d l e :
v = g l C r e a t e S h a d e r ( GL VERTEX SHADER ) ;
f = g l C r e a t e S h a d e r (GL FRAGMENT SHADER ) ;
// un programme = ( au moins ) un f r a g m e n t e t un v e r t e x s h a d e r
p = glCreateProgram ( ) ;
glAttachShader (p , v ) ;
glAttachShader (p , f ) ;
// l e c t u r e du GLSL s o u r c e :
v s o u r c e = t e x t F i l e R e a d ( ”e x e m p l e v e r t e x . v e r t ” ) ;
f s o u r c e = t e x t F i l e R e a d ( ”e x e m p l e p i x e l . f r a g ”) ;
// a f f e c t a t i o n d e s s o u r c e s aux h a n d l e s
g l S h a d e r S o u r c e ( v , 1 , ( c o n s t c h a r ∗∗)& v s o u r c e , NULL ) ;
g l S h a d e r S o u r c e ( f , 1 , ( c o n s t c h a r ∗∗)& f s o u r c e , NULL ) ;
// c o m p i l a t i o n d e s s o u r c e s
glCompileShader ( v ) ;
glCompileShader ( f ) ;
// m i s e en p l a c e d a n s l e
glLinkProgram (p ) ;
pipeline
:
g l U s e P r o g r a m ( p ) ; // s i p=0 , p i p e l i n e p a r d é f a u t
}
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
11 / 28
1 GLSL (GL Shading Language)
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
12 / 28
Types et qualifications
mat4 type matrice (16 floats) ; vec4 type vecteur (4 floats) ; etc
Exemples :
v o i d main ( ) {
v e c 4 a=v e c 4 ( 0 . 1 , 0 . 2 , 0 . 3 , 0 . 4 ) ;
f l o a t b=a . x ;
v e c 2 c=a . xy ;
f l o a t d=a . w ;
mat2 e = mat2 ( 1 . 0 , 0 . 0 , 1 . 0 , 0 . 0 ) ;
f l o a t f=e [ 1 ] [ 1 ] ;
v e c 2 g=e [ 1 ] ; // 2 i e m e c o l o n n e .
...
}
uniform : constant lors d’une primitive (glBegin(...) ... glEnd()) ;
uniquement en lecture seule par le shader.
attribute : peut changer entre chaque sommet. En lecture seule
(sauf spécial).
varying : valeur qui sera interpolée (généralement en écriture dans
un vertex, et en lecture dans un pixel).
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
13 / 28
Communiquer avec un shader
Les shaders peuvent lire (et non écrire) des variables globales passées
par OpenGL : utilisation des uniform.
vertex shader GLSL :
u n i f o r m f l o a t t i m e ; // v a r i a b l e g l o b a l e au s h a d e r
v o i d main ( v o i d ) {
vec4 v = vec4 ( g l V e r t e x ) ;
v . z = v . z+s i n ( 5 . 0 ∗ s q r t ( v . x∗v . x+v . y∗v . y ) + t i m e ∗ 0 . 5 ) ∗ 0 . 2 5 ;
gl Position = gl ModelViewProjectionMatrix ∗ v ;
}
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
14 / 28
Modification de time
programme OpenGL :
i n t l o c a t i o n t i m e ; // h a n d l e p o u r l a v a r i a b l e t i m e
// du s h a d e r
f l o a t mon temps ; // v a r i e e n t r e c h a q u e image
...
v o i d myShader ( ) {
...
glLinkProgram (p ) ;
l o c a t i o n t i m e = g l G e t U n i f o r m L o c a t i o n ( p , ”t i m e ” ) ; // d é t e r m i n a t i o n du h a n d l e
// l e programme p d o i t a v o i r é t é l i n k é
}
...
void myDisplay ( void ) {
g l C l e a r ( GL COLOR BUFFER BIT | GL DEPTH BUFFER BIT ) ;
glUseProgram ( p ) ;
// a f f e c t a t i o n de l ’ u n i f o r m ”t i m e ” du s h a d e r à l a v a l e u r mon temps :
g l U n i f o r m 1 f ( l o c a t i o n t i m e , mon temps ) ;
// g l U n i f o r m f o n c t i o n n e u n i q u e m e n t s u r l e programme c o u r a n t ( donné p a r g l U s e P r o g r a m )
glutSolidTeapot (1);
glutSwapBuffers ( ) ;
mon temps +=0.01;
}
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
15 / 28
2 Un exemple : éclairement par pixel
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
16 / 28
Rendre le spéculaire
Eclairement par défaut d’OpenGL : calcul d’éclairement en chacun
des sommets puis interpolation des couleurs pour chacun des pixels.
Intérêt avec le fragment shader : provoquer un éclairement pour
chaque pixel tracé pour nuancer le mauvais rendu du spéculaire
d’OpenGL.
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
17 / 28
Rappel
Calcul du spéculaire :
Cs = Is ∗ Ks (V · R)s
avec R = 2(N · L)N − L
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
18 / 28
Principe
Dans le vertex shader : pour chaque sommet on détermine les valeurs
N, L et V (dans le repère de l’observateur).
En chacun des pixels on pourra alors récupérer ces valeurs interpolées
(moyennées).
(L, N et V seront donc qualifiés de varying).
Dans le fragment shader : on calcul le vecteur R puis le spéculaire
résultant.
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
19 / 28
Vertex shader
v a r y i n g v e c 3 N, L , V ;
v o i d main ( ) {
L=g l L i g h t S o u r c e [ 0 ] . p o s i t i o n . x y z ;
N=g l N o r m a l M a t r i x ∗ g l N o r m a l ;
v e c 4 v e r t e x=g l V e r t e x ;
v e r t e x=g l M o d e l V i e w M a t r i x ∗ g l V e r t e x ;
V=n o r m a l i z e (− v e r t e x . x y z ) ;
N=n o r m a l i z e (N ) ;
L=n o r m a l i z e ( L ) ;
gl Position = gl ProjectionMatrix∗vertex ;
}
normalize est fournie par GLSL pour normer un vecteur.
gl_LightSource[0] correspond à la LIGHT0. On a accès à tous les
paramêtres de LIGHT0.
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
20 / 28
Fragment Shader
v a r y i n g v e c 3 N, L , V ;
v o i d main ( ) {
v e c 3 N2 , L2 , V2 ;
vec3 R;
N2=n o r m a l i z e (N ) ;
V2=n o r m a l i z e (V ) ;
L2=n o r m a l i z e ( L ) ;
R=2.0∗ d o t ( N2 , L2 )∗N2−L2 ;
n o r m a l i z e (R ) ;
// i n t e n s i t é s p é c u l a i r e
float i spec ;
i s p e c=pow ( max ( d o t ( V2 , R ) , 0 . 0 ) , g l F r o n t M a t e r i a l . s h i n i n e s s ) ;
vec4 s p e c u l a i r e ;
s p e c u l a i r e=g l L i g h t S o u r c e [ 0 ] . s p e c u l a r ∗ g l F r o n t M a t e r i a l . s p e c u l a r ∗ i s p e c ;
gl FragColor = speculaire ;
}
Il faut normer N, L et V.
dot est la fonction produit scalaire.
On accède au Ks par gl_FrontMaterial.specular, au Is par
gl_LightSource[0].specular, et à la brillance s par gl_FrontMaterial.shininess
(uniforms pré définis).
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
21 / 28
Remarques
Attention : il n’y a que le spéculaire ici. Il faut ajouter le diffus et
l’ambiant dans le vertex et le pixel shader.
Remarque : pour ces 2 derniers, il suffit de calculer la couleur aux
sommets, puis d’interpoler cette couleur (on ajoute directement à
couleur les 2 varying provenant du diffus et de l’ambiant).
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
22 / 28
3 Accès aux textures
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
23 / 28
Exemple : multi-texturing simple
Vertex :
v a r y i n g vec2 coord0 , coord1 ;
v o i d main ( ) {
c o o r d 0=g l M u l t i T e x C o o r d 0 . s t ;
c o o r d 1=g l M u l t i T e x C o o r d 1 . s t ;
g l P o s i t i o n = g l P r o j e c t i o n M a t r i x ∗gl ModelViewMatrix∗gl Vertex ;
}
Fragment :
v a r y i n g vec2 coord0 , coord1 ;
uniform sampler2D tex1 , t e x 0 ;
v o i d main ( ) {
vec4 color0 , c o l o r 1 ;
c o l o r 0=t e x t u r e 2 D ( t e x 0 , c o o r d 0 ) ;
c o l o r 1=t e x t u r e 2 D ( t e x 1 , c o o r d 1 ) ;
gl FragColor = color0∗color1 ;
}
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
24 / 28
Le type sampler2D
Doit être déclaré en uniform pour associer la variable du shader à une
unité de texture.
Exemple :
...
g l A c t i v e T e x t u r e ( GL TEXTURE2 ) ;
g l B i n d T e x t u r e ( GL TEXTURE 2D , t e x i d ) ;
...
l o c a t i o n=g l G e t U n i f o r m L o c a t i o n ( p , ”t e x 0 ” ) ;
glUseProgram ( p ) ;
glUniform1i ( location , 2 ) ;
// => t e x 0 f e r a r é f é r e n c e à l a t e x t u r e c o u r a n t e de l ’ u n i t é 2 ( i c i
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
tex id ).
2008-2009
25 / 28
Exemple
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
26 / 28
Exemple
Vertex :
uniform vec3 posSource ;
v a r y i n g vec2 coord0 , coord1 ;
v a r y i n g vec3 normal ;
v a r y i n g vec3 L ;
v o i d main ( ) {
vec3 vertexEye ;
c o o r d 0=g l M u l t i T e x C o o r d 0 . s t ;
c o o r d 1=g l M u l t i T e x C o o r d 1 . s t ;
v e r t e x E y e =( g l M o d e l V i e w M a t r i x ∗ g l V e r t e x ) . x y z ;
L=p o s S o u r c e−v e r t e x E y e ;
n o r m a l =( g l M o d e l V i e w M a t r i x ∗ v e c 4 ( g l N o r m a l , 0 ) ) . x y z ;
g l P o s i t i o n = g l P r o j e c t i o n M a t r i x ∗gl ModelViewMatrix∗gl Vertex ;
}
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
27 / 28
Exemple
Fragment :
varying
uniform
varying
varying
vec2 coord0 , coord1 ;
sampler2D tex0 , t e x 1 ;
vec3 normal ;
vec3 L ;
v o i d main ( ) {
vec4 color0 , c o l o r 1 ;
v e c 3 normal2 , L2 ;
n o r m a l 2=n o r m a l i z e ( n o r m a l ) ;
L2=n o r m a l i z e ( L ) ;
f l o a t d i f f u s =d o t ( L2 , n o r m a l 2 ) ;
i f ( d i f f u s <0.0) {
c o l o r 0=t e x t u r e 2 D ( t e x 0 , c o o r d 0 ) ;
d i f f u s=−d i f f u s ;
}
else {
c o l o r 0=t e x t u r e 2 D ( t e x 1 , c o o r d 0 ) ;
}
gl FragColor = color0∗ d i f f u s ∗2.0;
}
F. Aubert (LS6/MS2)
P3D/ VIII Shaders
2008-2009
28 / 28