gKit2 light
tuto_bvh2_gltf_brdf.cpp
Go to the documentation of this file.
1 
3 
4 #include <random>
5 #include <algorithm>
6 #include <vector>
7 #include <cfloat>
8 
9 #include "vec.h"
10 #include "mat.h"
11 #include "color.h"
12 #include "image.h"
13 #include "image_io.h"
14 #include "orbiter.h"
15 #include "gltf.h"
16 
17 
19 struct Ray
20 {
21  Point o; // origine
22  float pad;
23  Vector d; // direction
24  float tmax; // tmax= 1 ou \inf, le rayon est un segment ou une demi droite infinie
25 
26  Ray( const Point& _o, const Point& _e ) : o(_o), d(Vector(_o, _e)), tmax(1) {} // segment, t entre 0 et 1
27  Ray( const Point& _o, const Vector& _d ) : o(_o), d(_d), tmax(FLT_MAX) {} // demi droite, t entre 0 et \inf
28  Ray( const Point& _o, const Vector& _d, const float _tmax ) : o(_o), d(_d), tmax(_tmax) {} // explicite
29 };
30 
32 struct Hit
33 {
34  float t; // p(t)= o + td, position du point d'intersection sur le rayon
35  float u, v; // p(u, v), position du point d'intersection sur le triangle
36  int instance_id; // indice de l'instance, pour retrouver la transformation
37  int mesh_id; // indexation globale du triangle dans la scene gltf
38  int primitive_id;
39  int triangle_id;
40  int pad;
41 
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) {}
44 
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) {}
47 
48  operator bool ( ) { return (triangle_id != -1); } // renvoie vrai si l'intersection est definie / existe
49 };
50 
52 struct BBoxHit
53 {
54  float tmin, tmax;
55 
56  BBoxHit() : tmin(FLT_MAX), tmax(-FLT_MAX) {}
57  BBoxHit( const float _tmin, const float _tmax ) : tmin(_tmin), tmax(_tmax) {}
58 
59  operator bool( ) const { return tmin <= tmax; } // renvoie vrai si l'intersection est definie / existe
60 };
61 
62 
64 struct BBox
65 {
66  Point pmin, pmax;
67 
68  BBox( ) : pmin(), pmax() {}
69 
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)) {}
73 
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; }
76 
77  float centroid( const int axis ) const { return (pmin(axis) + pmax(axis)) / 2; }
78  Point centroid( ) const { return (pmin + pmax) / 2; }
79 
80  BBoxHit intersect( const Ray& ray, const Vector& invd, const float htmax ) const
81  {
82  Point rmin= pmin;
83  Point rmax= pmax;
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;
89 
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);
93  }
94 };
95 
96 
98 struct Node
99 {
100  BBox bounds;
101  int left;
102  int right;
103 
104  bool internal( ) const { return right > 0; } // renvoie vrai si le noeud est un noeud interne
105  int internal_left( ) const { assert(internal()); return left; } // renvoie le fils gauche du noeud interne
106  int internal_right( ) const { assert(internal()); return right; } // renvoie le fils droit
107 
108  bool leaf( ) const { return right < 0; } // renvoie vrai si le noeud est une feuille
109  int leaf_begin( ) const { assert(leaf()); return -left; } // renvoie le premier objet de la feuille
110  int leaf_end( ) const { assert(leaf()); return -right; } // renvoie le dernier objet
111 };
112 
114 Node make_node( const BBox& bounds, const int left, const int right )
115 {
116  Node node { bounds, left, right };
117  assert(node.internal()); // verifie que c'est bien un noeud...
118  return node;
119 }
120 
122 Node make_leaf( const BBox& bounds, const int begin, const int end )
123 {
124  Node node { bounds, -begin, -end };
125  assert(node.leaf()); // verifie que c'est bien une feuille...
126  return node;
127 }
128 
129 
131 template < typename T >
132 struct BVHT
133 {
134  // construit un bvh pour l'ensemble de primitives
135  int build( const std::vector<T>& _primitives )
136  {
137  primitives= _primitives; // copie les primitives pour les trier
138  nodes.clear(); // efface les noeuds
139  nodes.reserve(primitives.size());
140 
141  // construit l'arbre...
142  root= build(0, primitives.size());
143  return root;
144  }
145 
146  // intersection avec un rayon, entre 0 et htmax
147  Hit intersect( const Ray& ray, const float htmax ) const
148  {
149  Hit hit;
150  hit.t= htmax;
151  Vector invd= Vector(1 / ray.d.x, 1 / ray.d.y, 1 / ray.d.z);
152  intersect(root, ray, invd, hit);
153  return hit;
154  }
155 
156  // intersection avec un rayon, entre 0 et ray.tmax
157  Hit intersect( const Ray& ray ) const { return intersect(ray, ray.tmax); }
158 
159 protected:
160  std::vector<Node> nodes;
161  std::vector<T> primitives;
162  int root;
163 
164  int build( const int begin, const int end )
165  {
166  if(end - begin < 2)
167  {
168  // inserer une feuille et renvoyer son indice
169  int index= nodes.size();
170  nodes.push_back( make_leaf( primitive_bounds(begin, end), begin, end ) );
171  return index;
172  }
173 
174  // axe le plus etire de l'englobant des centres des englobants des primitives...
175  BBox cbounds= centroid_bounds(begin, end);
176  Vector d= Vector(cbounds.pmin, cbounds.pmax);
177  int axis;
178  if(d.x > d.y && d.x > d.z) // x plus grand que y et z ?
179  axis= 0;
180  else if(d.y > d.z) // y plus grand que z ? (et que x implicitement)
181  axis= 1;
182  else // x et y ne sont pas les plus grands...
183  axis= 2;
184 
185  // coupe l'englobant au milieu
186  float cut= cbounds.centroid(axis);
187 
188  // repartit les primitives
189  T *pm= std::partition(primitives.data() + begin, primitives.data() + end,
190  [axis, cut]( const T& primitive )
191  {
192  return primitive.bounds().centroid(axis) < cut;
193  }
194  );
195  int m= std::distance(primitives.data(), pm);
196 
197  // la repartition peut echouer, et toutes les primitives sont dans la meme moitiee de l'englobant
198  // forcer quand meme un decoupage en 2 ensembles
199  if(m == begin || m == end)
200  m= (begin + end) / 2;
201  assert(m != begin);
202  assert(m != end);
203 
204  // construire le fils gauche, les primtives se trouvent dans [begin .. m)
205  int left= build(begin, m);
206 
207  // on recommence pour le fils droit, les primtives se trouvent dans [m .. end)
208  int right= build(m, end);
209 
210  // construire le noeud et renvoyer son indice
211  int index= nodes.size();
212  nodes.push_back( make_node( BBox(nodes[left].bounds, nodes[right].bounds), left, right ) );
213  return index;
214  }
215 
216  // englobant des primitives
217  BBox primitive_bounds( const int begin, const int end )
218  {
219  BBox bbox= primitives[begin].bounds();
220  for(int i= begin +1; i < end; i++)
221  bbox.insert(primitives[i].bounds());
222 
223  return bbox;
224  }
225 
226  // englobant des centres des primitives
227  BBox centroid_bounds( const int begin, const int end )
228  {
229  BBox bbox= primitives[begin].bounds().centroid();
230  for(int i= begin +1; i < end; i++)
231  bbox.insert(primitives[i].bounds().centroid());
232 
233  return bbox;
234  }
235 
236  // intersection et parcours simple
237  void intersect( const int index, const Ray& ray, const Vector& invd, Hit& hit ) const
238  {
239  const Node& node= nodes[index];
240  if(node.bounds.intersect(ray, invd, hit.t))
241  {
242  if(node.leaf())
243  {
244  for(int i= node.leaf_begin(); i < node.leaf_end(); i++)
245  if(Hit h= primitives[i].intersect(ray, hit.t))
246  hit= h;
247  }
248  else // if(node.internal())
249  {
250  intersect(node.internal_left(), ray, invd, hit);
251  intersect(node.internal_right(), ray, invd, hit);
252  }
253  }
254  }
255 };
256 
257 
259 struct Triangle
260 {
261  Point p; // sommet a du triangle
262  Vector e1, e2; // aretes ab, ac du triangle
263  int mesh_id;
264  int primitive_id;
265  int triangle_id;
266 
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) {}
270 
271  /* calcule l'intersection ray/triangle
272  cf "fast, minimum storage ray-triangle intersection"
273 
274  renvoie faux s'il n'y a pas d'intersection valide (une intersection peut exister mais peut ne pas se trouver dans l'intervalle [0 tmax] du rayon.)
275  renvoie vrai + les coordonnees barycentriques (u, v) du point d'intersection + sa position le long du rayon (t).
276  convention barycentrique : p(u, v)= (1 - u - v) * a + u * b + v * c
277  */
278  Hit intersect( const Ray &ray, const float htmax ) const
279  {
280  Vector pvec= cross(ray.d, e2);
281  float det= dot(e1, pvec);
282 
283  float inv_det= 1 / det;
284  Vector tvec(p, ray.o);
285 
286  float u= dot(tvec, pvec) * inv_det;
287  if(u < 0 || u > 1) return Hit();
288 
289  Vector qvec= cross(tvec, e1);
290  float v= dot(ray.d, qvec) * inv_det;
291  if(v < 0 || u + v > 1) return Hit();
292 
293  float t= dot(e2, qvec) * inv_det;
294  if(t < 0 || t > htmax) return Hit();
295 
296  return Hit(t, u, v, mesh_id, primitive_id, triangle_id);
297  }
298 
299  BBox bounds( ) const
300  {
301  BBox box(p);
302  return box.insert(p+e1).insert(p+e2);
303  }
304 };
305 
306 typedef BVHT<Triangle> BVH;
307 typedef BVHT<Triangle> BLAS;
308 
309 
311 struct Instance
312 {
313  Transform object_transform;
314  BBox world_bounds;
315  BVH *object_bvh;
316  int instance_id;
317 
318  Instance( const BBox& bounds, const Transform& model, BVH *bvh, const int id ) :
319  object_transform(Inverse(model)), world_bounds(transform(bounds, model)),
320  object_bvh(bvh),
321  instance_id(id)
322  {}
323 
324  BBox bounds( ) const { return world_bounds; }
325 
326  Hit intersect( const Ray &ray, const float htmax ) const
327  {
328  // transforme le rayon
329  Ray object_ray(object_transform(ray.o), object_transform(ray.d), htmax);
330  // et intersection dans le bvh de l'objet instancie...
331 
332  Hit hit= object_bvh->intersect(object_ray, htmax);
333  if(hit)
334  // si intersection, stocker aussi l'indice de l'instance, cf retrouver la transformation et la matiere associee au mesh/triangle...
335  hit.instance_id= instance_id;
336 
337  return hit;
338  }
339 
340 protected:
341  BBox transform( const BBox& bbox, const Transform& m )
342  {
343  BBox bounds= BBox( m(bbox.pmin) );
344  // enumere les sommets de la bbox
345  for(unsigned i= 1; i < 8; i++)
346  {
347  // chaque sommet de la bbox est soit pmin soit pmax sur chaque axe...
348  Point p= bbox.pmin;
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;
352 
353  // transforme le sommet de l'englobant
354  bounds.insert( m(p) );
355  }
356 
357  return bounds;
358  }
359 };
360 
361 typedef BVHT<Instance> TLAS;
362 
363 
365 Point hit_position( const Hit& hit, const Ray& ray ) { assert(hit.triangle_id != -1); return ray.o + hit.t * ray.d; }
366 
368 bool has_normals( const Hit& hit, const GLTFScene& scene )
369 {
370  assert(hit.mesh_id != -1);
371  assert(hit.primitive_id != -1);
372  const GLTFMesh& mesh= scene.meshes[hit.mesh_id];
373  const GLTFPrimitives& primitives= mesh.primitives[hit.primitive_id];
374  return primitives.normals.size() > 0;
375 }
376 
378 Vector hit_normal( const Hit& hit, const GLTFScene& scene )
379 {
380  assert(hit.instance_id != -1);
381  assert(hit.mesh_id != -1);
382  assert(hit.primitive_id != -1);
383  assert(hit.triangle_id != -1);
384  const GLTFMesh& mesh= scene.meshes[hit.mesh_id];
385  const GLTFPrimitives& primitives= mesh.primitives[hit.primitive_id];
386 
387  // indice des sommets
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];
391 
392  // normales des sommets
393  assert(primitives.normals.size());
394  Vector na= primitives.normals[a];
395  Vector nb= primitives.normals[b];
396  Vector nc= primitives.normals[c];
397 
398  // interpole la normale au point d'intersection
399  // attention : il faut utiliser la meme convetion barycentrique que la fonction d'intersection rayon/triangle !!
400  Vector n= (1 - hit.u - hit.v) * na + hit.u * nb + hit.v * nc;
401 
402  // transforme la normale dans le repere de la scene
403  const GLTFNode& node= scene.nodes[hit.instance_id];
404  // les normales ne se transforment pas exactement comme les positions...
405  // les sommets sont transformes par node.model, comment transformer la normale pour quelle reste perpendiculaire au triangle ? cf Transform::normal()
406  Transform T= node.model.normal();
407 
408  n= normalize( T(n) );
409  return n;
410 }
411 
413 Vector triangle_normal( const Hit& hit, const GLTFScene& scene )
414 {
415  assert(hit.instance_id != -1);
416  assert(hit.mesh_id != -1);
417  assert(hit.primitive_id != -1);
418  assert(hit.triangle_id != -1);
419  const GLTFMesh& mesh= scene.meshes[hit.mesh_id];
420  const GLTFPrimitives& primitives= mesh.primitives[hit.primitive_id];
421 
422  // indice des sommets
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];
426 
427  // postion des sommets
428  assert(primitives.positions.size());
429  Point pa= primitives.positions[a];
430  Point pb= primitives.positions[b];
431  Point pc= primitives.positions[c];
432 
433  // normale geometrique
434  Vector n= cross( Vector(pa, pb), Vector(pa, pc) );
435 
436  // transforme la normale dans le repere de la scene
437  const GLTFNode& node= scene.nodes[hit.instance_id];
438  // les normales ne se transforment pas exactement comme les positions...
439  // les sommets sont transformes par node.model, comment transformer la normale pour quelle reste perpendiculaire au triangle ? cf Transform::normal()
440  Transform T= node.model.normal();
441 
442  n= normalize( T(n) );
443  return n;
444 }
445 
447 bool has_texcoords( const Hit& hit, const GLTFScene& scene )
448 {
449  assert(hit.mesh_id != -1);
450  assert(hit.primitive_id != -1);
451  const GLTFMesh& mesh= scene.meshes[hit.mesh_id];
452  const GLTFPrimitives& primitives= mesh.primitives[hit.primitive_id];
453  return primitives.texcoords.size() > 0;
454 }
455 
457 vec2 hit_texcoords( const Hit& hit, const GLTFScene& scene )
458 {
459  assert(hit.instance_id != -1);
460  assert(hit.mesh_id != -1);
461  assert(hit.primitive_id != -1);
462  assert(hit.triangle_id != -1);
463  const GLTFMesh& mesh= scene.meshes[hit.mesh_id];
464  const GLTFPrimitives& primitives= mesh.primitives[hit.primitive_id];
465 
466  // indice des sommets
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];
470 
471  // texcoords des sommets
472  assert(primitives.texcoords.size());
473  vec2 ta= primitives.texcoords[a];
474  vec2 tb= primitives.texcoords[b];
475  vec2 tc= primitives.texcoords[c];
476 
477  // interpole au point d'intersection
478  // attention : il faut utiliser la meme convention barycentrique que la fonction d'intersection rayon/triangle !!
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 );
480  return t;
481 }
482 
483 
486 Color sample_texture( const vec2& t, const ImageData& texture )
487 {
488  // retrouve les coordonnes de t dans l'image
489  int tx= int(t.x * texture.width) % texture.width;
490  int ty= int(t.y * texture.height) % texture.height;
491  // attention aux repetitions, les coordoornees ne sont pas toujours entre 0 et 1 !!
492  if(tx < 0) tx= -tx;
493  if(ty < 0) ty= -ty;
494 
495  // respecte la convention gltf, origine en haut de l'image...
496  ty= texture.height - 1 - ty;
497  // position du pixel dans l'image
498  size_t offset= texture.offset(tx, ty);
499 
500  // recupere la couleur du pixel
501  Color color= Color(texture.pixels[offset], texture.pixels[offset+1], texture.pixels[offset+2], 255) / 255;
502  if(texture.channels > 3)
503  // alpha / transparence, si disponible
504  color.a= float(texture.pixels[offset+3]) / float(255);
505 
506  return color;
507 }
508 
510 static GLTFMaterial default_material= GLTFMaterial ();
511 
513 const GLTFMaterial& hit_material( const Hit& hit, const GLTFScene& scene )
514 {
515  assert(hit.mesh_id != -1);
516  assert(hit.primitive_id != -1);
517  const GLTFMesh& mesh= scene.meshes[hit.mesh_id];
518  const GLTFPrimitives& primitives= mesh.primitives[hit.primitive_id];
519  if(primitives.material_index == -1)
520  return default_material;
521 
522  assert(primitives.material_index < int(scene.materials.size()));
523  return scene.materials[primitives.material_index];
524 }
525 
527 Color hit_color( const Hit& hit, const GLTFScene& scene )
528 {
529  return hit_material(hit, scene).color;
530 }
531 
532 
534 struct Brdf
535 {
537 
541 
542  float metallic;
543  float alpha;
544  float transmission;
545 };
546 
548 Brdf hit_brdf( const Hit& hit, const GLTFScene& scene, const std::vector<ImageData>& textures )
549 {
550  // recupere la description de la matiere...
551  const GLTFMaterial& material= hit_material(hit, scene);
552 
553  // et les coordonnees de textures, si elles existent...
554  vec2 texcoords= vec2(.5, .5);
555  bool use_texture= has_texcoords(hit, scene) && textures.size();
556  if(use_texture)
557  texcoords= hit_texcoords(hit, scene);
558 
559  Color color= material.color;
560  if(use_texture && material.color_texture != -1 && material.color_texture < int(textures.size()))
561  color= color * sample_texture(texcoords, textures[material.color_texture]);
562 
563  float metallic= material.metallic;
564  float roughness= material.roughness;
565  if(use_texture && material.metallic_roughness_texture != -1 && material.metallic_roughness_texture < int(textures.size()))
566  {
567  Color texel= sample_texture(texcoords, textures[material.metallic_roughness_texture]);
568  metallic= metallic * texel.b;
569  roughness= roughness * texel.g;
570  }
571 
572  float transmission= material.transmission;
573  if(use_texture && material.transmission_texture != -1 && material.transmission_texture < int(textures.size()))
574  transmission= transmission * sample_texture(texcoords, textures[material.transmission_texture]).r;
575 
576  Brdf brdf;
577  {
578  if(has_normals(hit, scene))
579  // normale interpolee
580  brdf.n= hit_normal(hit, scene);
581  else
582  // normale geometrique du triangle
583  brdf.n= triangle_normal(hit, scene);
584 
585  brdf.diffuse= (1 - metallic) * color;
586  brdf.F0= (1 - metallic) * Color(0.04) + metallic * color;
587  // todo utiliser material.ior a la place de 0.04, si metal==0 et ior != 0
588  // todo utiliser material.specular a la place de F0, si metal==0 et specular != black
589  brdf.emission= material.emission;
590  brdf.metallic= metallic;
591  brdf.alpha= roughness * roughness;
592  brdf.transmission= transmission;
593  }
594 
595  return brdf;
596 }
597 
598 
600 struct Sampler
601 {
602  std::uniform_real_distribution<float> u01;
603  std::default_random_engine rng;
604 
605  Sampler( const unsigned _seed ) : u01(), rng(_seed) {}
606  void seed( const unsigned _seed ) { rng= std::default_random_engine(_seed); }
607 
608  float sample( ) { return u01(rng); }
609 
610  int sample_range( const int n ) { return int(sample() * n); }
611 };
612 
613 
614 int main( int argc, char **argv )
615 {
616  const char *mesh_filename= "data/robot.gltf";
617  const char *orbiter_filename= nullptr;
618 
619  if(argc > 1) mesh_filename= argv[1];
620  if(argc > 2) orbiter_filename= argv[2];
621 
622  GLTFScene scene= read_gltf_scene(mesh_filename);
623 
624  // construit les bvh des objets de la scene, en parallele ! cf BLAS / bvh de triangles
625  std::vector<BVH *> bvhs(scene.meshes.size());
626  {
627  // parcourir les mesh
628  printf("%d meshes\n", int(scene.meshes.size()));
629 
630  #pragma omp parallel for
631  for(unsigned mesh_id= 0; mesh_id < scene.meshes.size(); mesh_id++)
632  {
633  const GLTFMesh& mesh= scene.meshes[mesh_id];
634 
635  // groupes de triangles du mesh
636  std::vector<Triangle> triangles;
637  for(unsigned primitive_id= 0; primitive_id < mesh.primitives.size(); primitive_id++)
638  {
639  const GLTFPrimitives& primitives= mesh.primitives[primitive_id];
640 
641  for(unsigned i= 0; i +2 < primitives.indices.size(); i+= 3)
642  {
643  // extraire les positions des sommets du triangle
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) );
648  // stocke aussi l'indice du triangle
649  }
650  }
651 
652  BVH *bvh= new BVH;
653  bvh->build(triangles);
654  bvhs[mesh_id]= bvh;
655  }
656  }
657 
658  // instancie les objets de la scene, cf TLAS / bvh d'instances
659  TLAS top_bvh;
660  {
661  printf("%d nodes\n", int(scene.nodes.size()));
662 
663  // 1 instance par noeud de la scene gltf
664  std::vector<Instance> instances;
665  for(unsigned node_id= 0; node_id < scene.nodes.size(); node_id++)
666  {
667  const GLTFNode& node= scene.nodes[node_id];
668  const GLTFMesh& mesh= scene.meshes[node.mesh_index];
669 
670  instances.push_back( Instance( BBox(mesh.pmin, mesh.pmax), node.model, bvhs[node.mesh_index], node_id ) );
671  }
672 
673  top_bvh.build(instances);
674  printf("done. %d instances\n", int(instances.size()));
675  }
676 
677  // charge les textures...
678  std::vector<ImageData> textures= read_gltf_images(mesh_filename);
679 
680 
681  // recupere les matrices de la camera gltf
682  assert(scene.cameras.size());
683  Transform view= scene.cameras[0].view;
684  Transform projection= scene.cameras[0].projection;
685 
686  // cree l'image en respectant les proportions largeur/hauteur de la camera gltf
687  int width= 1024;
688  int height= width / scene.cameras[0].aspect;
689  Image image(width, height, Color(0.2));
690 
691  // transformations
692  Transform model= Identity();
693  Transform viewport= Viewport(image.width(), image.height());
694  Transform inv= Inverse(viewport * projection * view * model);
695 
696 
697  // calcule l'image en parallele avec openMP
698 #pragma omp parallel for
699  for(int y= 0; y < image.height(); y++)
700  for(int x= 0; x < image.width(); x++)
701  {
702  // genere le rayon pour le pixel x,y
703  Point o= inv( Point(x, y, 0) ); // origine
704  Point e= inv( Point(x, y, 1) ); // extremite
705  Ray ray(o, Vector(o, e));
706 
707  // intersections !
708  if(Hit hit= top_bvh.intersect(ray))
709  {
710  // evalue les parametres de la matiere au point d'intersection
711  Brdf fr= hit_brdf(hit, scene, textures);
712 
713  float cos_theta= std::abs(dot(fr.n, normalize(ray.d)));
714  Color color= fr.diffuse * cos_theta;
715 
716  image(x, y)= Color(color, 1);
717  }
718  }
719  printf("\n");
720 
721  write_image(image, "render.png");
722  return 0;
723 }
representation d'une image.
Definition: image.h:21
GLenum primitives() const
renvoie le type de primitives.
Definition: mesh.h:336
scene glTF.
int mesh_index
indice du maillage.
Definition: gltf.h:131
std::vector< ImageData > read_gltf_images(const char *filename)
charge un fichier .gltf et charge les images referencees par les matieres.
Definition: gltf.cpp:655
int material_index
indice de la matiere des primitives.
Definition: gltf.h:102
GLTFScene read_gltf_scene(const char *filename)
charge un fichier .gltf et construit une scene statique, sans animation.
Definition: gltf.cpp:726
Transform model
transformation model pour dessiner le maillage.
Definition: gltf.h:130
description d'un maillage.
Definition: gltf.h:115
position et orientation d'un maillage dans la scene.
Definition: gltf.h:129
groupe de triangles d'un maillage. chaque groupe est associe a une matiere.
Definition: gltf.h:99
void begin(Widgets &w)
debut de la description des elements de l'interface graphique.
Definition: widgets.cpp:29
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().
Definition: text.cpp:140
void end(Widgets &w)
termine la description des elements de l'interface graphique.
Definition: widgets.cpp:404
int write_image(const Image &image, const char *filename)
enregistre une image dans un fichier png.
Definition: image_io.cpp:85
Transform Inverse(const Transform &m)
renvoie l'inverse de la matrice.
Definition: mat.cpp:197
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,...
Definition: vec.cpp:35
Transform Viewport(const float width, const float height)
renvoie la matrice representant une transformation viewport.
Definition: mat.cpp:357
Transform Identity()
construit la transformation identite.
Definition: mat.cpp:187
float distance(const Point &a, const Point &b)
renvoie la distance etre 2 points.
Definition: vec.cpp:14
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,...
Definition: vec.cpp:30
float dot(const Vector &u, const Vector &v)
renvoie le produit scalaire de 2 vecteurs.
Definition: vec.cpp:137
Vector normalize(const Vector &v)
renvoie un vecteur unitaire / longueur == 1.
Definition: vec.cpp:123
Vector cross(const Vector &u, const Vector &v)
renvoie le produit vectoriel de 2 vecteurs.
Definition: vec.cpp:129
void bounds(const MeshData &data, Point &pmin, Point &pmax)
renvoie l'englobant.
Definition: mesh_data.cpp:290
intersection avec une boite / un englobant.
Definition: tuto_bvh.cpp:36
boite englobante.
Definition: tuto_bvh.cpp:47
bvh parametre par le type des primitives, cf triangle et instance...
Definition: tuto_bvh2.cpp:128
representation d'une couleur (rgba) transparente ou opaque.
Definition: color.h:14
float transmission
transmission, transparent ou pas (= 0)
Definition: gltf.h:64
int color_texture
indice de la texture ou -1. cf read_gltf_images() pour charger les textures dans le bon ordre....
Definition: gltf.h:73
float roughness
rugosite de la micro surface.
Definition: gltf.h:63
int metallic_roughness_texture
indice de la texture ou -1. les valeurs RGB representent les parametres du modele : B= metallic,...
Definition: gltf.h:74
Color emission
emission pour les sources de lumieres ou pas (= noir).
Definition: gltf.h:61
int transmission_texture
indice de la texture ou -1.
Definition: gltf.h:78
float metallic
metallic / dielectrique.
Definition: gltf.h:62
Color color
base color.
Definition: gltf.h:60
std::vector< GLTFMaterial > materials
matieres.
Definition: gltf.h:153
std::vector< GLTFNode > nodes
noeuds / position et orientation des maillages dans la scene.
Definition: gltf.h:151
std::vector< GLTFMesh > meshes
ensemble de maillages.
Definition: gltf.h:150
std::vector< GLTFCamera > cameras
cameras.
Definition: gltf.h:155
intersection avec un triangle.
Definition: tuto_bvh2.cpp:33
stockage temporaire des donnees d'une image.
Definition: image_io.h:38
instance pour le bvh, cf fonctions bounds() et intersect().
Definition: tuto_bvh2.cpp:303
construction de l'arbre / BVH.
Definition: tuto_bvh.cpp:133
representation d'un point 3d.
Definition: vec.h:21
rayon.
Definition: tuto_bvh2.cpp:20
generation de nombres aleatoires entre 0 et 1.
representation d'une transformation, une matrice 4x4, organisee par ligne / row major.
Definition: mat.h:21
Transform normal() const
renvoie la transformation a appliquer aux normales d'un objet transforme par la matrice m.
Definition: mat.cpp:181
vec3 c
positions
Definition: mesh.h:96
triangle pour le bvh, cf fonction bounds() et intersect().
Definition: tuto_bvh.cpp:84
representation d'un vecteur 3d.
Definition: vec.h:59
vecteur generique, utilitaire.
Definition: vec.h:131
vecteur generique, utilitaire.
Definition: vec.h:146
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)
Color diffuse
color.
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.
Color F0
fresnel.
regroupe tous les parametres de la matiere du point d'intersection.