il est aussi recommandé de passer un peu de temps à (re-) lire les explications sur le fonctionnement de tout ça :
exercice 1 : modifez le code de départ pour charger 2 objets, data/cube.obj et data/robot.obj, ou data/bigguy.obj, par exemple et affichez-les.
exercice 2 : écrivez un
couple de shaders qui dessine les objets avec une couleur
uniforme.
dans un premier temps la couleur sera une constante dans le
fragment shader.
lorsque cette premiere version fonctionne, remplacez la couleur
constante par un parametre du fragment shader.
que faut-il modifier dans l'application pour dessiner un objet
rouge et un autre vert, par exemple ?
remarque : continuez à utiliser les utilitaires de gkit
exercice 3 : et avec les normales ?
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 de la source
de lumière.
le cosinus d'un angle entre 2 vecteurs se calcule directement avec le produit scalaire de ces 2 vecteurs (s'ils sont de longueur 1).
on souhaite calculer dot(n, l) pour n, la normale du triangle
visible pour le pixel et l, la direction de la source de lumiere.
en c++, on pourrait ecrire :
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 ?
indication : quelle
direction pour la source de lumière ? vers la camera, par exemple,
fonctionne correctement. c'est tout simplement 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...
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 precedent.
exercice 6 : dessinez avec openGL, créez un ou plusieurs buffers, un vao, etc... la totale.
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 ?
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 : Mesh définit 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 out
uint vertex_material;
/* décoration
flat
: 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 in
uint 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 de les
initialiser 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...
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 ?