TP1 - transformations / objets et camera


L'objectif du tp est de savoir afficher plusieurs objets, placés dans une scène, de les manipuler et de les observer.

remarque : toute la doc est la


Partie 1 : un objet

Pour dessiner un objet, un pipeline graphique, comme openGL, à besoin de quelques informations : les coordonnées des sommets des triangles de l'objet, sa position et son orientation dans le monde, la position et l'orientation de la camera dans le monde et les 2 shaders qui projettent les sommets, et calculent une couleur pour chaque pixel sur lequel se dessine chaque triangle.

Dans un premier temps, la première difficulté est de comprendre comment manipuler les transformations pour placer et orienter les objets, ainsi que comment placer et orienter une camera.

L'objet peut etre décrit "à la main" en construisant un objet Mesh triangle par triangle, ou en chargeant directement un fichier .obj exporté par blender, par exemple.

Comment ca marche ? il suffit de fournir les coordonnées des sommets de chaque triangle. Par exemple, pour un triangle abc, avec les sommets :

Point a= Point(0, 0, 0);
Point b= Point(1, 0, 0);
Point c= Point(0, 1, 0);

créer un objet Mesh :

Mesh objet(GL_TRIANGLES);
objet.vertex(a);
objet.vertex(b);
objet.vertex(c);

Pour afficher cet objet, il faut créer une application openGL, une camera, des shaders, etc. Pour démarrer rapidement, le plus simple est de modifier tuto7_camera.cpp. La création des objets à afficher est faite dans la méthode init().

Il est aussi direct de charger un objet .obj exporté par blender, par exemple le résultat du premier tp :

objet= read_mesh( "..." );


L'affichage de l'objet est réalisé dans la méthode render( ), 60 fois par seconde :

draw(objet, Identity(), camera());

Cette fonction crée tous les objets openGL nécessaires pour dessiner cet objet avec des shaders de base / par défaut. Ses paramètres sont :

draw( const Mesh& objet, const Transform& model, Orbiter& camera );

l'objet à dessiner, ou le placer dans le monde, cf model, et quel point de vue / camera utiliser, cf camera.

Pour dessiner l'objet à un autre endroit dans le monde, il faut modifier la transformation model. Pour le déplacer : Translation( x, y, z ) par exemple, pour le faire tourner : cf RotationX(), RotationY(), RotationZ(), etc.

Ces fonctions qui renvoient une matrice de transformation sont documentées dans mat.h.


Partie 2 : plusieurs objets...

Affichez plusieurs objets (ou plusieurs fois le même objet...) à des endroits différents du monde.

Vous pouvez animer leur transformation en fonction du temps, global_time() renvoie le temps écoulé, en millisecondes, depuis le démarrage de l'application.

Commnent placer un objet dans le monde puis le faire tourner sur lui meme ? Il faut composer 2 transformations : une translation et une rotation :

Transform t1= Translation(x, y, z) * RotationY(angle);
Transform t2= RotationY(angle) * Translation(x, y, z);
Comment placer un objet par rapport à un autre ? par exemple, placer un cube à droite d'un objet, faites tourner l'objet, le cube doit tourner autour de l'objet ? C'est encore une composition de transformations. Le cube est placé par rapport au repère de l'objet, si le repère de l'objet change, la position du cube dans le monde change, mais pas par rapport à l'objet. Il suffit de représenter la position du cube par rapport à l'objet par une Transform, et la position de l'objet dans le monde par une autre transform. On obtient la position du cube dans le monde en composant les 2 transformations.

L'exemple classique est de créer un système solaire - terre - lune, ou un bras articulé : la position de la main est connue par rapport au poignet, le poignet par rapport au coude, le coude par rapport à l'épaule... etc.

A quoi ca sert ? à déplacer des ensembles d'objets "connectés", par exemple pour attacher un objet dans la main d'un personnage, afficher des particules qui virevoltent autour d'un personnage, etc.

Utilisez les fonctions key_state() / clear_key_state() pour contrôler le déplacement d'un objet dans le monde. Le code de toutes les touches du clavier sont dans la doc de SDL2, colonne SDL_KeyCode. par exemple SDLK_LEFT, SDLK_RIGHT, SDLK_UP, SDLK_DOWN pour les fleches de direction...

On souhaite pouvoir le faire avancer, reculer, tourner sur lui meme, et avancer dans la direction qu'il observe / "devant", pas le long des axes XYZ du monde.

indication : Lookat() de mat.h peut etre assez utile pour calculer la transformation.

Partie 3 : camera

Placez un petit cube par rapport au personnage / objet de la partie précédente, en arrière et au dessus qui "regarde" l'objet.
On souhaite utiliser la position de ce petit cube comme camera, comment faire ?

indication : transformations standards : model permet de placer un objet dans le monde, view permet de placer le même objet dans le repere camera. Donc Inverse(view) permet de placer un objet dans le monde connaissant sa position dans le repere camera. Autrement dit, view est l'inverse de la transformation qui permet de placer une camera dans le monde... vous savez placer un objet dans le monde, donc vous savez utiliser cet objet comme camera.

Utilisez une autre version de draw pour dessiner les objets :

draw( const Mesh& objet, const Transform& model, const Transform& view, const Transform& projection );

La camera renvoie les transformations view et projection, cf Orbiter::view() et Orbiter::projection(). Draw( objet, model, camera ) est équivalent à :
Transform view= camera.view();
Transform projetion= cmaera.projection();

draw(objet, model, view, projection);
La projection perspective classique est renvoyée par Perspective(), cf mat.h :

Transform projection = Perspective(45, float(window_width()) / float(window_height()), 0.01, 100);


Partie 4 : shaders

Avant de modifier l'application pour utiliser des shaders, il est plus simple de les écrire et de les tester avec shader_kit. Mais il faut respecter 3 conventions :

un passage par shader et GLSL, est probablement nécéssaire...

Le pipeline openGL utilise un vertex shader et un fragment shader, shader_kit les regroupe dans un seul fichier .glsl :

#version 330

#ifdef VERTEX_SHADER
layout(location= 0) in vec3 position;    //!! attention layout(location= 0) obligatoire !!
uniform mat4 mvpMatrix;                  //!! attention mat4 mvpMatrix obligatoire !!

void main( )
{
    gl_Position= mvpMatrix * vec4(position, 1);
}
#endif

#ifdef FRAGMENT_SHADER
out vec4 fragment_color;

void main( )
{
    fragment_color= vec4(1, 0.6, 0, 1);
}
#endif

Une fois que les shaders fonctionnent dans shader_kit, que faut-il faire pour les utiliser dans l'application ? cf tuto9 et éventuellement tuto10...