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