main
creer une image
charger un maillage et ses matières
extraire les sources de lumiere (trouver les triangles dont la matière émet de la lumiere)
pour chaque pixel de l'image :
générer un rayon dans le repere de la scene
// trouver le point de la scène visible pour le rayon
pour chaque triangle :
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
./bin/shader_kit data/shaders/mesh.glsl mesh.obj
utilisez N points sk générés aléatoirement à la surface des sources de lumières pour évaluer l'éclairage direct.
rappel :
theta est l'angle entre la normale en p et la direction p vers s,
theta_s est l'angle entre la normale en s et la direction s vers p.
cf GI
compendium eq 18 pour générer des points dans un triangle,
ou cette solution plus récente (et mieux expliquée) : "A
Low-Distortion Map Between Triangle and Square", E. Heitz,
2019
les directions vk sont uniformes sur l'hemisphère,
cf GI compendium eq 34, ou distribuées selon
,
cf GI compendium eq 35.
V(p, vk) = 1 s'il n'y a pas d'intersection
/ de géométrie dans la direction vk, et 0 sinon (le
point p est à l'ombre)
remarque : les directions aléatoires sont générées
dans un repère local, il faut donc les transformer pour
connaitre leurs coordonnées dans le repère du monde. On peut
construire le changement de repère avec la normale du point p
et un autre vecteur (+ 2 produits vectoriels pour construire 2
directions orthogonales). Il y a quelques subtilités dans
cette construction, autant en utiliser une qui est robuste et
rapide :
cf "Generating
a consistently oriented tangent space", Frisvad,
2012,
et "Building
an Orthonormal Basis, Revisited", Pixar, 2017
struct World
{
World( const Vector& _n ) : n(_n)
{
float sign= std::copysign(1.0f, n.z);
float a= -1.0f / (sign + n.z);
float d= n.x * n.y * a;
t= Vector(1.0f + sign * n.x * n.x * a, sign * d, -sign * n.x);
b= Vector(d, sign + n.y * n.y * a, -n.y);
}
// transforme le vecteur du repere local vers le repere du monde
Vector operator( ) ( const Vector& local ) const { return local.x * t + local.y * b + local.z * n; }
// transforme le vecteur du repere du monde vers le repere local
Vector inverse( const Vector& global ) const { return Vector(dot(global, t), dot(global, b), dot(global, n)); }
Vector t;
Vector b;
Vector n;
};
construisez un arbre BVH pour accélérer les calculs et
pouvoir calculer l'exemple de la partie 2. les différentes
constructions sont expliquées dans la doc.
On peut aussi construire des arbres plus efficaces à parcourir
en utilisant une des solutions présentées ici.
vous pouvez charger et manipuler les textures en utilisant la
classe Image
connaissant les coordonnées de texture, vous pouvez utiliser
Color Image::sample( u, v )
avec u et v
compris etntre 0 et 1,Color Image::texture( x, y )
avec x et y
compris entre 0 et largeur ou hauteur de l'image.attention : les coordonnées de texture de la
scène bistro se repètent à la surface des objets, elles
peuvent > 1 (ou négatives). il faut en tenir compte, sinon
segfault... Image::texture() n'est pas robuste à ce type
d'erreur. le pixel est sensé exister... une solution simple
est de calculer le modulo des coordonnées de textures par
rapport aux dimensions de l'image.
pendant le parcours du bvh, lorsqu'une intersection avec un
triangle est trouvée, il faut vérifier si une texture
transparente est "plaquée" sur le triangle et vérifier la
valeur du canal alpha / transparence pour les coordonnées de
texture du point d'intersection.
si alpha < 0.5 la texture indique qu'il n'y a pas de
géométrie à cet endroit, il faut donc poursuivre le parcours
du bvh...
remarque : votre intersecteur rayon / bvh doit donc
avoir accès au bvh (noeuds + feuilles + triangles, bien sur),
mais aussi aux matières et textures du mesh pour récupérer ces
informations.
(faites attention à la gestion mémoire des images / textures,
il y a 1Go de textures compressées dans la scène... évitez de
charger plusieurs fois les images)