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 / le plus proche de la camera
pour chaque objet :
transformer le rayon dans le repere local de l'objet
pour chaque triangle :
calculer l'intersection du rayon et du 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
connaissant les 3 axes dans le repère du monde, comment calculer les coordonnées de la direction vi (dans le repère du monde) ?
#include <cmath>
// b1, b2, n sont 3 axes orthonormes.
void branchlessONB(const Vector &n,
Vector
&b1,
Vector
&b2)
{
float sign = std::copysign(1.0f, n.z);
const float a = -1.0f / (sign + n.z);
const float b = n.x * n.y * a;
b1 = Vector(1.0f + sign * n.x * n.x * a, sign * b, -sign * n.x);
b2 = Vector(b, sign + n.y * n.y * a, -n.y);
}
#include <algorithm>
struct BBox { ... };
struct Node { ... };
struct Primitive
{
BBox bounds;
Point center;
int triangle_id;
};
int build_node( std::vector<Node>& nodes, std::vector<Primitive>& primitives, const int begin, const int end )
{
if(end - begin <= 1)
{
// construire une feuille qui reference la primitive d'indice begin
, et la boite englobante du triangle associee a la primitive...
// renvoyer l'indice de la feuille
}
// construire la boite englobante des centres des primitives d'indices [begin .. end[
// trouver l'axe le plus etire de la boite englobante
int axe= ...
// couper en 2 au milieu de la boite englobante sur l'axe le plus etire
float coupe= ...
// partitionner les primitives par rapport a la "coupe"
Primitive *pmid= std::partition(primitives.data() + begin, primitives.data() + end, predicat(axe, coupe))
int mid= std::distance(primitives.data(), pmid);
// verifier que la partition n'est pas degeneree (toutes les primitives du meme cote de la separation)
assert(mid != begin);
assert(mid != end);
// remarque : il est possible que 2 (ou plus) primitives aient le meme centre,
// dans ce cas, la boite englobante des centres est reduite à un point, et la partition est forcement degeneree
// une solution est de construire une feuille,
// ou, autre solution, forcer une repartition arbitraire des primitives entre 2 les fils, avec mid= (begin + end) / 2
// construire le fils gauche
int left= build_node(nodes, primitives, begin, mid)
// construire le fils droit
int right= build_node(nodes, primitives, mid, end)
// construire un noeud
interne
// quelle est sa boite englobante ?
// renvoyer l'indice du noeud
}
et le prédicat de la partition :
struct predicat
{
int axe;
float coupe;
predicat( const int _axe, const float _coupe ) : axe(_axe), coupe(_coupe) {}
bool operator() ( const Primitive& p ) const
{
// renvoyer vrai si le centre de l'englobant de la primitive se trouve avant la coupe..
.
}
};
// utilitaire : renvoie les coordonnees min / max de 2 points
inline Point min( const Point& a, const Point& b ) { return Point( std::min(a.x, b.x), std::min(a.y, b.y), std::min(a.z, b.z) ); }
inline Point max( const Point& a, const Point& b ) { return Point( std::max(a.x, b.x), std::max(a.y, b.y), std::max(a.z, b.z) ); }
structBBox
{
Point pmin;
Point pmax;
bool intersect( const Ray& ray, const float htmax, float& rtmin, float& rtmax ) const
{
Vector invd= Vector(1.f / ray.d.x, 1.f / ray.d.y, 1.f / ray.d.z);
// remarque : il est un peu plus rapide de stocker invd dans la structure Ray, ou dans l'appellant / algo de parcours, au lieu de la recalculer à chaque fois
Point rmin= pmin;
Point rmax= pmax;
if(ray.d.x < 0) std::swap(rmin.x, rmax.x); // le rayon entre dans la bbox par pmax et ressort par pmin, echanger...
if(ray.d.y < 0) std::swap(rmin.y, rmax.y);
if(ray.d.z < 0) std::swap(rmin.z, rmax.z);
Vector dmin= (rmin - ray.o) * invd;// intersection avec les plans -U -V -W attachés à rmin
Vector dmax= (rmax - ray.o) * invd;
// intersection avec les plans +U +V +W attachés à rmax
rtmin= std::max(dmin.x, std::max(dmin.y, std::max(dmin.z, 0.f))); // borne min de l'intervalle
d'intersection
rtmax= std::min(dmax.x, std::min(dmax.y, std::min(dmax.z, htmax))); // borne max
// ne renvoie vrai que si l'intersection est valide (l'intervalle n'est pas degenere)
return (rtmin <= rtmax);
}
};