M2 - Images
TP 1 - Méthodes de monte carlo
et lancer de rayons.
L'objectif de ce tp est de mettre en place les éléments nécessaires
pour une simulation "correcte" de l'éclairage direct et indirect
basé sur les méthodes de Monte Carlo.
Utilisez les scenes de test suivantes, elles sont composées de très
peu de geométrie (triangles), ce qui permet d'éviter de construire
une structure accélératrice pour limiter les calculs de visibilité
(au moins dans un premier temps ...) :
Utilisez tuto_ray1.cpp
comme point de depart.
completez premake4.lua pour generer un projet. cf le wiki de gKit2
Partie 1 : lancer de rayons et visibilité
exercice 1 : visualisation par lancer de rayons
Pour chaque pixel de l'image résultat, déterminez les coordonnées de
l'origine du rayon dans le repère de la scène, ainsi que celles de
son extrémité.
Dans quel espace est-il le plus simple de calculer l'origine et
l'extrémité du rayon ?
Calculez l'intersection du rayon avec tous les triangles de la scène
(cf. la fonction intersect() dans tuto_ray1.cpp) et ne conservez que
l'intersection valide la plus proche de l'origine du rayon.
remarques :
gk::Ray
permet de représenter et de manipuler
facilement un rayon.
gk::Hit
permet de représenter simplement une
intersection,
- les fonctions d'intersections rayon / triangle et rayon / bbox
sont déjà écrites :
gk::Triangle::intersect( )
et
gk::BBox::intersect( )
.
indications : votre
programme devrait ressembler à :
main
creer une image
charger un ou plusieurs objets
extraire les sources de lumiere (trouver les
objets emettant de la lumiere)
pour chaque pixel de l'image :
générer un rayon dans le
repere de la scene
// trouver le point visible
pour le rayon
pour chaque triangle de chaque
objet :
transformer
le rayon dans le repere local de l'objet
calculer
l'intersection du rayon avec le triangle,
conserver
l'intersection si elle est valide et plus proche que la dernière
trouvée
si une intersection valide
existe
ecrire un
pixel blanc dans l'image
sauver l'image
remarque : pour déterminer la position du point
d'intersection à l'abscisse t, le plus simple est d'utiliser
l'opérateur ( ) de gk::Ray. par exemple, gk::Point p=
ray(hit.t);
exercice 2 : matière
Choisissez une couleur arbitraire pour chaque triangle de la scène.
Utilisez cette couleur pour construire l'image des objets visibles.
exercice bonus : observateur quelconque
Comment placer et orienter l'observateur directement dans la scène ?
une solution simple consiste à utiliser la classe Orbiter et les
fonctions OrbiterIO::readOribter() et gk::OrbiterIO::writeOrbiter()
permettant de charger et de stocker les transformations nécessaires.
l'utilitaire mesh_viewer de gKit permet de charger une scène, de se
déplacer / changer la position de l'observateur, de relire et
d'enregistrer les transformations (cf les touches 'c' et 'v').
exercice 3 : ombres et éclairage direct
Choisissez un point sur chaque source de lumière et vérifiez que
chaque point visible par la caméra est également visible par les
sources de lumière.
Comment déterminer l'origine et la direction des rayons permettant
de faire ce test ?
Les points non éclairés seront noirs.
remarque : gk::Triangle::point(0.3, 0.3) permet de trouver
le centre du triangle. pourquoi ?
exercice 4 : sources étendues
Générez plusieurs points à la surface des sources de lumières. cf
gk::Triangle::sampleUniform(). Comment déterminer la couleur du
pixel ?
voila un exemple de ce que l'on peut obtenir. remarque : l'image de
gauche est incorrecte. pourquoi ?
exercice 5 : matières
Utilisez la description de matières associée aux triangles pour
calculer l'image.
remarque : les matières sont décrites dans un fichier texte,
"materials.mtl", il est très simple de le modifier.
Par exemple, la matière par défaut s'appelle "diffuse50SG", elle
est déclarée :
newmtl diffuse50SG
Kd 0.7 0.7 0.7
ce qui correspond à une matière diffuse grise qui réflechit 70%
(rgb 0.7 0.7 0.7) de la lumière incidente. La couleur décrite par
Kd dans le fichier mtl est conservée dans le champ diffuse_color
de la structure gk::MeshMaterial.
pour utiliser une texture pour moduler le résultat, il suffit
d'ajouter la ligne map_Kd nom_image, par exemple :
newmtl diffuse50SG
Kd 0.7 0.7 0.7
map_Kd simple.png
Voici les correspondances entre le fichier mtl et les valeurs
stockées dans un objet gk::MeshMaterial :
Kd r g b
|
MeshMaterial::diffuse_color=
Normalize(r,g,b)
MeshMaterial::kd= Length(r,g,b)
|
map_Kd image
|
MeshMaterial::diffuse_texture= image
|
Ks r g b
|
MeshMaterial::specular_color=
Normalize(r,g,b)
MeshMaterial::ks= Length(r,g,b)
|
map_Ks image
|
MeshMaterial::specular_texture= image
|
Ns |
MeshMaterial::ns= Ns
|
Avec les valeurs de l'exemple précédent :
gk::MeshMaterial material;
material.name= "diffuse50SG"
material.kd= 0.7;
material.diffuse= gk::Energy(1.0, 1.0, 1.0);
material.map_Kd= "simple.png"
Il est également assez simple de charger des textures pour changer
l'apparence de l'objet.
consultez la mise à jour de tuto_ray1.cpp (sur le depot)
// ensemble de textures, une par triangle
std::vector<gk::Image *> diffuse_textures;
// recupere la texture 'diffuse_texture' de chaque
triangle
int build_textures( const gk::Mesh *mesh )
{
// cree une image blanche
gk::Image *white= gk::createImage(
2, 2, gk::Vec4(1, 1, 1, 1) );
// destruction automatique
gk::ImageManager::manager().insert(
white );
for(int i= 0; i <
mesh->triangleCount(); i++)
{
gk::Image
*image= NULL;
const
gk::MeshMaterial& material= mesh->triangleMaterial(i);
if(material.diffuse_texture.empty())
// pas de texture definie, utiliser une texture blanche (cf
multiplication par diffuse_color)
image= white;
else
{
// charger l'image, utilise le cache de lecture de
gk::ImageManager
image= gk::readImage(material.diffuse_texture);
if(image == NULL)
// utilise une texture par defaut, en cas d'echec de lecture
(debug.png)
image= gk::defaultImage();
}
diffuse_textures.push_back( image );
}
printf("%lu textures.\n",
diffuse_textures.size());
return 0;
}
// renvoie une valeur 'filtree' de la texture
gk::Color image_sample( const gk::Image *image, const
gk::Point& texcoord )
{
int u= (image->width -1) *
texcoord.x;
int v= (image->height -1) *
texcoord.y;
// mode nearest + repeat, renvoie le
pixel le plus proche
gk::Vec4 color= image->pixel(
abs(u % image->width), abs(v % image->height) );
// \todo les autres modes de
filtrage... linear, mipmaps, etc.
return gk::Color(color);
}
exercice 6 : toutes les sources
Comment répartir N points à la surface de l'ensemble des sources
de lumières (au lieu de N par source, solution a priori utilisee à
l'exercice 4) ?
L'idée est de choisir aléatoirement d'abord une source, puis un
point sur cette source, et de recommencer N fois.
Proposez au moins 2 manières différentes de choisir une source
parmis l'ensemble de sources.
Vous pouvez tester vos idées avec la scène emission.obj, prévue
pour ce cas précis.
Quel comportement observez-vous dans chaque cas ?
voila un exemple, avec 8 échantillons par pixel : à gauche une
solution a priori correcte, à droite, sélection uniforme de la
source.
quel est le problème ? quelle est la répartition des calculs dans
les 2 cas (quelles sont les sources sélectionnées dans les 2 cas
pour N échantillons ?)
(en orange : les sources de lumière, les 2 panneaux lumineux
emettent le même flux)
exercice 7 : éclairage indirect
En plus de calculer la lumière directement incidente, calculez aussi
la lumière réfléchie indirectement par les autres objets de la
scène.
L'idée est d'estimer la lumière directe avec 1 seul point choisit
sur l'ensemble des sources et d'estimer la lumière indirecte en
choisissant une autre direction. Il suffit alors de trouver l'objet
visible dans cette direction et d'estimer l'energie directe qu'il
réfléchit.
Comment choisir cette nouvelle direction pour estimer la lumière
indirecte ?
Q1. commencez par écrire la formulation (de la solution) du problème
: L(x, o)= L0(x, o) + L1(x, o) + L2(x, o) + ...
avec L0(x, o)= Le(x, o), la luminance émise par
un point x (sur une source de lumière) vers o, la position de
l'observateur,
avec
la luminance emise de y vers x,
la reflexion en x vers o
de la luminance emise par y,
le terme géométrique associé au changement de variables
d'intégration (directions vers points) et V(x, y)= 1 si x et y sont
visibles.
quel est l'estimateur Monte Carlo permettant
d'évaluer L1 ?
quelle(s) variables aléatoires ?
quelles densités de probabilités ? y se trouve
sur une source lumière, par définition (pourquoi ?)
avec
et
le triangle
/ source sélectionné.
comment choisir
? (indication:
? ou autre chose de plus adapté ? cf exercice 6)
Q2. quelle est la formulation de L2, L3, etc ? leurs estimateurs
Monte Carlo ?
indication :
Q3. comment évaluer L2 ?
indication :
z se trouve sur une source de lumière (pourquoi
?)
y doit donc etre visible à la fois de x (cf G(x,
y)) et de z (cf G(y, z)). ou se trouvent les points visibles de x ?
et ceux visibles de z ?
Q4. existe-t-il plusieurs "stratégies" / manières de choisir y ?
citez en 3... et formulez leur densité.
Q5. peut-on construire plusieurs estimateurs Monte Carlo de L2 ?
sont-ils équivalents ? quel est le meilleur ? quoi faire dans ce cas
?
Q6. memes questions avec un rebond supplementaire, comment évaluer
L3 ?
Vous pouvez tester vos idées avec les scènes geometrie.obj et
secondary.obj.
Quel comportement observez-vous ? Essayez plusieurs choix de
directions pour estimer la lumière indirecte