L'objectif du tp est de comprendre comment décrire un objet 3d, de
le placer et de l'orienter dans le monde. Dans un premier temps,
vous ne manipulerez pas directement openGL pour vous concentrer
sur l'essentiel : le pipeline, et ses entrées : attributs de
sommets, et transformations.
Dézippez gKit, et générez les projets pour votre environnement de
développement. si nécessaire relisez le tuto1.
Vérifiez que tout fonctionne, par exemple :
premake4 gmake
make tuto1GL -j8
./bin/tuto1GL
exercice 1 : un carré
Mesh mesh= Mesh(GL_TRIANGLES);
// 1er triangle
mesh.vertex(x, y, z);
mesh.vertex(x, y, z);
mesh.vertex(x, y, z);
// 2ieme triangle
mesh.vertex(x, y, z);
mesh.vertex(x, y, z);
mesh.vertex(x, y, z);
Mesh mesh= Mesh(GL_TRIANGLES);
// sommets
unsigned int a= mesh.vertex(x, y, z);
unsigned int b= mesh.vertex(x, y, z);
unsigned int c= mesh.vertex(x, y, z);
unsigned int d= mesh.vertex(x, y, z);
// triangle indexes
mesh.triangle(a, b, c);
mesh.triangle(a, c, d);
mesh.color(
Color(r, g, b) ).
Orbiter camera;
//
deplace la camera
int mx,
my;
unsigned
int mb= SDL_GetRelativeMouseState(&mx, &my);
if(mb
&
SDL_BUTTON(1))
// le bouton gauche est enfonce
camera.rotation(mx, my);
else
if(mb &
SDL_BUTTON(3))
// le bouton droit est enfonce
camera.move(mx);
L'objectif est de construire une géométrie plus "interressante", par exemple un terrain, à partir d'une carte d'altitude.
Il est très simple de charger une image et de parcourir ses pixels :
Chaque groupe de 4 pixels voisins va permettre de décrire 2 triangles, les coordonnées des sommets seront dans le plan xz et l'altitude sera sur l'axe y.
#include "color.h"
#include "image.h"
#include "image_io.h"
Image data= read_image("data/terrain/Clipboard01.png");
for(int y= 0; y < data.height(); y++)
for(int x= 0; x < data.width(); x++)
{
Color pixel= data(x, y);
...
}
Mesh terrain= Mesh(GL_TRIANGLES);
...
Point pmin, pmax;
terrain.bounds(pmin, pmax);
Orbiter camera;
camera.lookat(pmin, pmax);
la déclaration de l'attribut position respecte une convention particulière : la décoration layout(location= 0) indique que l'attribut position est l'attribut numero 0, et la classe Mesh crée un buffer et configure un vertex array en utilisant cette convention.#version 330
uniform mat4 matrix;
layout(location= 0) in vec3 position;
void main( )
{
gl_Position= matrix * vec4(position, 1);
}
#version 330
void main()
{
gl_FragColor= vec4(1, 0, 0, 1); // tous les pixels seront rouge.
}
étape 1 : compiler les shaders
le plus simple est d'utiliser un utilitaire (cf tuto) pour compiler les shaders, et d'afficher les erreurs eventuelles :
il faut écrire les 2 shaders dans le même fichier texte (extension au choix, .glsl est classique), en les séparant par des #ifdef / #endif :#include "program.h"
GLuint program;
init( )
program= read_program("...");
program_print_errors(program);
#version 330
#ifdef VERTEX_SHADER
uniform mat4 matrix;
layout(location= 0) in vec3 position;
void main( )
{
gl_Position= matrix * vec4(position, 1);
}
#endif
#ifdef FRAGMENT_SHADER
void main()
{
gl_FragColor= vec4(1, 0, 0, 1); // tous les pixels seront rouge.
}
#endif
étape 2 : affecter une valeur à un uniform
utiliser la bonne version de glUniform() en fonction du type de la valeur, les détails sont dans le tuto.
quelle transformation utiliser pour afficher le terrain ?
rappel : les transformations standards sont model, view, projection et viewport. et gKit définit la classe Transform (cf mat.h) pour les représenter / manipuler.
a priori, model est l'identité dans l'exemple du terrain, et les transformations view et projection sont calculées par la camera, cf Orbiter::view() et Orbiter::projection(). il suffit de les composer ensemble pour connaitre la transformation complète.
par exemple :
#include "mat.h"
Orbiter camera;
draw( )
Transform
model= Identity();
Transform view= camera.view();
Transform projection= camera.projection(window_width(), window_height(), 45);
Transform t= ... ; // composer les 3 matrices, dans quel ordre ? model * view * projection, ou autre chose
// selectionner le shader program
glUseProgram(program);
// affecte une valeur au parametre declare dans le vertex shader
//
uniform mat4 matrix; cf l'exemple ci-dessus
// etape 1 : recupere l'identifiant de la variable declaree dans le shader
GLint location= glGetUniformLocation(program, "matrix");
// etape 2 : affecte une valeur à la variable déclarée dans le shader, utilise la "bonne" version de glUinform
glUniformMatrix4fv(location, 1, GL_TRUE, t.buffer());
// ou :
// utilise un utilitaire de uniforms.h
// program_uniform(program, "matrix", t);
étape 3 : dessiner !
les derniers paramètres de la fonction draw( ) indiquent quels attributs doivent etre transferés dans un buffer et configurés dans un vertex array (cf Mesh).draw( )
// dessine le terrain
draw(terrain, program, /* position */ true, /* texcoord */ false, /* normal */ false, /* color */ false);
le vertex shader minimaliste n'utilise qu'un seul attribut, position.
Mesh terrain;
GLuint program;
init( )
program= read_program("...");
program_print_errors(program);
terrain= ...
draw( )
Transform t= ... ;
// configure le pipeline, selectionne le shader program
glUseProgram(program);
// affecte une valeur au parametre declare dans le vertex shader
//
uniform mat4 matrix; cf exemple ci-dessus
// etape 1 : recupere l'identifiant de la variable declaree dans le shader
GLint location= glGetUniformLocation(program, "matrix");
// etape 2 : affecte une valeur à la variable déclarée dans le shader, utilise la "bonne" version de glUinform
glUniformMatrix4fv(location, 1, GL_TRUE, t.buffer());
// dessine le terrain
draw(terrain, program, /* position */ true, /* texcoord */ false, /* normal */ false, /* color */ false);
#version 330
#ifdef VERTEX_SHADER
layout(location= 0) in vec3 position; // attribut 0
uniform mat4 matrix; // parametre uniform
out vec3 color; // varying, sorite du vertex shader
void main( )
{
gl_Position= ... ;
color= ... ; // affecter une valeur au varying
}
#endif
#ifdef FRAGMENT_SHADER
in vec3 color; // meme type, meme nom que la sortie declaree dans le vertex shader
void main( )
{
gl_FragColor= vec4(color, 1);
}
#endif