27 Ray(
const Point& _o,
const Vector& _d ) : o(_o), d(_d), tmax(FLT_MAX) {}
28 Ray(
const Point& _o,
const Vector& _d,
const float _tmax ) : o(_o), d(_d), tmax(_tmax) {}
42 Hit( ) : t(FLT_MAX), u(), v(), instance_id(-1), mesh_id(-1), primitive_id(-1), triangle_id(-1) {}
43 Hit(
const Ray& ray ) : t(ray.tmax), u(), v(), instance_id(-1), mesh_id(-1), primitive_id(-1), triangle_id(-1) {}
45 Hit(
const float _t,
const float _u,
const float _v,
const int _mesh_id,
const int _primitive_id,
const int _id ) : t(_t), u(_u), v(_v),
46 instance_id(-1), mesh_id(_mesh_id), primitive_id(_primitive_id), triangle_id(_id) {}
48 operator bool ( ) {
return (triangle_id != -1); }
56 BBoxHit() : tmin(FLT_MAX), tmax(-FLT_MAX) {}
57 BBoxHit(
const float _tmin,
const float _tmax ) : tmin(_tmin), tmax(_tmax) {}
59 operator bool( )
const {
return tmin <= tmax; }
68 BBox( ) : pmin(), pmax() {}
70 BBox(
const Point& p ) : pmin(p), pmax(p) {}
71 BBox(
const BBox& box ) : pmin(box.pmin), pmax(box.pmax) {}
72 BBox(
const BBox& a,
const BBox& b ) : pmin(
min(a.pmin, b.pmin)), pmax(
max(a.pmax, b.pmax)) {}
74 BBox& insert(
const Point& p ) { pmin=
min(pmin, p); pmax=
max(pmax, p);
return *
this; }
75 BBox& insert(
const BBox& box ) { pmin=
min(pmin, box.pmin); pmax=
max(pmax, box.pmax);
return *
this; }
77 float centroid(
const int axis )
const {
return (pmin(axis) + pmax(axis)) / 2; }
78 Point centroid( )
const {
return (pmin + pmax) / 2; }
80 BBoxHit intersect(
const Ray& ray,
const Vector& invd,
const float htmax )
const
84 if(ray.d.x < 0) std::swap(rmin.x, rmax.x);
85 if(ray.d.y < 0) std::swap(rmin.y, rmax.y);
86 if(ray.d.z < 0) std::swap(rmin.z, rmax.z);
87 Vector dmin= (rmin - ray.o) * invd;
88 Vector dmax= (rmax - ray.o) * invd;
104 bool internal( )
const {
return right > 0; }
105 int internal_left( )
const { assert(
internal());
return left; }
106 int internal_right( )
const { assert(
internal());
return right; }
108 bool leaf( )
const {
return right < 0; }
109 int leaf_begin( )
const { assert(leaf());
return -left; }
110 int leaf_end( )
const { assert(leaf());
return -right; }
117 assert(node.internal());
131 template <
typename T >
135 int build(
const std::vector<T>& _primitives )
137 primitives= _primitives;
139 nodes.reserve(primitives.size());
142 root= build(0, primitives.size());
147 Hit intersect(
const Ray& ray,
const float htmax )
const
151 Vector invd=
Vector(1 / ray.d.x, 1 / ray.d.y, 1 / ray.d.z);
152 intersect(root, ray, invd, hit);
157 Hit intersect(
const Ray& ray )
const {
return intersect(ray, ray.tmax); }
160 std::vector<Node> nodes;
161 std::vector<T> primitives;
164 int build(
const int begin,
const int end )
169 int index= nodes.size();
178 if(d.x > d.y && d.x > d.z)
186 float cut= cbounds.centroid(axis);
189 T *pm= std::partition(primitives.data() +
begin, primitives.data() +
end,
190 [axis, cut](
const T& primitive )
192 return primitive.bounds().centroid(axis) < cut;
205 int left= build(
begin, m);
208 int right= build(m,
end);
211 int index= nodes.size();
221 bbox.insert(primitives[i].bounds());
229 BBox bbox= primitives[
begin].bounds().centroid();
231 bbox.insert(primitives[i].bounds().centroid());
237 void intersect(
const int index,
const Ray& ray,
const Vector& invd,
Hit& hit )
const
239 const Node& node= nodes[index];
240 if(node.bounds.intersect(ray, invd, hit.t))
244 for(
int i= node.leaf_begin(); i < node.leaf_end(); i++)
245 if(
Hit h= primitives[i].intersect(ray, hit.t))
250 intersect(node.internal_left(), ray, invd, hit);
251 intersect(node.internal_right(), ray, invd, hit);
267 Triangle(
const vec3& a,
const vec3& b,
const vec3&
c,
const int _mesh_id,
const int _primitive_id,
const int _id ) :
269 mesh_id(_mesh_id), primitive_id(_primitive_id), triangle_id(_id) {}
278 Hit intersect(
const Ray &ray,
const float htmax )
const
281 float det=
dot(e1, pvec);
283 float inv_det= 1 / det;
286 float u=
dot(tvec, pvec) * inv_det;
287 if(u < 0 || u > 1)
return Hit();
290 float v=
dot(ray.d, qvec) * inv_det;
291 if(v < 0 || u + v > 1)
return Hit();
293 float t=
dot(e2, qvec) * inv_det;
294 if(t < 0 || t > htmax)
return Hit();
296 return Hit(t, u, v, mesh_id, primitive_id, triangle_id);
302 return box.insert(p+e1).insert(p+e2);
319 object_transform(
Inverse(model)), world_bounds(transform(
bounds, model)),
326 Hit intersect(
const Ray &ray,
const float htmax )
const
329 Ray object_ray(object_transform(ray.o), object_transform(ray.d), htmax);
332 Hit hit= object_bvh->intersect(object_ray, htmax);
335 hit.instance_id= instance_id;
345 for(
unsigned i= 1; i < 8; i++)
349 if(i & 1) p.x= bbox.pmax.x;
350 if(i & 2) p.y= bbox.pmax.y;
351 if(i & 4) p.z= bbox.pmax.z;
370 assert(hit.mesh_id != -1);
371 assert(hit.primitive_id != -1);
374 return primitives.normals.size() > 0;
380 assert(hit.instance_id != -1);
381 assert(hit.mesh_id != -1);
382 assert(hit.primitive_id != -1);
383 assert(hit.triangle_id != -1);
388 int a= primitives.indices[3*hit.triangle_id];
389 int b= primitives.indices[3*hit.triangle_id+1];
390 int c= primitives.indices[3*hit.triangle_id+2];
393 assert(primitives.normals.size());
394 Vector na= primitives.normals[a];
395 Vector nb= primitives.normals[b];
396 Vector nc= primitives.normals[c];
400 Vector n= (1 - hit.u - hit.v) * na + hit.u * nb + hit.v * nc;
415 assert(hit.instance_id != -1);
416 assert(hit.mesh_id != -1);
417 assert(hit.primitive_id != -1);
418 assert(hit.triangle_id != -1);
423 int a= primitives.indices[3*hit.triangle_id];
424 int b= primitives.indices[3*hit.triangle_id+1];
425 int c= primitives.indices[3*hit.triangle_id+2];
428 assert(primitives.positions.size());
429 Point pa= primitives.positions[a];
430 Point pb= primitives.positions[b];
431 Point pc= primitives.positions[c];
449 assert(hit.mesh_id != -1);
450 assert(hit.primitive_id != -1);
453 return primitives.texcoords.size() > 0;
459 assert(hit.instance_id != -1);
460 assert(hit.mesh_id != -1);
461 assert(hit.primitive_id != -1);
462 assert(hit.triangle_id != -1);
467 int a= primitives.indices[3*hit.triangle_id];
468 int b= primitives.indices[3*hit.triangle_id+1];
469 int c= primitives.indices[3*hit.triangle_id+2];
472 assert(primitives.texcoords.size());
473 vec2 ta= primitives.texcoords[a];
474 vec2 tb= primitives.texcoords[b];
475 vec2 tc= primitives.texcoords[c];
479 vec2 t=
vec2( (1 - hit.u - hit.v) * ta.x + hit.u * tb.x + hit.v * tc.x, (1 - hit.u - hit.v) * ta.y + hit.u * tb.y + hit.v * tc.y );
489 int tx= int(t.x * texture.width) % texture.width;
490 int ty= int(t.y * texture.height) % texture.height;
496 ty= texture.height - 1 - ty;
498 size_t offset= texture.offset(tx, ty);
501 Color color=
Color(texture.pixels[offset], texture.pixels[offset+1], texture.pixels[offset+2], 255) / 255;
502 if(texture.channels > 3)
504 color.a= float(texture.pixels[offset+3]) / float(255);
515 assert(hit.mesh_id != -1);
516 assert(hit.primitive_id != -1);
520 return default_material;
555 bool use_texture=
has_texcoords(hit, scene) && textures.size();
568 metallic= metallic * texel.b;
569 roughness= roughness * texel.g;
585 brdf.
diffuse= (1 - metallic) * color;
586 brdf.
F0= (1 - metallic) *
Color(0.04) + metallic * color;
591 brdf.
alpha= roughness * roughness;
602 std::uniform_real_distribution<float> u01;
603 std::default_random_engine rng;
605 Sampler(
const unsigned _seed ) : u01(), rng(_seed) {}
606 void seed(
const unsigned _seed ) { rng= std::default_random_engine(_seed); }
608 float sample( ) {
return u01(rng); }
610 int sample_range(
const int n ) {
return int(sample() * n); }
614 int main(
int argc,
char **argv )
616 const char *mesh_filename=
"data/robot.gltf";
617 const char *orbiter_filename=
nullptr;
619 if(argc > 1) mesh_filename= argv[1];
620 if(argc > 2) orbiter_filename= argv[2];
625 std::vector<BVH *> bvhs(scene.
meshes.size());
630 #pragma omp parallel for
631 for(
unsigned mesh_id= 0; mesh_id < scene.
meshes.size(); mesh_id++)
636 std::vector<Triangle> triangles;
637 for(
unsigned primitive_id= 0; primitive_id < mesh.
primitives.size(); primitive_id++)
641 for(
unsigned i= 0; i +2 < primitives.indices.size(); i+= 3)
644 vec3 a= primitives.positions[primitives.indices[i]];
645 vec3 b= primitives.positions[primitives.indices[i+1]];
646 vec3 c= primitives.positions[primitives.indices[i+2]];
647 triangles.push_back(
Triangle(a, b, c, mesh_id, primitive_id, i/3) );
653 bvh->build(triangles);
664 std::vector<Instance> instances;
665 for(
unsigned node_id= 0; node_id < scene.
nodes.size(); node_id++)
673 top_bvh.build(instances);
674 printf(
"done. %d instances\n",
int(instances.size()));
688 int height= width / scene.
cameras[0].aspect;
698 #pragma omp parallel for
699 for(
int y= 0; y < image.height(); y++)
700 for(
int x= 0; x < image.width(); x++)
708 if(
Hit hit= top_bvh.intersect(ray))
716 image(x, y)=
Color(color, 1);
representation d'une image.
GLenum primitives() const
renvoie le type de primitives.
int mesh_index
indice du maillage.
std::vector< ImageData > read_gltf_images(const char *filename)
charge un fichier .gltf et charge les images referencees par les matieres.
int material_index
indice de la matiere des primitives.
GLTFScene read_gltf_scene(const char *filename)
charge un fichier .gltf et construit une scene statique, sans animation.
Transform model
transformation model pour dessiner le maillage.
description d'un maillage.
position et orientation d'un maillage dans la scene.
groupe de triangles d'un maillage. chaque groupe est associe a une matiere.
void begin(Widgets &w)
debut de la description des elements de l'interface graphique.
void printf(Text &text, const int px, const int py, const char *format,...)
affiche un texte a la position x, y. meme utilisation que printf().
void end(Widgets &w)
termine la description des elements de l'interface graphique.
int write_image(const Image &image, const char *filename)
enregistre une image dans un fichier png.
Transform Inverse(const Transform &m)
renvoie l'inverse de la matrice.
Point max(const Point &a, const Point &b)
renvoie la plus grande composante de chaque point. x, y, z= max(a.x, b.x), max(a.y,...
Transform Viewport(const float width, const float height)
renvoie la matrice representant une transformation viewport.
Transform Identity()
construit la transformation identite.
float distance(const Point &a, const Point &b)
renvoie la distance etre 2 points.
Point min(const Point &a, const Point &b)
renvoie la plus petite composante de chaque point. x, y, z= min(a.x, b.x), min(a.y,...
float dot(const Vector &u, const Vector &v)
renvoie le produit scalaire de 2 vecteurs.
Vector normalize(const Vector &v)
renvoie un vecteur unitaire / longueur == 1.
Vector cross(const Vector &u, const Vector &v)
renvoie le produit vectoriel de 2 vecteurs.
void bounds(const MeshData &data, Point &pmin, Point &pmax)
renvoie l'englobant.
intersection avec une boite / un englobant.
bvh parametre par le type des primitives, cf triangle et instance...
representation d'une couleur (rgba) transparente ou opaque.
float transmission
transmission, transparent ou pas (= 0)
int color_texture
indice de la texture ou -1. cf read_gltf_images() pour charger les textures dans le bon ordre....
float roughness
rugosite de la micro surface.
int metallic_roughness_texture
indice de la texture ou -1. les valeurs RGB representent les parametres du modele : B= metallic,...
Color emission
emission pour les sources de lumieres ou pas (= noir).
int transmission_texture
indice de la texture ou -1.
float metallic
metallic / dielectrique.
std::vector< GLTFMaterial > materials
matieres.
std::vector< GLTFNode > nodes
noeuds / position et orientation des maillages dans la scene.
std::vector< GLTFMesh > meshes
ensemble de maillages.
std::vector< GLTFCamera > cameras
cameras.
intersection avec un triangle.
stockage temporaire des donnees d'une image.
instance pour le bvh, cf fonctions bounds() et intersect().
construction de l'arbre / BVH.
representation d'un point 3d.
generation de nombres aleatoires entre 0 et 1.
triangle pour le bvh, cf fonction bounds() et intersect().
representation d'un vecteur 3d.
vecteur generique, utilitaire.
vecteur generique, utilitaire.
Vector triangle_normal(const Hit &hit, const GLTFScene &scene)
normale geometrique du triangle, si necessaire... ie si has_normals(hit, scene) == false....
const GLTFMaterial & hit_material(const Hit &hit, const GLTFScene &scene)
renvoie la matiere du point d'intersection.
Color emission
emission pour les sources de lumieres ou pas (= noir).
float alpha
rugosite de la micro surface.
Vector hit_normal(const Hit &hit, const GLTFScene &scene)
renvoie la normale interpolee au point d'intersection dans le repere de la scene.
Point hit_position(const Hit &hit, const Ray &ray)
renvoie la position du point d'intersection sur le rayon.
Brdf hit_brdf(const Hit &hit, const GLTFScene &scene, const std::vector< ImageData > &textures)
evalue les parametres pbr (couleur, metal, rugosite) de la matiere au point d'intersection,...
Color hit_color(const Hit &hit, const GLTFScene &scene)
renvoie la couleur de base de la matiere du point d'intersection.
Color sample_texture(const vec2 &t, const ImageData &texture)
float transmission
transmission, transparent ou pas (= 0)
bool has_normals(const Hit &hit, const GLTFScene &scene)
verifie la presence des normales par sommet.
bool has_texcoords(const Hit &hit, const GLTFScene &scene)
verifie la presence des coordonnees de texture...
Node make_leaf(const BBox &bounds, const int begin, const int end)
creation d'une feuille.
vec2 hit_texcoords(const Hit &hit, const GLTFScene &scene)
renvoie les coordonnees de texture du point d'intersection, suppose que has_texcoords(hit,...
Vector n
normale interpolee du point d'intersection
float metallic
metallic / dielectrique.
Node make_node(const BBox &bounds, const int left, const int right)
creation d'un noeud interne.
regroupe tous les parametres de la matiere du point d'intersection.