L'objectif du tp est de comprendre un élément essentiel de
l'écriture des shaders. vous pourrez ensuite afficher les Mesh de
votre projet en utilisant vos shaders plutot que ceux par défaut.
rappel : les tutos
de gkit... et surtout le tuto9
: utiliser un shader program avec draw(mesh)...
les shaders sont des fonctions classiques, la seule différence
est qu'elles sont exécutées en parallèle par les processeurs de la
carte graphique. Avant d'appeler une fonction qui prend un ou
plusieurs paramètres, il faut leur affecter une valeur. pour un
shader c'est la même chose, mais l'affectation ne s'écrit pas avec
a= 10; ou f(10)...
pourquoi ?
la variable a
de l'exemple, se trouve quelquepart
dans la mémoire de la carte graphique... pas dans la mémoire
accessible directement par l'application. openGL fournit un
mécanisme en deux temps :
c'est le même principe que d'affecter une valeur à la variable désignée par un pointeur :
#include "program.h"
GLuint program;
// init( )
:
// creer un shader program (qui declare des
parametres uniforms)
program= read_program( ... );
program_print_errors(
program);
// draw( ) :
// recuperer l'identifiant de la variable "a"
program GLint id=
glGetUniformLocation(program, "a");
// affecter une valeur
glUniform(id, 10);
openGL est une librairie C, sans surcharge, glUniform est une famille de fonctions dont le nom se termine par le type de la valeur à affecter :
plus de détails dans le tuto3
dans la section "uniforms et shader program".
si vous trouvez tout ça fastidieux, "uniforms.h" fournit les surcharges les plus courantes, par exemple :
#include "program.h"
#include "uniforms.h"
GLuint program= { ... };
program_uniform(program, "a", int(1));
exercice 1 : lisez le tuto9
exercice 2 : mettez à
jour gKit : git pull
et compilez tuto10.
modifiez tuto10 pour changer la couleur du cube. vous pouvez le
faire en changeant la constante dans le source du shader, mais
l'idée est que la couleur soit un paramètre (uniform) du fragment
shader. il faudra donc l'initialiser dans l'application.
exercice 3 : modifiez tuto10 pour
changer la position du cube et affichez un autre cube d'une autre
couleur à un autre endroit. la position et la couleur sont des paramètres
des shaders...
rappel : les transformations classiques sont fournies dans mat.h
le pipeline interpole les varyings, les sorties du vertex shader, avant d'exécuter les fragments shaders qui déterminent la couleur des pixels de l'image du triangle.
exercice 1 : remplacez
votre shader précédent par celui ci, tuto10_uv.glsl
#version 330
#ifdef VERTEX_SHADER
uniform mat4 mvpMatrix;
layout(location= 0) in vec3 position;
out vec3 vertex_uv; // declare une sortie du
vertex shader (un paramètre varying), mot clé : out
void main( )
{
gl_Position= mvpMatrix * vec4(position, 1);
//
coordonnees barycentriques des sommets abc d'un triangle
// exporte les coordonnees
barycentriques du sommet traite par le shader
const vec3 uv[3]= vec3[3](vec3(1, 0,
0), vec3(0, 1, 0), vec3(0, 0, 1));
vertex_uv= uv[gl_VertexID%3];
}
#endif
#ifdef FRAGMENT_SHADER
in vec3 vertex_uv; // recupere la sortie du
vertex shader (un parametre varying), mot-clé : in
// meme type et meme nom que la sortie du vertex shader
out vec4 fragment_color;
void main( )
{
fragment_color= vec4(vertex_uv, 1);
}
#endif
il n'y a pas d'outil pour inspecter les valeurs des variables
locales d'un shader, il faut donc tricher un peu.
une solution courante renvoie la valeur d'une variable comme
une couleur, mais il faut ensuite interpreter le résultat.
(attention : l'image
n'affiche que des valeurs positives comprises entre 0 et 1,
il faudra normaliser les valeurs des variables dans certains
cas, et/ou utiliser abs()).
une autre idée permet de faire l'equivalent d'un assert( ) :
si la condition est fausse, renvoyez une couleur improbable,
violet/magenta, par exemple, vec4(1, 0, 1, 1).
(vous pouvez aussi utiliser
discard pour détruire les fragments pour lesquels la condition
est vraie).
layout(location=
0) in vec3 position;
gl_Position
!!#ifdef FRAGMENT_SHADER
in vec3 vertex_normal;
out vec4 fragment_color;
void main( )
{
fragment_color=
vec4(abs(vertex_normal), 1);
//
abs() permet de "convertir" les coordonnées de la
normale qui sont comprises entre [-1 1] vers l'intervalle [0
1]
}
#endif
il peut y avoir 2 problèmes : soit l'application
n'a pas configuré le bon attribut, soit le vertex shader
n'affecte pas de valeur au varying vertex_normal
:#ifdef VERTEX_SHADER
uniform mat4 mvpMatrix;
layout(location= 0) in vec3 position;
layout(location= 1) in vec3 normal;
// erreur : l'atttribut
normal ne respecte pas la convention de Mesh ou de l'application !!
out vec3 vertex_normal;
void main( )
{
gl_Position= mvpMatrix *
vec4(position, 1);
//
erreur : vertex_normal n'est pas affecté !! et le fragment shader
récupère une valeur par défaut, vec3(0, 0, 0)
}
#endif