indications : vous
pouvez reproduire l'exemple avec
Vector l=
normalize(Vector(-4, 6,
1)); //
direction vers le "soleil"
Intermède : grand nettoyage du code !
avant d'écrire la suite, il est conseillé de passer un peu de
temps à nettoyer le code et surtout à le ré-organiser pour le
rendre plus simple à manipuler. il y a probablement 2 choses à
reprendre, il faut connaitre facilement la normale de
l'intersection ainsi que la couleur de l'objet touché par le
rayon. pour la suite du tp, on va construire plus de rayons et
calculer plus d'intersections avec plus d'objets, pour calculer
les ombres, les pénombres et les reflets, par exemple.
il y a bien sur plusieurs manières d'organiser tout ca. voila
une solution qui fontionne bien et qui permet de faire la suite
du tp sans problemes :
exercice 1 : nettoyage des fonctions d'intersection
créez une structure plan, avec les parametres d'un plan et sa
couleur, créez une autre structure pour la sphère.
par exemple :
struct Plan
{
Point
a; // point sur le plan
Vector
n; // normale du plan
Color
color; // couleur
};
il serait plus simple que les fonctions d'intersection renvoient
toutes les infos sur une intersection : sa position sur le
rayon, sa normale et la couleur de l'objet. le plus direct est
d'utiliser une structure :
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) {}
};
et les fonctions d'intersection prennent en paramètre la
structure qui stocke les paramètres de l'objet et renvoient une
structure Hit avec toutes les informations sur l'intersection :
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
}
remarque : oui bien
sur, on peut définir une structure pour le rayon...
on peut encore se simplifier la vie en ajoutant un paramètre
aux fonctions d'intersection : la valeur max de t, ie quelles
sont les valeurs possibles de t : entre 0 et tmax. les
intersections en dehors de cet intervalle ne sont pas valides.
ca permet de simplifier encore un peu la logique du reste du
code pour garder l'intersection la plus proche de la camera.
pourquoi tmax ? les rayons sont soit des demi droites
infinies et tmax= inf, soit des segments, par exemple pour
vérifier qu'un point est à l'ombre, ou pour vérifier qu'il
existe une intersection plus proche que celle que l'on vient de
trouver.
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
}
remarque : si vous êtes à l'aise en c++, n'hésitez pas à
utiliser des fonctions membres... par exemple :
struct Plan
{
Point
a; // point sur le plan
Vector
n; // normale du plan
Color
color; // couleur
Hit intersect( const Point& o,
const Vector& d );
};
vous pouvez également créer une classe de base pour les objets
et rendre la fonction intersect() virtuelle, ce qui simplifiera
les calculs d'intersections avec des objets différents, par
exemple des sphères et des plans.
struct Objet
{
Objet() {}
virtual ~Objet() {}
virtual Hit intersect(
const Point& o, const Vector& d, const float tmax );
};
struct Plan : public Objet
{
...
};
struct Sphere : public Objet
{
...
};
exercice 2 : plein d'objets !
une sphère posée sur un plan est pratique pour écrire la
première version du code, mais on est rapidement lassé de voir
la même image...
il n'est pas très compliqué de stocker plusieurs sphères dans un
std::vector :
struct Scene
{
std::vector<Sphere> spheres;
Plan plan;
};
et d'écrire une fonction d'intersection qui renvoie
l'intersection valide la plus proche trouvée en testant toutes
les sphères + le 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;
}
et voila !
ce travail préparatoire va permettre de passer à la suite très
facilement !