installez gKit et ses dépendances. compilez la doc : cd gKit; doxygen; firefox html/index.html
Ce qu'il faut retenir : la classe gk::App
permet de construire une application openGL, elle crée une
fenetre et un contexte de rendu openGL et permet également de
gérer les évènements claviers, souris, joysticks,
etc., facilement.
Pour construire une application, il faut dériver la classe
gk::App et redéfinir les méthodes suivantes :
// creer un shader
// 1. creer l'objet c++ shader
GLShaderProgram *shader= new
gk::GLShaderProgram("simple.vsl", "simple.fsl");
if(shader == NULL)
return "erreur";
// 2. creer effectivement le shader openGL
if(shader->createGLResource() < 0)
return "erreur de creation du
shader";
// utiliser le shader
glUseProgram(shader->name());
// dessiner quelquechose ...
// detruire le shader
// 1. detruire l'objet openGL
shader->releaseGLResource();
// 2. detruire l'objet c++ shader
delete shader;
#include "App.h"
#include "Transform.h"
#include "MeshIO.h"
#include "BufferManager.h"
#include "ShaderManager.h"
class TP : public gk::App
{
gk::GLShaderProgram *program;
public:
TP( )
:
gk::App(1024, 768)
{}
~TP( ) {}
int init( )
{
// gk::MeshIO::read(
mesh_filename ) pour lire un objet 3d
//
gk::createAttributeBuffer( ) pour creer un vertex buffer
//
gk::createIndexBuffer( ) pour creer un index buffer
// pour creer un
shader program en compilant les fichiers sources vertex et fragment
program=
gk::createShaderProgram( "vertex.vsl", "fragment.fsl" );
if(program == NULL ||
program->createGLResource() < 0)
//
erreur de chargement des fichiers sources ou de compilation des
shaders, sortir de l'application
return
-1;
return
0; // tout c'est bien passe, sinon
renvoyer -1
}
int quit( )
{
return 0;
}
int draw( )
{
if(key(SDLK_ESCAPE))
//
fermer l'application si l'utilisateur appuie sur ESCAPE
close();
// activer le shader
glUseProgram(program->name());
// parametrer le
shader, par exemple, mvpMatrix
// activer les buffers
d'attributs
// activer le buffer
d'indice
// draw
// afficher le buffer
de dessin
swap();
return
1; // continuer
}
};
int main( int argc, char **argv )
{
TP app;
app.run();
return 0;
}
compilez l'exemple à compléter, tuto_coreprofile.cpp :
premake4 gmake; make tuto_coreprofile
./tuto_coreprofile
Dans un premier temps, utilisez une transformation identité :
quelle est la partie de scène visible par la camera ?
Comment paramétrer le shader avec la transformation ? cf gk::setUniform()
Décrivez la position de 3 sommets d'un triangle, visible dans
ces condiditions, modifiez le début de la fonction init( ).
rappel : l'ordre des sommets est important, respectez le sens trigo lorsque la face est vue de l'extérieur
ou désactivez l'élimination des faces cachées/mal
orientiées dans la fonction init( ): glDisable(GL_CULL_FACE);
Décrivez un cube.
Modifiez les transformations : définissez une transformation perspective avec gk::Perspective() et déplacez votre cube.
Décrivez un cube en utilisant des sommets indexés / partagés.
// insere les indices de 3 sommets decrivant un triangle oriente dans un tableau d'indices :
void pushTriangle( std::vector<unsigned int>& indices, const unsigned int a, const unsigned int b, const unsigned int c )
{
indices.push_back(a);
indices.push_back(b);
indices.push_back(c);
}
// insere les indices de 4 sommets decrivant un quad oriente dans un tableau d'indices :
void pushQuad( std::vector<unsigned int>& indices, const unsigned int a, const unsigned int b, const unsigned int c, const unsigned int d )
{
pushTriangle(indices, a, b, c);
pushTriangle(indices, a, c, d);
// attention a l'ordre des sommets : respecter le sens trigo
}
initialisation de la position des sommets et des faces du cube avec indexation des sommets :
// initialisation : construire l'ensemble des positions des sommets des triangles a dessiner
std::vector<gk::Point> positions;
{
positions.push_back( gk::Point(0.0, 0.0, 0.0) ); // sommets face arriere (z= 0)
positions.push_back( gk::Point(1.0, 0.0, 0.0) );
positions.push_back( gk::Point(1.0, 1.0, 0.0) );
positions.push_back( gk::Point(0.0, 1.0, 0.0) );
positions.push_back( gk::Point(0.0, 0.0, 1.0) ); // sommets face avant (z= 1) // la camera regarde -z
positions.push_back( gk::Point(1.0, 0.0, 1.0) );
positions.push_back( gk::Point(1.0, 1.0, 1.0) );
positions.push_back( gk::Point(0.0, 1.0, 1.0) );
}
// initialisation : construire l'ensemble d'indices des faces du cube
std::vector<unsigned int> indices;
{
pushQuad(indices, 4, 5, 6, 7); // face avant
pushQuad(indices, 1, 2, 6, 5); // face doite
pushQuad(indices, 0, 1, 5, 4); // face dessous
pushQuad(indices, 0, 3, 2, 1); // face arriere
pushQuad(indices, 0, 4, 7, 3); // face gauche
pushQuad(indices, 2, 3, 7, 6); // face dessus
}
m_count= indices.size(); // conserve le nombre d'indices places dans l'index buffer
Modifiez la description du triangle pour associer une couleur à chaque sommet. et utilisez la paire de shaders :
m_program= gk::createShaderProgram( "vertex_color.vsl", "fragment_color.fsl" );
Il faut bien sur décrire le nouvel attribut de sommet : une couleur, et l'associer au shader program.
Modifiez la description du cube pour obtenir des faces de couleurs différentes.
Quelle est la définition d'un vertex pour openGL ?
Proposez une manière de déplacer l'observateur ou de re-orienter l'objet.
gKit fournit également des fonctions permettant de charger très simplement un objet décrit dans un fichier texte, les fichiers .obj utilisés par Maya/Blender, etc. :
#include "Mesh.h" // classe Mesh
#include "MeshIO.h" // contruction d'un Mesh a partir d'un fichier decrivant la forme d'un objet
gk::Mesh *mesh= gk::MeshIO::read("bigguy.obj");
if(mesh == NULL)
return "erreur de chargement";
La classe gk::Mesh organise ses données de manière à simplifier la création des buffers d'attributs de sommets et d'indices :
std::vector<gk::Point>& positions= mesh->positions();
std::vector<gk::Normal>& normales= mesh->normals();
std::vector<int>& indices= mesh->indices();
il suffit de créer le/les buffers et de transférer les données pour afficher l'objet composé de triangles.
Chargez un objet, "bigguy.obj", par exemple, construisez le/les buffers et affichez-le en utilisant la paire de shaders de base (couleur uniforme).
remarque : si vous trouvez pénible la gestion "manuelle" des buffers et des objets openGL, utilisez les fonctions utilitaires de gKit :
#include "BufferManager.h"
gk::GLAttributeBuffer *position_buffer= gk::createAttributeBuffer( );
gk::GLIndexBuffer *indice_buffer= gk::createIndexBuffer( )
;
Modifiez les shaders de base, vertex.vsl et fragment.fsl pour
déterminer une couleur du pixel qui dépend de
l'orientation du triangle dessiné.
"Gonflez" l'objet : poussez / déplacez les sommets des triangles dans la direction de leur normale (orientée vers l'extérieur).
Testez le geometry shader vu en cours qui dessine la normale des triangles d'un objet.
remarque : vérifiez le fonctionnement avec ou sans vertex shader.
Ecrivez un geometry shader qui calcule la normale des triangles et la
transmet au fragment shader pour déterminer une couleur qui
dépend de l'orientation des triangles par rapport à la
camera, par exemple :
fragment_color=
dot(normalize(normale), vec3(0, 0, 1)) * color;
// ou directement fragment_color= normalize(
normale).z