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
#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;
};
unsigned int build_node( std::vector<Node>& nodes, std::vector<Primitive>& primitives, const unsigned int begin, const unsigned int end )
{
if(end - begin <= 1)
{
// construire une feuille qui reference la primitive d'indice begin
// 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 boite englobante sur l'axe le plus etire
float coupe= ...
// partitionner les primitives par rapport a la "coupe"
Triangle *pmid= std::partition(primitives.data() + begin, primitives.data() + end, predicat(axe, coupe))
unsigned 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);
// construire le fils gauche
unsigned int left= build_node(nodes, primitives, begin, mid)
// construire le fils droit
unsigned int right= build_node(nodes, primitives, mid, end)
// construire un noeud
interne
// 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..
.
}
};
struct
Box
{
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 le main, au lieu de la recalculer à chaque fois
Vector dmin= (pmin - ray.o) * invd;
Vector dmax= (pmax - ray.o) * invd;
Vector tmin= min(dmin, dmax); // intersection avec les plans -U -V -W attachés à pmin
Vector tmax= max(dmin, dmax); // intersection avec les plans +U +V +W attachés à pmax
float t0= std::max(tmin.x, std::max(tmin.y, tmin.z)); // borne min de l'intervalle
d'intersection
float t1= std::min(tmax.x, std::min(tmax.y, tmax.z)); // borne max
// ne renvoie vrai que si l'intersection est valide
if(t0 <= t1 && t0 <= htmax && t1 > 0.f)
{
rtmin= t0;
rtmax= t1;
return true;
}
return false;
}
};
cf ray_tuto.cpp