git clone
https://forge.univ-lyon1.fr/JEAN-CLAUDE.IEHL/gkit3.git
pourquoi ? gkit compile et fonctionne sur linux, windows, mac os, ios, android et meme WebGL. Chaque système dispose de plusieurs compilateurs et environnements de travail. Il n'est pas envisageable de créer et de maintenir tous ces projets manuellement. gkit utilise donc un outil : un générateur de projet, ce qui permet de décrire les projets une seule fois et c'est l'outil (premake dans ce cas...) qui génère le projet pour votre environnement de travail.
il faut donc apprendre à générer le projet pour votre environnement de travail, en utilisant premake.
sous linux, premake4 est
disponible, installez le sur votre machine, si nécessaire : sudo
apt install premake4
sinon, téléchargez le dans le répertoire gkit3 : cf premake5
ouvrez un terminal, et naviguez jusqu'au répertoire contenant gKit :
powershell
ou windows
terminal
dans le menu démarrerctrl
-alt
-t
,terminal
rappel : commandes ls
et cd
pour naviguer.
./premake5.exe codeblocks
le workspace (groupe de projets)
codeblocks ainsi que les projets sont crées dans le répertoire build/
,
ouvrez build/gKit3.workspace
.
pour générer une solution (groupe de projets) visual studio, il suffit de choisir la bonne version :
./premake5.exe
vs2022
la solution visual studio ainsi que
les projets sont crées dans le répertoire build/
,
ouvrez build/gkit3.sln
.
./premake5 xcode
./premake5 gmake
le Makefile
se trouve
dans le répertoire de base de gKit.
premake4 gmake
// si premake4 est installe dans le système./premake5 gmake
// si premake5 est copie dans le repertoire
de gKitle Makefile
se trouve
dans le répertoire de base de gKit.
remarque : si premake5 est
disponible dans les paquets de votre distribution utilisez-le !
cf premake5 gmake
générez les makefiles, comme au
dessus dans linux + makefile
compilez tp1, par exemple, si vous voulez vérifiez qu'une application compile et fonctionne.
les makefile peuvent générer les versions debug (cf utiliser un debugger comme gdb ou lldb) ou les versions release, plus rapide (2 ou 3 fois, interressant pour les projets avec beaucoup de calculs) :
make help
, affiche la liste des projets et les
options disponibles,make tp1
, compile la version debug de tp1,make
tp1
config=release
,
compile la version release de tp1,make
tp1
config=debug
,
compile la version debug de tp1,make
tp1
verbose=1
,
compile la version debug de tp1 et affiche le détail des
commandes exécutées.les exécutables sont crées dans le
répertoire gkit3/bin
, pour les exécuter :
bin/tp1
#include "vec.h"
float intersect_plan( /* parametres du plan */ const Point&
a, const Vector& n, /* parametres du rayon */ const
Point& o, const Vector& d )
{
}
// infini
#include <limits>
const float inf=
std::numeric_limits<float>::infinity();
int main( )
{
// rayon
Point o= Point(0, 0, 0);
Vector d= Vector(0, 0, -1);
// plan
Point a= Point(0, 0, -1);
Vector n= Vector(0, 0, 1);
float t= intersect_plan(a,
n, o, d);
std::cout << t
<< std::endl;
if(t != 1) std::cout << "oops,
loupe\n";
else
std::cout << "c'est bon...\n";
return 0;
}
#include <cmath>
#include "vec.h"
float intersect_sphere( /* parametres de la
sphere */ const Point& c, const float r, /* parametres du
rayon */ const Point& o, const Vector& d )
{
float a= ...;
float b= ...;
float k= ...;
float det= b*b - 4*a*k;
...
}
#include "color.h"
#include "image.h"
#include "image_io.h"
int main( )
{
// cree l'image resultat
Image image(512,
512); // par exemple...
for(int py= 0;
py < image.height(); py++)
for(int px= 0;
px < image.width(); px++)
{
Point o= Point(0, 0,
0); // origine
Point e=
...
// extremite
Vector d= Vector(o,
e); // direction : extremite - origine
image(px, py)= Red();
}
write_image(image,
"image.png");
return 0;
}
Vector l=
normalize(Vector(-4, 6,
1)); //
direction vers le "soleil"
struct Plan
{
Point
a; // point sur le plan
Vector
n; // normale du plan
Color
color; // couleur
};
struct Hit
{
float
t; // position sur le
rayon, ou inf s'il n'y a pas d'intersection
Point
p; // position du
point, s'il existe
Vector n;
// normale du point d'intersection, s'il existe
Color
color; // couleur du point d'intersection,
s'il existe
Hit( ) : t(inf), p(), n(), color() {}
// pas d'intersection
Hit( const float _t, const Point& _p, const
Vector& _n, const Color& _color ) : t(_t), p(_p), n(_n),
color(_color) {}
};
Hit intersect( const
Plan& plan, const Point& o, const Vector& d )
{
float t= ... ;
Point p= { ... };
if(t < 0)
return
Hit();
// renvoie une intersection non valide / par defaut.
l'intersection n'est pas valide / derriere l'origine du
rayon
else
return Hit(t, p, plan.n,
plan.color); // renvoie la position de
l'intersection, + la normale et la couleur du plan
}
Hit intersect( const
Plan& plan, const Point& o, const Vector& d,
const float tmax )
{
float t= ... ;
Point p= { ... };
if(t < 0 || t > tmax)
return Hit();
// renvoie une intersection non valide / par defaut.
l'intersection n'est pas valide / derriere l'origine du
rayon, ou trop loin
else
return Hit(t, p,
plan.n, plan.color); // renvoie la
position de l'intersection, + la normale et la couleur du
plan
}
struct Scene
{
std::vector<Sphere> spheres;
Plan plan;
};
Hit intersect( const
Scene& scene, const Point& o, const Vector& d )
{
Hit plus_proche;
plus_proche.t= inf;
for(unsigned i= 0 ; i
< scene.spheres.size(); i++)
{
//
tester la ieme sphere
Hit
h= intersect(scene.spheres[i], o, d, plus_proche.t);
if(h
... )
plus_proche= ... ;
}
// et bien sur, on
n'oublie pas le plan...
Hit h=
intersect(scene.plan, o, d, plus_proche.t);
if(h ...)
plus_proche= ...;
return plus_proche;
}
struct Hit
{
float t;
// position sur le rayon ou inf
Point p;
Vector n; //
normale du point d'intersection, s'il existe
Color color; // couleur du
point d'intersection, s'il existe
operator bool() { return t >= 0 &&
t < inf; } // renvoie vrai si l'intersection existe et quelle
est valide...
};
Hit h= intersect( ... )
if(h)
{
// faire un truc
avec l'intersection h
}
`if(h)
` appelle la fonction que l'on
vient d'ajouter dans la structure hit, c'est la meme chose que
si l'on ecrit le test à chaque fois
`if(hit.t >= 0
&& hit.t < inf)
` mais c'est plus simple et ça
peut éviter certaines erreurs.
ou encore plus simple :
if(Hit h= intersect( ... ))
{
// faire un
truc avec l'intersection h
}
remarque : on peut aussi définir l'opérateur de
comparaison entre 2 structures Hit pour vérifier laquelle est la plus
petite...
Point o= p;
Vector d= l;
Point o= p + epsilon * n; // avec epsilon=
0.001 par exemple...
Vector d= l;
#pragma omp parallel for schedule(dynamic, 1)
for(int py= 0; py < image.height(); py++)
for(int px= 0; px < image.width(); px++)
{
...
image(px, py)= { ... };
}
make
config=release tp
, au lieu de make tp
(qui
génère la version debug du programme).
pour visual studio, il faut cocher l'option openmp dans les
propriétes du projet.
temps d'exécution avant (cf make
tp
) : time bin/tp,
1m34smake config=release tp
) :
time bin/tp,
8s
pas mal pour une seule ligne changée dans le programme ! (12
coeurs sur la machine de test et un facteur d'accélération de 11.75,
c'est plutot bien !)
#pragma omp parallel for schedule(dynamic, 1)
for(int py= 0; py < image.height(); py++)
{
// initialiser un generateur de nombre aléatoire pour chaque ligne / chaque thread
for(int px= 0; px < image.width(); px++)
std::random_device hwseed;
std::default_random_engine rng( hwseed() );
std::uniform_real_distribution<float> uniform;
// chaque thread a une copie privee de ces 3 variables
{
// generer les nombres aleatoires avec uniform( rng )
float ux= uniform( rng );
float uy= uniform( rng );
...
image(px, py)= { ... };
}
}