bin/tp
les exercices suivants permettent de manipuler chaque étape, ie
passer d'un code simpifié avec gKit vers un code openGL direct.
exercice 1 : modifez le
code de départ (cf projets/tp.cpp) pour charger 1 objet,
data/cube.obj, data/robot.obj, ou data/bigguy.obj, par exemple, et
affichez-les.
on souhaite dessiner bigguy plus petit pour que sa taille soit
comparable à celle du robot. comment faire ?
si on souhaite dessiner bigguy posé sur un cube, comment faire ?
quelles transformations utiliser pour dessiner les 2 objets 3d ?
relisez "transformations et affichage" cf doc,
si nécessaire.


exercice 2 : écrivez un
couple de shaders qui dessine les objets avec une couleur uniforme
/ constante.
utilisez shader_kit pour écrire et tester les shaders, sans
modifier l'application.
(ou est la doc de shader_kit ?)
dans un premier temps, la couleur sera une constante dans le
fragment shader.
que faudra-t-il modifier (dans l'application et dans le shader)
pour dessiner un objet rouge et un autre vert, par exemple ?
dessiner un objet avec une couleur uniforme est un peu limité...
pour faire mieux, il faut une matière de base et une source de
lumière.
la matière la plus simple est une constante, ca tombe bien, ce
n'est pas difficile à calculer...
par contre, il faut quand même calculer le cosinus de l'angle
entre la normale d'un fragment / d'un point de la surface de
l'objet et la direction vers la source de lumière.
Color fragment_shader( const
Vector& n, const Vector& l )
{
float cos_theta= dot(normalize(n),
normalize(l));
return Color(cos_theta);
}
écrivez le shader en utilisant shader_kit. quels sont ses
paramètres ?
ou se trouve la doc sur toutes les fonctions de calcul de GLSL ?
ie comment calculer un produit scalaire, comment normaliser un
vecteur, etc.
est ce que ce shader pourra fonctionner avec tous les objets 3d ?
pourquoi ? ou pas ?
indication : quelle
direction utiliser pour la source de lumière ? vers la camera, par
exemple, fonctionne correctement. c'est tout simplement l'axe Z
(0, 0, 1) dans le repere camera.
rappel : calculs sur les
points et les vecteurs, toutes les coordonnées doivent être dans
le même repère, sinon c'est faux et souvent très bizarre... et
oui, il faudra sans doute transformer les coordonnées de certains
points ou vecteurs...
shader_kit initialise a peu près toutes les matrices pour
transformer les points et leur normales dans à peu près n'importe
quel repère.
ou se trouve la doc de shader_kit ?
faudra-t-il modifier l'application pour que les shaders obtiennent
ces informations ?

exercice 4 : et avec un peu de style ?
modifiez votre shader précédent, toujours dans shader_kit, pour réaliser un effet "toon shading", avec des aplats de couleurs plutot qu'un rendu plus lisse comme dans l'exercice précédent.

exercice 5 : modifiez votre application pour dessiner
avec les shaders de l'exercice précédent.
relisez la doc,
si necessaire.
si ces derniers shaders sont trop compliqués à utiliser, commencez
par celui de l'exercice 2.
solution alternative :
Mesh peut créer les buffers et configurer le format de sommet /
vao pour vous : cf GLuint
Mesh::create_buffers(...).
il ne reste plus qu'à affecter une valeur aux uniforms des shaders
et on peut dessiner directement avec openGL, à peu près comme ça :
GLuint program= 0;
GLuint vao= 0;
unsigned n= 0;
init( ):
Mesh mesh= read_mesh( /* fichier.obj */ );
vao= mesh.create_buffers( /* texcoods */ false, /* normals */ true, /* color */ false, /* material index */ false );
n= mesh.triangles_count() * 3;
program= read_program( /* fichier.glsl */ );
render( ):
glBindVertexArrayObject( vao );
glUseProgram( program );
program_uniform( program, /* uniform */, /* valeur */ );
glDrawArrays(GL_TRIANGLES, 0, n);
bonus : dessinez avec
openGL, créez un ou plusieurs buffers, un vao, etc... la totale.
ou est la doc pour faire ça ?
tous les objets ne sont pas constitués d'un seul groupe de
triangles de même couleur, par exemple data/robot.obj, comment
peut on dessiner ces triangles avec la bonne couleur ?
bien sur, il y a plusieurs solutions, les exercices suivants proposent de réaliser et de comparer 2 solutions.

std::vector<TriangleGroup>
Mesh::groups() trie les triangles d'un Mesh par
couleur / matière et renvoie les groupes de triangles. TriangleGroup est une structure toute simple : struct TriangleGroup
{
int index;
// indice de la
matiere dans Mesh::materials()
int
first;
// indice du premier sommet a dessiner
int
n;
// nombre d'indices
};
first et n ?
ce sont les parametres de glDrawArrays(GL_TRIANGLES, /* first
*/, /* n */ ) qui dessine des triangles avec n sommets
(donc n/3 triangles) à partir du sommet d'indice first dans le
tableau de sommets du Mesh, ce qui permet de dessiner le groupe de
triangles.const Materials& Mesh::materials()
permet de les récupérer. cf Materials
et Material
pour la description d'une matière.Mesh m_objet;
std::vector<TriangleGroup> m_groups;
init( ):
m_objet= read_mesh("data/robot.obj");// charge le fichier .obj et la description des matieres (fichier .mtl), s'il existe...
if(m_objet.materials().count() == 0)
return -1; // pas de matieres, pas d'affichage
// trie les triangles par matiere et recupere les groupes de triangles utilisant la meme matiere.
m_groups= m_objet.groups();
render( ):
// ensemble de matieres de l'objet
const Materials& materials=m_objet.materials();
// dessine chaque groupe de triangles, avec sa matiere
for(unsigned i= 0; i < m_groups.size(); i++)
{
const TriangleGroup& group= m_groups[i];
// recuperer la couleur de la matiere du groupe
Color color= materials[group.index].diffuse;
// parametrer le shader pour dessiner avec la couleur
glUseProgram(m_program);
{ ... }
// dessiner les triangles du groupe
glDrawArrays(GL_TRIANGLES, group.first, group.n);
}
exercice 2 / solution 2 : Mesh définit aussi un attribut
de sommet supplémentaire, l'indice de la matiere / couleur du
triangle. connaissant cet indice, comment le fragment shader
peut-il récupérer la couleur du triangle ?
en dessinant avec Mesh::draw(), le vertex shader peut récupérer l'indice de la matière / couleur : cf
mesh.draw(program,
/* use position */
true, /* use texcoord */ false, /* use normal */ true,
/* use color */
false, /* use material index */ true);
il faut déclarer l'indice de la matière dans le vertex shader en respectant la convention utilisée par Mesh, cf doc, section "et les attributs",
par contre, cet attribut n'est pas un réel, c'est juste un entier, et il n'est pas vraiment interpolable par le pipeline (entre la sortie du vertex shader et l'entrée du fragment shader), il faut prendre quelques précautions lors de la déclaration du varying. la décoration flat indique que le varying est constant à la surface du triangle :layout(location= 4) in uint material;
Reste une dernière étape : déclarer un tableau de couleurs dans le fragment shader et l'initialiser avec les couleurs des matières (dans l'application).// vertex shader
// attributs
layout(location= 0) in vec3 position;
layout(location= 2) in vec3 normal;
layout(location= 4) in uint material;
...
// sorties / varyings
flat outuint vertex_material;
/* décorationflat: le varying est un entier, donc pas vraiment interpolable... il faut le déclarer explicitement */
void main( )
{
gl_Position= ...;
vertex_material= material;
}
// fragment shader
flat inuint vertex_material; // !! decoration flat, le varying est marque explicitement comme non interpolable !!
...
void main( )
{
gl_FragColor= ...;
}
indication : oui, il faut déclarer la taille du tableau.
le plus simple est de déclarer un tableau de 16 couleurs par
exemple, seules les premières seront utilisées, il suffit
d'initialiser les autres qui ne seront pas utilisées (normalement)
avec une couleur par défaut bien visible, comme du magenta par
exemple : Color(1, 0, 1).
pourquoi une couleur bien moche
? pour debugger facilement, s'il y a un problème avec les
indices dans le shader...
exercice 3 : quelle est
la meilleure solution ? pour l'application ? pour le gpu ? et si
on doit aussi changer de texture ? ou de buffers ?
avec une texture plaquée sur l'objet, que faut-il modifier dans
les shaders ? dans l'application ? peut on texturer n'importe quel
objet chargé ?
indication : cf utilisation des textures et des shaders
avec Mesh::draw(), doc
section "et avec une texture ?"
et avec plusieurs groupes de triangles qui utilisent des textures
différentes ? quelle semble être la meilleure solution ?
il existe des tableaux de textures (cf texture2DArray) en plus des
textures classiques (texture2D), est ce que ca change le type de
solution applicable ?
reprenez les exercices précédents et modifiez l'application
correspondante pour dessiner directement avec openGL.
c'est à dire : créer les buffers nécessaires pour stocker les sommets et leurs propriétés, configurer un vertex array object (VAO) pour décrire comment sont stockés ces propriétés, puis au moment de dessiner : affecter une valeur à chaque paramètre uniform des shaders et enfin dessiner avec glDrawArrays().
comme précisé dans la doc,
une application va ressembler à ça :
init( ):
chargement de l'objet 3d,
création des buffers,
configuration du vao,
chargement et compilation des shaders,
état minimal d'openGL (resolution, zbuffer et ztest, orientation des triangles et culling, couleur et z par défaut)
render( ):
glBindVertexArray(vao);
glUseProgram(program);
glUniform( ... );
...
glDrawArrays(GL_TRIANGLES, ... );
