26 Ray(
const Point& _o,
const Point& _e ) : o(_o), d(Vector(_o, _e)), tmax(1) {}
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;
90 float tmin= std::max(dmin.z, std::max(dmin.y, std::max(dmin.x, 0.f)));
91 float tmax= std::min(dmax.z, std::min(dmax.y, std::min(dmax.x, htmax)));
92 return BBoxHit(tmin, tmax);
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; }
114Node make_node(
const BBox&
bounds,
const int left,
const int right )
117 assert(node.internal());
131template <
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();
175 BBox cbounds= centroid_bounds(
begin, end);
176 Vector d= Vector(cbounds.pmin, cbounds.pmax);
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;
195 int m= std::distance(primitives.data(), pm);
199 if(m ==
begin || m == end)
200 m= (
begin + end) / 2;
205 int left= build(
begin, m);
208 int right= build(m, end);
211 int index= nodes.size();
217 BBox primitive_bounds(
const int begin,
const int end )
219 BBox bbox= primitives[
begin].bounds();
220 for(
int i=
begin +1; i < end; i++)
221 bbox.insert(primitives[i].bounds());
227 BBox centroid_bounds(
const int begin,
const int end )
229 BBox bbox= primitives[
begin].bounds().centroid();
230 for(
int i=
begin +1; i < end; i++)
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 ) :
268 p(a), e1(Vector(a, b)), e2(Vector(a,
c)),
269 mesh_id(_mesh_id), primitive_id(_primitive_id), triangle_id(_id) {}
278 Hit intersect(
const Ray &ray,
const float htmax )
const
280 Vector pvec=
cross(ray.d, e2);
281 float det=
dot(e1, pvec);
283 float inv_det= 1 / det;
284 Vector tvec(p, ray.o);
286 float u=
dot(tvec, pvec) * inv_det;
287 if(u < 0 || u > 1)
return Hit();
289 Vector qvec=
cross(tvec, e1);
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);
313 Transform object_transform;
318 Instance(
const BBox& bounds,
const Transform& model, BVH *bvh,
const int id ) :
319 object_transform(
Inverse(model)), world_bounds(transform(bounds, model)),
324 BBox bounds( )
const {
return world_bounds; }
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;
341 BBox transform(
const BBox& bbox,
const Transform& m )
343 BBox bounds= BBox( m(bbox.pmin) );
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;
354 bounds.insert( m(p) );
366 std::uniform_real_distribution<float> u01;
367 std::default_random_engine rng;
369 Sampler(
const unsigned _seed ) : u01(), rng(_seed) {}
370 void seed(
const unsigned _seed ) { rng= std::default_random_engine(_seed); }
372 float sample( ) {
return u01(rng); }
374 int sample_range(
const int n ) {
return int(sample() * n); }
378int main(
int argc,
char **argv )
380 const char *mesh_filename=
"data/robot.gltf";
381 const char *orbiter_filename=
nullptr;
383 if(argc > 1) mesh_filename= argv[1];
384 if(argc > 2) orbiter_filename= argv[2];
386 GLTFScene scene= read_gltf_scene(mesh_filename);
389 std::vector<BVH *> bvhs(scene.meshes.size());
392 printf(
"%d meshes\n",
int(scene.meshes.size()));
394 #pragma omp parallel for
395 for(
unsigned mesh_id= 0; mesh_id < scene.meshes.size(); mesh_id++)
397 const GLTFMesh& mesh= scene.meshes[mesh_id];
400 std::vector<Triangle> triangles;
401 for(
unsigned primitive_id= 0; primitive_id < mesh.primitives.size(); primitive_id++)
405 for(
unsigned i= 0; i +2 < primitives.indices.size(); i+= 3)
408 vec3 a= primitives.positions[primitives.indices[i]];
409 vec3 b= primitives.positions[primitives.indices[i+1]];
410 vec3 c= primitives.positions[primitives.indices[i+2]];
411 triangles.push_back(
Triangle(a, b, c, mesh_id, primitive_id, i/3) );
417 bvh->build(triangles);
425 printf(
"%d nodes\n",
int(scene.nodes.size()));
428 std::vector<Instance> instances;
429 for(
unsigned node_id= 0; node_id < scene.nodes.size(); node_id++)
431 const GLTFNode& node= scene.nodes[node_id];
437 top_bvh.build(instances);
438 printf(
"done. %d instances\n",
int(instances.size()));
442 assert(scene.cameras.size());
444 Transform projection= scene.cameras[0].projection;
448 int height= width / scene.cameras[0].aspect;
449 Image image(width, height);
458#pragma omp parallel for
459 for(
unsigned y= 0; y < image.height(); y++)
460 for(
unsigned x= 0; x < image.width(); x++)
468 if(
Hit hit= top_bvh.intersect(ray))
470 assert(hit.instance_id != -1);
471 assert(hit.mesh_id != -1);
472 assert(hit.primitive_id != -1);
474 const GLTFMesh& mesh= scene.meshes[hit.mesh_id];
475 const GLTFPrimitives& primitives= mesh.primitives[hit.primitive_id];
477 unsigned a= primitives.indices[3*hit.triangle_id];
478 unsigned b= primitives.indices[3*hit.triangle_id+1];
479 unsigned c= primitives.indices[3*hit.triangle_id+2];
509 image(x, y)=
Color(diffuse, 1);
representation d'une image.
int mesh_index
indice du maillage.
int material_index
indice de la matiere des primitives.
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().
Color Yellow()
utilitaire. renvoie une couleur jaune.
bool write_image(const Image &image, const char *filename, const bool flipY)
enregistre une image au format .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 { max(a.x, b.x), max(a.y, b.y), max(a....
Transform Viewport(const float width, const float height)
renvoie la matrice representant une transformation viewport.
Transform Identity()
construit la transformation identite.
Point min(const Point &a, const Point &b)
renvoie la plus petite composante de chaque point { min(a.x, b.x), min(a.y, b.y), min(a....
float dot(const Vector &u, const Vector &v)
renvoie le produit scalaire de 2 vecteurs.
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.
intersection avec un triangle.
instance pour le bvh, cf fonctions bounds() et intersect().
construction de l'arbre / BVH.
representation d'un point 3d.
triangle pour le bvh, cf fonction bounds() et intersect().
representation d'un vecteur 3d.
vecteur generique, utilitaire.
Node make_leaf(const BBox &bounds, const int begin, const int end)
creation d'une feuille.
Node make_node(const BBox &bounds, const int left, const int right)
creation d'un noeud interne.