gKit2 light
tuto_gltf.cpp
1 
2 #include <random>
3 
4 #include "orbiter.h"
5 #include "draw.h"
6 #include "app_camera.h" // classe Application a deriver
7 #include "widgets.h" // classe Application a deriver
8 #include "image_hdr.h"
9 #include "texture.h"
10 
11 #include "gltf.h"
12 
13 
14 // utilitaire. creation d'une grille / repere.
15 Mesh make_grid( const int n= 10 )
16 {
17  Mesh grid= Mesh(GL_LINES);
18 
19  // grille
20  grid.color(White());
21  for(int x= 0; x < n; x++)
22  {
23  float px= float(x) - float(n)/2 + .5f;
24  grid.vertex(Point(px, -0.01, - float(n)/2 + .5f));
25  grid.vertex(Point(px, -0.01, float(n)/2 - .5f));
26  }
27 
28  for(int z= 0; z < n; z++)
29  {
30  float pz= float(z) - float(n)/2 + .5f;
31  grid.vertex(Point(- float(n)/2 + .5f, -0.01, pz));
32  grid.vertex(Point(float(n)/2 - .5f, -0.01, pz));
33  }
34 
35  // axes XYZ
36  grid.color(Red());
37  grid.vertex(Point(0, .01, 0));
38  grid.vertex(Point(0.25, .01, 0));
39 
40  grid.color(Green());
41  grid.vertex(Point(0, .01, 0));
42  grid.vertex(Point(0, 0.25, 0));
43 
44  grid.color(Blue());
45  grid.vertex(Point(0, .01, 0));
46  grid.vertex(Point(0, .01, 0.25));
47 
48  return grid;
49 }
50 
51 Mesh make_camera( )
52 {
53  Mesh camera= Mesh(GL_LINES);
54 
55  camera.color(Yellow());
56  camera.vertex(0,0,0);
57  camera.vertex(-0.25, -0.25, -0.5);
58  camera.vertex(0,0,0);
59  camera.vertex(-0.25, 0.25, -0.5);
60  camera.vertex(0,0,0);
61  camera.vertex(0.25, 0.25, -0.5);
62  camera.vertex(0,0,0);
63  camera.vertex(0.25, -0.25, -0.5);
64 
65  camera.vertex(-0.25, -0.25, -0.5);
66  camera.vertex(-0.25, 0.25, -0.5);
67 
68  camera.vertex(-0.25, 0.25, -0.5);
69  camera.vertex(0.25, 0.25, -0.5);
70 
71  camera.vertex(0.25, 0.25, -0.5);
72  camera.vertex(0.25, -0.25, -0.5);
73 
74  camera.vertex(0.25, -0.25, -0.5);
75  camera.vertex(-0.25, -0.25, -0.5);
76 
77  // axes XYZ
78  camera.color(Red());
79  camera.vertex(Point(0, 0, 0));
80  camera.vertex(Point(0.25, 0, 0));
81 
82  camera.color(Green());
83  camera.vertex(Point(0, 0, 0));
84  camera.vertex(Point(0, 0.25, 0));
85 
86  camera.color(Blue());
87  camera.vertex(Point(0, 0, 0));
88  camera.vertex(Point(0, 0, 0.25));
89 
90  return camera;
91 }
92 
93 Mesh make_image_plane( const float z= -1, const int n= 8 )
94 {
95  Mesh plane= Mesh(GL_POINTS);
96 
97  plane.color(Yellow());
98  for(int py= 0; py < n; py++)
99  for(int px= 0; px < n; px++)
100  {
101  // repere projectif...
102  float x= 2 * float(px + 0.5) / float(n) - 1;
103  float y= 2 * float(py + 0.5) / float(n) - 1;
104  plane.vertex(x, y, z);
105  }
106 
107  return plane;
108 }
109 
110 
111 Mesh make_frustum( const float znear= -0.5, const float zfar= 0.95 )
112 {
113  Mesh frustum= Mesh(GL_LINES);
114 
115  frustum.color(Yellow());
116  // repere projectif...
117  // face avant
118  frustum.vertex(-1, -1, znear);
119  frustum.vertex(-1, 1, znear);
120  frustum.vertex(-1, 1, znear);
121  frustum.vertex( 1, 1, znear);
122  frustum.vertex( 1, 1, znear);
123  frustum.vertex( 1, -1, znear);
124  frustum.vertex( 1, -1, znear);
125  frustum.vertex(-1, -1, znear);
126 
127  // face arriere
128  frustum.vertex(-1, -1, zfar);
129  frustum.vertex(-1, 1, zfar);
130  frustum.vertex(-1, 1, zfar);
131  frustum.vertex( 1, 1, zfar);
132  frustum.vertex( 1, 1, zfar);
133  frustum.vertex( 1, -1, zfar);
134  frustum.vertex( 1, -1, zfar);
135  frustum.vertex(-1, -1, zfar);
136 
137  // aretes
138  frustum.vertex(-1, -1, znear);
139  frustum.vertex(-1, -1, zfar);
140  frustum.vertex(-1, 1, znear);
141  frustum.vertex(-1, 1, zfar);
142  frustum.vertex( 1, 1, znear);
143  frustum.vertex( 1, 1, zfar);
144  frustum.vertex( 1, -1, znear);
145  frustum.vertex( 1, -1, zfar);
146 
147  return frustum;
148 }
149 
150 // rayon
151 struct Ray
152 {
153  Point o; // origine
154  float pad;
155  Vector d; // direction
156  float tmax; // tmax= 1 ou \inf, le rayon est un segment ou une demi droite infinie
157 
158  Ray( const Point& _o, const Point& _e ) : o(_o), d(Vector(_o, _e)), tmax(1) {} // segment, t entre 0 et 1
159  Ray( const Point& _o, const Vector& _d ) : o(_o), d(_d), tmax(FLT_MAX) {} // demi droite, t entre 0 et \inf
160  Ray( const Point& _o, const Vector& _d, const float _tmax ) : o(_o), d(_d), tmax(_tmax) {} // explicite
161 };
162 
163 // intersection avec un triangle
164 struct Hit
165 {
166  float t; // p(t)= o + td, position du point d'intersection sur le rayon
167  float u, v; // p(u, v), position du point d'intersection sur le triangle
168  int node_id; // indice de l'instance, pour retrouver la transformation
169  int mesh_id; // indexation globale du triangle dans la scene gltf
170  int primitive_id;
171  int triangle_id;
172  int pad;
173 
174  Hit( ) : t(FLT_MAX), u(), v(), node_id(-1), mesh_id(-1), primitive_id(-1), triangle_id(-1) {}
175  Hit( const Ray& ray ) : t(ray.tmax), u(), v(), node_id(-1), mesh_id(-1), primitive_id(-1), triangle_id(-1) {}
176 
177  Hit( const float _t, const float _u, const float _v, const int _node_id, const int _mesh_id, const int _primitive_id, const int _id ) : t(_t), u(_u), v(_v),
178  node_id(_node_id), mesh_id(_mesh_id), primitive_id(_primitive_id), triangle_id(_id) {}
179 
180  operator bool ( ) { return (triangle_id != -1); } // renvoie vrai si l'intersection est definie / existe
181 };
182 
183 // intersection avec une boite / un englobant
184 struct BBoxHit
185 {
186  float tmin, tmax;
187 
188  BBoxHit() : tmin(FLT_MAX), tmax(-FLT_MAX) {}
189  BBoxHit( const float _tmin, const float _tmax ) : tmin(_tmin), tmax(_tmax) {}
190 
191  operator bool( ) const { return tmin <= tmax; } // renvoie vrai si l'intersection est definie / existe
192 };
193 
194 
195 // boite englobante
196 struct BBox
197 {
198  Point pmin, pmax;
199 
200  BBox( ) : pmin(), pmax() {}
201 
202  BBox( const Point& p ) : pmin(p), pmax(p) {}
203  BBox( const BBox& box ) : pmin(box.pmin), pmax(box.pmax) {}
204  BBox( const BBox& a, const BBox& b ) : pmin(min(a.pmin, b.pmin)), pmax(max(a.pmax, b.pmax)) {}
205 
206  BBox& insert( const Point& p ) { pmin= min(pmin, p); pmax= max(pmax, p); return *this; }
207  BBox& insert( const BBox& box ) { pmin= min(pmin, box.pmin); pmax= max(pmax, box.pmax); return *this; }
208 
209  float centroid( const int axis ) const { return (pmin(axis) + pmax(axis)) / 2; }
210  Point centroid( ) const { return (pmin + pmax) / 2; }
211 
212  BBoxHit intersect( const Ray& ray, const Vector& invd, const float htmax ) const
213  {
214  Point rmin= pmin;
215  Point rmax= pmax;
216  if(ray.d.x < 0) std::swap(rmin.x, rmax.x);
217  if(ray.d.y < 0) std::swap(rmin.y, rmax.y);
218  if(ray.d.z < 0) std::swap(rmin.z, rmax.z);
219  Vector dmin= (rmin - ray.o) * invd;
220  Vector dmax= (rmax - ray.o) * invd;
221 
222  float tmin= std::max(dmin.z, std::max(dmin.y, std::max(dmin.x, 0.f)));
223  float tmax= std::min(dmax.z, std::min(dmax.y, std::min(dmax.x, htmax)));
224  return BBoxHit(tmin, tmax);
225  }
226 };
227 
228 
229 // construction de l'arbre / BVH
230 struct Node
231 {
232  BBox bounds;
233  int left;
234  int right;
235 
236  bool internal( ) const { return right > 0; } // renvoie vrai si le noeud est un noeud interne
237  int internal_left( ) const { assert(internal()); return left; } // renvoie le fils gauche du noeud interne
238  int internal_right( ) const { assert(internal()); return right; } // renvoie le fils droit
239 
240  bool leaf( ) const { return right < 0; } // renvoie vrai si le noeud est une feuille
241  int leaf_begin( ) const { assert(leaf()); return -left; } // renvoie le premier objet de la feuille
242  int leaf_end( ) const { assert(leaf()); return -right; } // renvoie le dernier objet
243 };
244 
245 // creation d'un noeud interne
246 Node make_node( const BBox& bounds, const int left, const int right )
247 {
248  Node node { bounds, left, right };
249  assert(node.internal()); // verifie que c'est bien un noeud...
250  return node;
251 }
252 
253 // creation d'une feuille
254 Node make_leaf( const BBox& bounds, const int begin, const int end )
255 {
256  Node node { bounds, -begin, -end };
257  assert(node.leaf()); // verifie que c'est bien une feuille...
258  return node;
259 }
260 
261 
262 // bvh parametre par le type des primitives, cf triangle et instance...
263 template < typename T >
264 struct BVHT
265 {
266  // construit un bvh pour l'ensemble de primitives
267  int build( const std::vector<T>& _primitives )
268  {
269  primitives= _primitives; // copie les primitives pour les trier
270  nodes.clear(); // efface les noeuds
271  nodes.reserve(primitives.size());
272 
273  // construit l'arbre...
274  root= build(0, primitives.size());
275  return root;
276  }
277 
278  // intersection avec un rayon, entre 0 et htmax
279  Hit intersect( const Ray& ray, const float htmax ) const
280  {
281  Hit hit;
282  hit.t= htmax;
283  Vector invd= Vector(1 / ray.d.x, 1 / ray.d.y, 1 / ray.d.z);
284  intersect_fast(root, ray, invd, hit);
285  return hit;
286  }
287 
288  // intersection avec un rayon, entre 0 et ray.tmax
289  Hit intersect( const Ray& ray ) const { return intersect(ray, ray.tmax); }
290 
291  bool occluded( const Ray& ray ) const
292  {
293  Vector invd= Vector(1 / ray.d.x, 1 / ray.d.y, 1 / ray.d.z);
294  return occluded(root, ray, invd, 1);
295  }
296 
297  bool occluded( const Point& p, const Point& q ) const { Ray ray(p, q); return occluded(ray); }
298 
299  bool visible( const Point& p, const Point& q ) const { return !occluded(p, q); }
300  bool visible( const Ray& ray ) const { return !occluded(ray); }
301 
302 
303 protected:
304  std::vector<Node> nodes;
305  std::vector<T> primitives;
306  int root;
307 
308  int build( const int begin, const int end )
309  {
310  if(end - begin < 2)
311  {
312  // inserer une feuille et renvoyer son indice
313  int index= nodes.size();
314  nodes.push_back( make_leaf( primitive_bounds(begin, end), begin, end ) );
315  return index;
316  }
317 
318  // axe le plus etire de l'englobant des centres des englobants des primitives...
319  BBox cbounds= centroid_bounds(begin, end);
320  Vector d= Vector(cbounds.pmin, cbounds.pmax);
321  int axis;
322  if(d.x > d.y && d.x > d.z) // x plus grand que y et z ?
323  axis= 0;
324  else if(d.y > d.z) // y plus grand que z ? (et que x implicitement)
325  axis= 1;
326  else // x et y ne sont pas les plus grands...
327  axis= 2;
328 
329  // coupe l'englobant au milieu
330  float cut= cbounds.centroid(axis);
331 
332  // repartit les primitives
333  T *pm= std::partition(primitives.data() + begin, primitives.data() + end,
334  [axis, cut]( const T& primitive )
335  {
336  return primitive.bounds().centroid(axis) < cut;
337  }
338  );
339  int m= std::distance(primitives.data(), pm);
340 
341  // la repartition peut echouer, et toutes les primitives sont dans la meme moitiee de l'englobant
342  // forcer quand meme un decoupage en 2 ensembles
343  if(m == begin || m == end)
344  m= (begin + end) / 2;
345  assert(m != begin);
346  assert(m != end);
347 
348  // construire le fils gauche, les triangles se trouvent dans [begin .. m)
349  int left= build(begin, m);
350 
351  // on recommence pour le fils droit, les triangles se trouvent dans [m .. end)
352  int right= build(m, end);
353 
354  // construire le noeud et renvoyer son indice
355  int index= nodes.size();
356  nodes.push_back( make_node( BBox(nodes[left].bounds, nodes[right].bounds), left, right ) );
357  return index;
358  }
359 
360  // englobant des primitives
361  BBox primitive_bounds( const int begin, const int end )
362  {
363  BBox bbox= primitives[begin].bounds();
364  for(int i= begin +1; i < end; i++)
365  bbox.insert(primitives[i].bounds());
366 
367  return bbox;
368  }
369 
370  // englobant des centres des primitives
371  BBox centroid_bounds( const int begin, const int end )
372  {
373  BBox bbox= primitives[begin].bounds().centroid();
374  for(int i= begin +1; i < end; i++)
375  bbox.insert(primitives[i].bounds().centroid());
376 
377  return bbox;
378  }
379 
380  void intersect_fast( const int index, const Ray& ray, const Vector& invd, Hit& hit ) const
381  {
382  const Node& node= nodes[index];
383 
384  if(node.leaf())
385  {
386  if(node.bounds.intersect(ray, invd, hit.t))
387  {
388  for(int i= node.leaf_begin(); i < node.leaf_end(); i++)
389  if(Hit h= primitives[i].intersect(ray, hit.t))
390  hit= h;
391  }
392  }
393  else
394  {
395  BBoxHit left= nodes[node.internal_left()].bounds.intersect(ray, invd, hit.t);
396  BBoxHit right= nodes[node.internal_right()].bounds.intersect(ray, invd, hit.t);
397 
398  // visiter les 2 fils
399  if(left && right)
400  {
401  if(left.tmin < right.tmin) // gauche puis droit
402  {
403  intersect_fast(node.internal_left(), ray, invd, hit);
404  intersect_fast(node.internal_right(), ray, invd, hit);
405  }
406  else // d'abord le droit
407  {
408  intersect_fast(node.internal_right(), ray, invd, hit);
409  intersect_fast(node.internal_left(), ray, invd, hit);
410  }
411  }
412 
413  // visiter un seul fils
414  else if(left)
415  intersect_fast(node.internal_left(), ray, invd, hit);
416  else if(right)
417  intersect_fast(node.internal_right(), ray, invd, hit);
418  }
419  }
420 
421  bool occluded( const int index, const Ray& ray, const Vector& invd, const float htmax ) const
422  {
423  const Node& node= nodes[index];
424  if(node.bounds.intersect(ray, invd, htmax))
425  {
426  if(node.leaf())
427  {
428  for(int i= node.leaf_begin(); i < node.leaf_end(); i++)
429  if(primitives[i].intersect(ray, htmax))
430  return true;
431  }
432  else // if(node.internal())
433  {
434  if(occluded(node.internal_left(), ray, invd, htmax) || occluded(node.internal_right(), ray, invd, htmax))
435  return true;
436  }
437  }
438 
439  return false;
440  }
441 };
442 
443 
444 // triangle pour le bvh, cf fonction bounds() et intersect()
445 struct Triangle
446 {
447  Point p; // sommet a du triangle
448  Vector e1, e2; // aretes ab, ac du triangle
449  int node_id;
450  int mesh_id;
451  int primitive_id;
452  int triangle_id;
453 
454  Triangle( const vec3& a, const vec3& b, const vec3& c, const int _node_id, const int _mesh_id, const int _primitive_id, const int _id ) :
455  p(a), e1(Vector(a, b)), e2(Vector(a, c)),
456  node_id(_node_id), mesh_id(_mesh_id), primitive_id(_primitive_id), triangle_id(_id) {}
457 
458  /* calcule l'intersection ray/triangle
459  cf "fast, minimum storage ray-triangle intersection"
460 
461  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.)
462  renvoie vrai + les coordonnees barycentriques (u, v) du point d'intersection + sa position le long du rayon (t).
463  convention barycentrique : p(u, v)= (1 - u - v) * a + u * b + v * c
464  */
465  Hit intersect( const Ray &ray, const float htmax ) const
466  {
467  Vector pvec= cross(ray.d, e2);
468  float det= dot(e1, pvec);
469 
470  float inv_det= 1 / det;
471  Vector tvec(p, ray.o);
472 
473  float u= dot(tvec, pvec) * inv_det;
474  if(u < 0 || u > 1) return Hit();
475 
476  Vector qvec= cross(tvec, e1);
477  float v= dot(ray.d, qvec) * inv_det;
478  if(v < 0 || u + v > 1) return Hit();
479 
480  float t= dot(e2, qvec) * inv_det;
481  if(t < 0 || t > htmax) return Hit();
482 
483  return Hit(t, u, v, node_id, mesh_id, primitive_id, triangle_id);
484  }
485 
486  BBox bounds( ) const
487  {
488  BBox box(p);
489  return box.insert(p+e1).insert(p+e2);
490  }
491 };
492 
493 typedef BVHT<Triangle> BVH;
494 
495 
496 // renvoie la positions sur le rayon
497 Point hit_position( const Hit& hit, const Ray& ray )
498 {
499  assert(hit.triangle_id != -1);
500  return ray.o + hit.t * ray.d;
501 }
502 
503 
504 //
505 static GLTFMaterial default_material= GLTFMaterial();
506 
507 // renvoie la matiere du point d'intersection
508 const GLTFMaterial& hit_material( const Hit& hit, const GLTFScene& scene )
509 {
510  assert(hit.mesh_id != -1);
511  assert(hit.primitive_id != -1);
512 
513  const GLTFMesh& mesh= scene.meshes[hit.mesh_id];
514  const GLTFPrimitives& primitives= mesh.primitives[hit.primitive_id];
515 
516  if(primitives.material_index == -1)
517  return default_material;
518 
519  assert(primitives.material_index < int(scene.materials.size()));
520  return scene.materials[primitives.material_index];
521 }
522 
523 Color material_diffuse( const GLTFMaterial& material )
524 {
525  return (1 - material.metallic) * material.color / float(M_PI);
526 }
527 
528 
529 // renvoie la normale interpolee au point d'intersection dans le repere de la scene
530 Vector hit_normal( const Hit& hit, const GLTFScene& scene )
531 {
532  assert(hit.node_id != -1);
533  assert(hit.mesh_id != -1);
534  assert(hit.primitive_id != -1);
535  assert(hit.triangle_id != -1);
536 
537  const GLTFMesh& mesh= scene.meshes[hit.mesh_id];
538  const GLTFPrimitives& primitives= mesh.primitives[hit.primitive_id];
539 
540  // indice des sommets
541  int a= primitives.indices[hit.triangle_id];
542  int b= primitives.indices[hit.triangle_id+1];
543  int c= primitives.indices[hit.triangle_id+2];
544 
545  // normales des sommets
546  assert(primitives.normals.size());
547  Vector na= primitives.normals[a];
548  Vector nb= primitives.normals[b];
549  Vector nc= primitives.normals[c];
550 
551  // interpole la normale au point d'intersection
552  // attention : il faut utiliser la meme convetion barycentrique que la fonction d'intersection rayon/triangle !!
553  Vector n= (1 - hit.u - hit.v) * na + hit.u * nb + hit.v * nc;
554 
555  // transforme la normale dans le repere de la scene
556  const GLTFNode& node= scene.nodes[hit.node_id];
557  // les normales ne se transforment pas exactement comme les positions...
558  // les sommets sont transformes par node.model, comment transformer la normale pour quelle reste perpendiculaire au triangle
559  Transform T= node.model.normal();
560 
561  return normalize( T(n) );
562 }
563 
564 
565 // construit un repere tbn, a partir d'un seul vecteur.
566 // cf "generating a consistently oriented tangent space"
567 // http://people.compute.dtu.dk/jerf/papers/abstracts/onb.html
568 // et
569 // cf "Building an Orthonormal Basis, Revisited", Pixar, 2017
570 // http://jcgt.org/published/0006/01/01/
571 // pour corriger un probleme numerique...
572 struct World
573 {
574  World( ) : t(), b(), n() {}
575  World( const Vector& _n ) : n(_n)
576  {
577  float sign= std::copysign(1.0f, n.z);
578  float a= -1.0f / (sign + n.z);
579  float d= n.x * n.y * a;
580  t= Vector(1.0f + sign * n.x * n.x * a, sign * d, -sign * n.x);
581  b= Vector(d, sign + n.y * n.y * a, -n.y);
582  }
583 
584  // transforme le vecteur du repere local vers le repere du monde
585  Vector operator( ) ( const Vector& local ) const { return local.x * t + local.y * b + local.z * n; }
586 
587  // transforme le vecteur du repere du monde vers le repere local
588  Vector local( const Vector& global ) const { return Vector(dot(global, t), dot(global, b), dot(global, n)); }
589 
590  Vector t;
591  Vector b;
592  Vector n;
593 };
594 
595 
596 struct Sampler
597 {
598  std::uniform_real_distribution<float> u01;
599  std::default_random_engine rng;
600 
601  Sampler( const unsigned _seed ) : u01(), rng(_seed) {}
602  void seed( const unsigned _seed ) { rng.seed(_seed); }
603 
604  float sample( ) { return u01(rng); } // renvoie un reel [0, 1)
605  unsigned sample_binary( ) { return rng(); } // renvoie un entier 32bits
606  int sample_range( const int n ) { return int(sample() * n); } // renvoie un entier [0 n)
607 };
608 
609 
610 struct Source
611 {
612  Point a, b, c;
613  Vector n;
614  Color emission;
615  float area;
616 
617  Source( const Point& _a, const Point& _b, const Point& _c, const Color& _emission ) : a(_a), b(_b), c(_c), n(), emission(_emission), area()
618  {
619  Vector ng= cross( Vector(a, b), Vector(a, c) );
620  area= length(ng) / 2;
621  n= normalize(ng);
622  assert(area > 0);
623  }
624 
625  Point sample( const float u1, const float u2 ) const
626  {
627  #if 0
628  // cf GI compendium eq 18, https://people.cs.kuleuven.be/~philip.dutre/GI/TotalCompendium.pdf
629  // ou PBRT http://www.pbr-book.org/3ed-2018/Monte_Carlo_Integration/2D_Sampling_with_Multidimensional_Transformations.html#SamplingaTriangle
630  float r1= std::sqrt(u1);
631  float alpha= 1 - r1;
632  float beta= (1 - u2) * r1;
633  float gamma= u2 * r1;
634 
635  return alpha*a + beta*b + gamma*c;
636 
637  #else
638  // cf A Low-Distortion Map Between Triangle and Square
639  // https://hal.archives-ouvertes.fr/hal-02073696v2/document
640  float t1= u1 / 2;
641  float t2= u2 / 2;
642  float offset= t2 - t1;
643  if(offset > 0)
644  t2+= offset;
645  else
646  t1-= offset;
647 
648  return t1*a + t2*b + (1 - t1 - t2)*c;
649  #endif
650  }
651 
652  float pdf( const Point& p ) const
653  {
654  return 1 / area;
655  }
656 };
657 
658 
659 float blinn( const float roughness )
660 {
661  if(roughness < 0.001)
662  return 1000;
663 
664  return 2 / (roughness*roughness*roughness*roughness) - 2;
665 }
666 
667 // matiere : melange diffuse + reflet
668 // modele blinn-phong normalise, cf http://perso.univ-lyon1.fr/jean-claude.iehl/Public/educ/M1IMAGE/html/group__brdf.html
669 struct Brdf
670 {
671  World world;
672  Color diffuse;
673  float kd;
674  float ns;
675 
676  Brdf( ) : world(), diffuse(), kd(), ns() {}
677  Brdf( const Vector& ng, const Color& color, const float plastic, const float n ) : world(ng), diffuse(color), kd(1 - plastic), ns(n) {}
678 
679  Color f( const Vector& l, const Vector& o ) const
680  {
681  float cos_theta= dot(world.n, l);
682  if(cos_theta <= 0)
683  return Black();
684 
685  Vector h= normalize(o + l);
686  float cos_theta_h= dot(world.n, h);
687  if(cos_theta_h <= 0)
688  return Black();
689 
690  // evalue la brdf : mixture d'un terme diffus et d'un reflet,
691  Color d= 1 / float(M_PI) * diffuse;
692  Color s= (ns + 8) / float(8*M_PI) * std::pow(cos_theta_h, ns) * White();
693  return kd * d + (1 - kd) * s;
694  }
695 
696  Vector sample( const float u1, const float u2, const float u3, const Vector& o ) const
697  {
698  // echantillonne la mixture diffus + reflet
699  if(u1 <= kd)
700  {
701  // terme diffus
702  // genere une direction cos theta / pi, cf GI compendium, eq 35
703  float phi= float(2*M_PI) * u3;
704  float cos_theta= std::sqrt(u2);
705  float sin_theta= std::sqrt(1 - cos_theta*cos_theta);
706 
707  return world(Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta));
708  }
709  else
710  {
711  // terme reflechissant
712  // genere une direction h
713  // genere une direction cos^n theta / pi, cf GI compendium, eq 35+
714  float phi= float(2*M_PI) * u3;
715  float cos_theta= std::pow(u2, 1 / float(ns +1));
716  assert(cos_theta >= 0);
717  float sin_theta= std::sqrt(1 - cos_theta*cos_theta);
718  Vector h= world(Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta));
719  // genere une direction reflechie, l= reflect(o | h)
720  return -o + 2 * dot(h, o) * h;
721  // attention : peut etre sous le plan tangent...
722  }
723  }
724 
725  float pdf( const Vector& l, const Vector& o ) const
726  {
727  float cos_theta= dot(world.n, l);
728  if(cos_theta <= 0)
729  return 0;
730 
731  // pdf du terme diffus
732  float d= cos_theta / float(M_PI);
733 
734  // pdf du terme reflechissant
735  Vector h= normalize(o + l);
736  if(dot(world.n, h) <= 0)
737  return 0;
738  if(dot(l, h) <= 0)
739  return 0;
740 
741  float cos_theta_h= std::max(float(0), dot(world.n, h));
742  float s= (ns + 1) / float(2*M_PI) * std::pow(cos_theta_h, ns) / (4 * dot(l, h));
743  // le terme 1 / dot(l, h) est introduit par le changement de variable : direction h vers direction reflechie
744 
745  // pdf de la mixture
746  return kd*d + (1 - kd)*s;
747  }
748 };
749 
750 struct MCColor
751 {
752  Color color;
753  Color M;
754  Color M2;
755  int n;
756 };
757 
758 struct F
759 {
760  const GLTFScene& scene;
761  const BVH& bvh;
762  const std::vector<Source>& sources;
763 
764  Image La;
765  Image Le;
766  Image Li;
767  Image Fr;
768  Image M2;
769  Color Lo;
770  int width;
771  int height;
772  int samples;
773  float scale;
774 
775  F( const int w, const int h, const float _scale, const int _samples, const GLTFScene& _scene, const BVH& _bvh, const std::vector<Source>& _sources ) :
776  scene(_scene), bvh(_bvh), sources(_sources),
777  La(w, h), Le(w, h), Li(w, h), Fr(w, h), M2(w, h), Lo(), width(w), height(h), samples(_samples), scale(_scale) {}
778 
779  MCColor eval( const Point& o, const Point& p, const Vector& pn, const GLTFMaterial& pmaterial, Sampler& rng, const int samples )
780  {
781  if(sources.size() == 0)
782  return { White(), White(), Black(), 0 };
783 
784  Brdf pbrdf(pn, pmaterial.color, 1 - pmaterial.color.max(), blinn(pmaterial.roughness));
785 
786  Color M;
787  Color M2;
788  int n= 0;
789 
790  Color color;
791  for(int i= 0; i < samples; i++)
792  {
793  int s= rng.sample_range(sources.size());
794  Point q= sources[s].sample(rng.sample(), rng.sample());
795  Vector qn= sources[s].n;
796 
797  Vector l= normalize( Vector(p, q) );
798  Color fr= pbrdf.f(l, normalize(o - p));
799  float cos_theta= std::max(float(0), dot(pbrdf.world.n, l));
800  float G= std::max(float(0), dot(qn, -l)) / distance2(p, q);
801  float V= 1;
802  if(bvh.occluded(p + float(.001)*pbrdf.world.n, q + float(.001)*qn))
803  V= 0;
804 
805  float pdf= sources[s].pdf(q) * 1 / float(sources.size());
806  Color sample= sources[s].emission * fr * G * V * cos_theta / pdf;
807 
808  color= color + sample;
809 
810  // accumule le sample,
811  // cf https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
812  n++;
813  Color Mn1= M;
814  M= Mn1 + (sample - Mn1) / float(n);
815  M2= M2 + (sample - Mn1) * (sample - M);
816  }
817 
818  return { color / float(samples), M, M2 / float(n), n };
819  }
820 
821  void sample( const Point& o, const Point& p, const Vector& pn, const GLTFMaterial& pmaterial )
822  {
823  std::random_device hwseed;
824 
825  Brdf pbrdf(pn, pmaterial.color, 1 - pmaterial.color.max(), blinn(pmaterial.roughness));
826 
827  int n= 0;
828  #pragma omp parallel for
829  for(int y= 0; y < height; y++)
830  {
831  Sampler rng( hwseed() );
832 
833  for(int x= 0; x < width; x++)
834  {
835  Vector d;
836  {
837  float u= (x + float(0.5)) / float(width) * 2 -1;
838  float v= (y + float(0.5)) / float(height) * 2 -1;
839 
840  float w= 1 - u*u - v*v;
841  if(w <= 0)
842  continue;
843 
844  w= std::sqrt(w);
845  d= pbrdf.world(Vector(u, v, w));
846  }
847  n++;
848 
849  //
850  La(x, y)= White();
851 
852  //
853  Color fr= pbrdf.f(d, normalize(o - p));
854  float cos_theta= std::max(float(0), dot(pn, d));
855  Fr(x, y)= scale * fr * cos_theta;
856 
857  //
858  Ray ray(p + float(.001) * pn, d);
859  if(Hit hit= bvh.intersect(ray))
860  {
861  const GLTFMaterial& material= hit_material(hit, scene);
862 
863  La(x, y)= scale * material.color * cos_theta;
864  Le(x, y)= scale * material.emission + material.color * cos_theta;
865 
866  MCColor mc= eval(p, hit_position(hit, ray), hit_normal(hit, scene), material, rng, samples);
867  Li(x, y)= scale * material.emission + scale * mc.color;
868 
869  M2(x, y)= mc.M2;
870  //~ M2(x, y)= scale * mc.M;
871 
872  //~ Li(x, y)= scale * fr * material.color * cos_theta;
873 
874  Lo= Lo + material.emission * fr * cos_theta;
875  }
876  }
877  }
878  Lo= Lo / float(n);
879  }
880 };
881 
882 void plot( Image& image, const float px, const float py, const Color& color )
883 {
884  int x= std::floor(px * image.width());
885  int y= std::floor(py * image.height());
886 
887  image(x, y)= color;
888  image(x-1, y)= color;
889  image(x+1, y)= color;
890  image(x, y-1)= color;
891  image(x, y+1)= color;
892 }
893 
894 void plot_brdf( Mesh& plot, const Point& o, const Point& p, const Vector& n, const GLTFMaterial& material )
895 {
896  Brdf pbrdf(n, material.color, 1 - material.color.max(), blinn(material.roughness));
897 
898  plot.clear();
899  for(int ti= 0; ti <= 10; ti++)
900  for(int pi= 0; pi <= 10; pi++)
901  {
902  Vector d= pbrdf.sample(1, float(ti + .5f) / 10, float(pi + .5f) / 10, normalize(o - p));
903  Color fr= pbrdf.f(d, normalize(o - p));
904  if(pbrdf.pdf(d, normalize(o - p)) > 0)
905  {
906  //~ assert(pbrdf.pdf(d, normalize(o - p)) > 0);
907 
908  plot.vertex(Origin());
909  plot.vertex(d * fr.max());
910  }
911  }
912 }
913 
914 
915 Image render_sun( const Orbiter& camera, const GLTFScene& scene, const BVH& bvh, const Vector& sun, const Color& emission )
916 {
917  Image image(camera.width(), camera.height());
918 
919  printf("render sun %dx%d...\n", image.width(), image.height());
920 
921  Transform model= Identity();
922  Transform view= camera.view();
923  Transform projection= camera.projection();
924  Transform viewport= camera.viewport();
925  Transform inv= Inverse(viewport *projection * view); // image vers scene
926 
927  std::random_device hwseed;
928  for(int py= 0; py < image.height(); py++)
929  {
930  Sampler rng( hwseed() );
931 
932  for(int px= 0; px < image.width(); px++)
933  {
934  Color color;
935  for(int pa= 0; pa < 64; pa++)
936  {
937  float ax= rng.sample();
938  float ay= rng.sample();
939  Point o= inv( Point(px +ax, py +ay, 0) );
940  Point e= inv( Point(px +ax, py +ay, 1) );
941  Vector d= Vector(o, e);
942 
943  Ray ray(o, d);
944  if(Hit hit= bvh.intersect(ray))
945  {
946  Point p= hit_position(hit, ray);
947  Vector pn= hit_normal(hit, scene);
948  Color diffuse= material_diffuse( hit_material(hit, scene) );
949 
950  if(dot(ray.d, pn) > 0) // retourne la normale si elle n'est pas orientee vers l'origine du rayon...
951  pn= -pn;
952 
953  const float epsilon= 0.0001;
954  float V= bvh.visible( Ray(p+epsilon*pn, sun) ) ? 1 : 0;
955  float cos_theta= std::max( float(0), dot( normalize(pn), normalize(sun) ) );
956  color= color + diffuse * V * emission * cos_theta;
957  }
958  }
959  color= color / 64;
960 
961  image(px, py)= Color(color, 1);
962  }
963  }
964 
965  return image;
966 }
967 
968 Image render_sky( const Orbiter& camera, const GLTFScene& scene, const BVH& bvh, const Vector& sun, const Color& emission )
969 {
970  Image image(camera.width(), camera.height());
971 
972  printf("render sky %dx%d...\n", image.width(), image.height());
973 
974  Transform model= Identity();
975  Transform view= camera.view();
976  Transform projection= camera.projection();
977  Transform viewport= camera.viewport();
978  Transform inv= Inverse(viewport *projection * view); // image vers scene
979 
980  std::random_device hwseed;
981  for(int py= 0; py < image.height(); py++)
982  {
983  Sampler rng( hwseed() );
984 
985  for(int px= 0; px < image.width(); px++)
986  {
987  Color color;
988  for(int pa= 0; pa < 64; pa++)
989  {
990  float ax= rng.sample();
991  float ay= rng.sample();
992  Point o= inv( Point(px +ax, py +ay, 0) );
993  Point e= inv( Point(px +ax, py +ay, 1) );
994  Vector d= Vector(o, e);
995 
996  Ray ray(o, d);
997  if(Hit hit= bvh.intersect(ray))
998  {
999  Point p= hit_position(hit, ray);
1000  Vector pn= hit_normal(hit, scene);
1001  Color diffuse= material_diffuse( hit_material(hit, scene) );
1002 
1003  if(dot(ray.d, pn) > 0) // retourne la normale si elle n'est pas orientee vers l'origine du rayon...
1004  pn= -pn;
1005 
1006  float cos_theta= std::max( float(0), dot( normalize(pn), normalize(sun) ) );
1007  color= color + diffuse * emission * (cos_theta+1)/2;
1008  }
1009  }
1010  color= color / 64;
1011 
1012  image(px, py)= Color(color, 1);
1013  }
1014  }
1015 
1016  return image;
1017 }
1018 
1019 
1020 //
1021 float fract( const float v ) { return v - std::floor(v); }
1022 
1023 // spirale fibonacci : renvoie la ieme direction parmi n
1024 Vector fibonacci( const int i, const int N, const float rotation= 0 )
1025 {
1026  const float ratio= (std::sqrt(5) + 1) / 2;
1027 
1028  float phi= float(2 * M_PI) * fract((i + rotation) / ratio);
1029  float cos_theta= 1 - float(2*i +1) / float(N * 2);
1030  float sin_theta= std::sqrt(1 - cos_theta*cos_theta);
1031 
1032  return Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta);
1033  //~ return Vector(std::cos(phi) * sin_theta, cos_theta, std::sin(phi) * sin_theta);
1034 }
1035 
1036 Image render_fibonacci( const Orbiter& camera, const GLTFScene& scene, const BVH& bvh, const int samples, const Color& emission )
1037 {
1038  Image image(camera.width(), camera.height());
1039 
1040  const int AA= 1;
1041  const int N= samples;
1042 
1043  printf("render fibonacci %dx%d %d samples...\n", image.width(), image.height(), samples);
1044 
1045  Transform model= Identity();
1046  Transform view= camera.view();
1047  Transform projection= camera.projection();
1048  Transform viewport= camera.viewport();
1049  Transform inv= Inverse(viewport *projection * view); // image vers scene
1050 
1051  #pragma omp parallel for schedule(dynamic, 1)
1052  for(int py= 0; py < image.height(); py++)
1053  {
1054  std::random_device hwseed;
1055  Sampler rng( hwseed() );
1056 
1057  for(int px= 0; px < image.width(); px++)
1058  {
1059  Color color;
1060  Color emission;
1061  for(int pa= 0; pa < AA; pa++)
1062  {
1063  //~ float ax= rng.sample();
1064  //~ float ay= rng.sample();
1065  float ax= 0;
1066  float ay= 0;
1067  float ar= rng.sample(); // rotation pour fibonacci
1068 
1069  Point o= inv( Point(px +ax, py +ay, 0) );
1070  Point e= inv( Point(px +ax, py +ay, 1) );
1071  Vector d= Vector(o, e);
1072 
1073  Ray ray(o, d);
1074  if(Hit hit= bvh.intersect(ray))
1075  {
1076  Point p= hit_position(hit, ray);
1077  Vector pn= hit_normal(hit, scene);
1078  Color diffuse= material_diffuse( hit_material(hit, scene) );
1079 
1080  if(dot(ray.d, pn) > 0) // retourne la normale si elle n'est pas orientee vers l'origine du rayon...
1081  pn= -pn;
1082 
1083  emission= emission + hit_material(hit, scene).emission;
1084 
1085  World world(pn);
1086  for(int i= 0; i < N; i++)
1087  {
1088  Vector l= world( fibonacci(i, N, 0) );
1089  //~ Vector l= world( fibonacci(i, N, ar) );
1090 
1091  const float epsilon= 0.0001;
1092  //~ float V= bvh.visible( Ray(p+epsilon*pn, l) ) ? 1 : 0;
1093  if(Hit source= bvh.intersect( Ray(p+epsilon*pn, l) ))
1094  {
1095  Color Le= hit_material(source, scene).emission;
1096  float cos_theta= std::max( float(0), dot( normalize(pn), normalize(l) ) );
1097 
1098  Vector sn= hit_normal(source, scene);
1099  float cos_theta_s= std::max( float(0), - dot( normalize(sn), normalize(l) ) );
1100  if(cos_theta_s > 0)
1101  color= color + diffuse * Le * cos_theta;
1102  }
1103  }
1104  }
1105  }
1106 
1107  emission= emission / float(AA);
1108 
1109  color= color / float(N);
1110  color= color / float(AA);
1111  color= color * 2; //
1112 
1113  image(px, py)= Color(color + emission, 1);
1114  }
1115  }
1116 
1117  return image;
1118 }
1119 
1120 
1121 // disco ball !!
1122 Image render_disco( const Orbiter& camera, const GLTFScene& scene, const BVH& bvh, const float rotation, const int samples, const Color& emission )
1123 {
1124  Image image(camera.width(), camera.height());
1125 
1126  const int AA= 1;
1127  const int N= samples;
1128 
1129  printf("render disco %dx%d %d samples...\n", image.width(), image.height(), samples);
1130 
1131  Transform model= Identity();
1132  Transform view= camera.view();
1133  Transform projection= camera.projection();
1134  Transform viewport= camera.viewport();
1135  Transform inv= Inverse(viewport *projection * view); // image vers scene
1136 
1137  #pragma omp parallel for schedule(dynamic, 1)
1138  for(int py= 0; py < image.height(); py++)
1139  {
1140  std::random_device hwseed;
1141  Sampler rng( hwseed() );
1142 
1143  for(int px= 0; px < image.width(); px++)
1144  {
1145  Color color;
1146  Color emission;
1147  for(int pa= 0; pa < AA; pa++)
1148  {
1149  //~ float ax= rng.sample();
1150  //~ float ay= rng.sample();
1151  float ax= 0;
1152  float ay= 0;
1153  //~ float ar= rng.sample(); // rotation pour fibonacci
1154 
1155  Point o= inv( Point(px +ax, py +ay, 0) );
1156  Point e= inv( Point(px +ax, py +ay, 1) );
1157  Vector d= Vector(o, e);
1158 
1159  Ray ray(o, d);
1160  if(Hit hit= bvh.intersect(ray))
1161  {
1162  Point p= hit_position(hit, ray);
1163  Vector pn= hit_normal(hit, scene);
1164  Color diffuse= material_diffuse( hit_material(hit, scene) );
1165 
1166  if(dot(ray.d, pn) > 0) // retourne la normale si elle n'est pas orientee vers l'origine du rayon...
1167  pn= -pn;
1168 
1169  emission= emission + hit_material(hit, scene).emission;
1170 
1171  World world(pn);
1172  for(int i= 0; i < N; i++)
1173  {
1174  //~ Vector l= world( fibonacci(i, N, 0) );
1175  Vector l= world( fibonacci(i, N, rotation) );
1176 
1177  const float epsilon= 0.0001;
1178  if(Hit source= bvh.intersect( Ray(p+epsilon*pn, l) ))
1179  {
1180  Color Le= hit_material(source, scene).emission;
1181  float cos_theta= std::max( float(0), dot( normalize(pn), normalize(l) ) );
1182 
1183  Vector sn= hit_normal(source, scene);
1184  float cos_theta_s= std::max( float(0), - dot( normalize(sn), normalize(l) ) );
1185  if(cos_theta_s > 0)
1186  color= color + diffuse * Le * cos_theta;
1187  }
1188  }
1189  }
1190  }
1191 
1192  emission= emission / float(AA);
1193 
1194  color= color / float(N);
1195  color= color / float(AA);
1196  color= color * 2; //
1197 
1198  image(px, py)= Color(color + emission, 1);
1199  }
1200  }
1201 
1202  return image;
1203 }
1204 
1205 
1206 Image render_uniform( const Orbiter& camera, const GLTFScene& scene, const BVH& bvh, const int samples, const Color& emission )
1207 {
1208  Image image(camera.width(), camera.height());
1209 
1210  const int AA= 1;
1211  const int N= samples;
1212 
1213  printf("render uniform %dx%d %d samples...\n", image.width(), image.height(), samples);
1214 
1215  Transform model= Identity();
1216  Transform view= camera.view();
1217  Transform projection= camera.projection();
1218  Transform viewport= camera.viewport();
1219  Transform inv= Inverse(viewport *projection * view); // image vers scene
1220 
1221  #pragma omp parallel for schedule(dynamic, 1)
1222  for(int py= 0; py < image.height(); py++)
1223  {
1224  std::random_device hwseed;
1225  Sampler rng( hwseed() );
1226 
1227  for(int px= 0; px < image.width(); px++)
1228  {
1229  Color color;
1230  Color emission;
1231  for(int pa= 0; pa < AA; pa++)
1232  {
1233  //~ float ax= rng.sample();
1234  //~ float ay= rng.sample();
1235  float ax= 0;
1236  float ay= 0;
1237 
1238  Point o= inv( Point(px +ax, py +ay, 0) );
1239  Point e= inv( Point(px +ax, py +ay, 1) );
1240  Vector d= Vector(o, e);
1241 
1242  Ray ray(o, d);
1243  if(Hit hit= bvh.intersect(ray))
1244  {
1245  Point p= hit_position(hit, ray);
1246  Vector pn= hit_normal(hit, scene);
1247  Color diffuse= material_diffuse( hit_material(hit, scene) );
1248 
1249  if(dot(ray.d, pn) > 0) // retourne la normale si elle n'est pas orientee vers l'origine du rayon...
1250  pn= -pn;
1251 
1252  emission= emission + hit_material(hit, scene).emission;
1253 
1254  World world(pn);
1255  for(int i= 0; i < N; i++)
1256  {
1257  Vector l;
1258  float pdf= 0;
1259  {
1260  float cos_theta= rng.sample();
1261  float sin_theta= std::sqrt(1 - cos_theta * cos_theta);
1262  float phi= float(2*M_PI) * rng.sample();
1263 
1264  l= world( Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta) );
1265  pdf= 1 / (2*M_PI);
1266  }
1267 
1268  const float epsilon= 0.0001;
1269  if(Hit source= bvh.intersect( Ray(p+epsilon*pn, l) ))
1270  {
1271  Color Le= hit_material(source, scene).emission;
1272  float cos_theta= std::max( float(0), dot( normalize(pn), normalize(l) ) );
1273 
1274  Vector sn= hit_normal(source, scene);
1275  float cos_theta_s= std::max( float(0), - dot( normalize(sn), normalize(l) ) );
1276  if(cos_theta_s > 0)
1277  color= color + diffuse * Le * cos_theta / pdf;
1278  }
1279  }
1280  }
1281  }
1282 
1283  emission= emission / float(AA);
1284 
1285  color= color / float(N);
1286  color= color / float(AA);
1287 
1288  image(px, py)= Color(color + emission, 1);
1289  }
1290  }
1291 
1292  return image;
1293 }
1294 
1295 Image render_sources( const Orbiter& camera, const GLTFScene& scene, const BVH& bvh, const int samples, const std::vector<Source>& sources )
1296 {
1297  Image image(camera.width(), camera.height());
1298 
1299  const int AA= 1;
1300  const int N= samples;
1301 
1302  printf("render uniform %dx%d %d samples...\n", image.width(), image.height(), samples);
1303 
1304  // normalisation pdf source
1305  float total= 0;
1306  for(unsigned i= 0; i < sources.size(); i++)
1307  total+= sources[i].area;
1308 
1309  Transform model= Identity();
1310  Transform view= camera.view();
1311  Transform projection= camera.projection();
1312  Transform viewport= camera.viewport();
1313  Transform inv= Inverse(viewport *projection * view); // image vers scene
1314 
1315  #pragma omp parallel for schedule(dynamic, 1)
1316  for(int py= 0; py < image.height(); py++)
1317  {
1318  std::random_device hwseed;
1319  Sampler rng( hwseed() );
1320 
1321  for(int px= 0; px < image.width(); px++)
1322  {
1323  Color color;
1324  Color emission;
1325  for(int pa= 0; pa < AA; pa++)
1326  {
1327  //~ float ax= rng.sample();
1328  //~ float ay= rng.sample();
1329  float ax= 0;
1330  float ay= 0;
1331 
1332  Point o= inv( Point(px +ax, py +ay, 0) );
1333  Point e= inv( Point(px +ax, py +ay, 1) );
1334  Vector d= Vector(o, e);
1335 
1336  Ray ray(o, d);
1337  if(Hit hit= bvh.intersect(ray))
1338  {
1339  Point p= hit_position(hit, ray);
1340  Vector pn= hit_normal(hit, scene);
1341  Color diffuse= material_diffuse( hit_material(hit, scene) );
1342 
1343  if(dot(ray.d, pn) > 0) // retourne la normale si elle n'est pas orientee vers l'origine du rayon...
1344  pn= -pn;
1345 
1346  emission= emission + hit_material(hit, scene).emission;
1347 
1348  World world(pn);
1349  for(int i= 0; i < N; i++)
1350  {
1351  Point q;
1352  float pdf= 0;
1353  {
1354  //~ q= sources[i%int(sources.size())].sample(rng.sample(), rng.sample());
1355  //~ pdf= sources[i%int(sources.size())].pdf(q);
1356 
1357  int s= rng.sample_range(sources.size());
1358  q= sources[s].sample(rng.sample(), rng.sample());
1359  pdf= float(1)/float(sources.size()) * sources[s].pdf(q);
1360 
1361  //~ // fonction de repartition
1362  //~ float cdf= 0;
1363  //~ float u= rng.sample();
1364  //~ for(unsigned i= 0; i < sources.size(); i++)
1365  //~ {
1366  //~ cdf+= sources[i].area / total;
1367  //~ if(u < cdf)
1368  //~ {
1369  //~ q= sources[i].sample(rng.sample(), rng.sample());
1370  //~ pdf= 1 / total;
1371  //~ break;
1372  //~ }
1373  //~ }
1374  }
1375  Vector l= Vector(p, q);
1376 
1377  const float epsilon= 0.0001;
1378  if(Hit source= bvh.intersect( Ray(p+epsilon*pn, l) ))
1379  {
1380  Color Le= hit_material(source, scene).emission;
1381  float cos_theta= std::max( float(0), dot( normalize(pn), normalize(l) ) );
1382 
1383  Vector sn= hit_normal(source, scene);
1384  float cos_theta_s= std::max( float(0), - dot( normalize(sn), normalize(l) ) );
1385  color= color + diffuse * Le * cos_theta * cos_theta_s / length2(l) / pdf;
1386  }
1387  }
1388  }
1389  }
1390 
1391  emission= emission / float(AA);
1392 
1393  color= color / float(N);
1394  color= color / float(AA);
1395 
1396  image(px, py)= Color(color + emission, 1);
1397  }
1398  }
1399 
1400  return image;
1401 }
1402 
1403 
1404 class TP : public AppCamera
1405 {
1406 public:
1407  // constructeur : donner les dimensions de l'image, et eventuellement la version d'openGL.
1408  TP( const char *filename ) : AppCamera(1024, 640, 3,3, 8)
1409  {
1410  m_scene= read_gltf_scene(filename);
1411  }
1412 
1413  // creation des objets de l'application
1414  int init( )
1415  {
1416  if(m_scene.meshes.size() == 0)
1417  return -1;
1418 
1419  std::vector<Triangle> triangles;
1420  std::vector<Color> colors;
1421  {
1422  for(unsigned node_id= 0; node_id < m_scene.nodes.size(); node_id++)
1423  {
1424  const GLTFNode& node= m_scene.nodes[node_id];
1425 
1426  const Transform& model= node.model;
1427  int mesh_id= node.mesh_index;
1428 
1429  const GLTFMesh& mesh= m_scene.meshes[mesh_id];
1430  for(unsigned primitive_id= 0; primitive_id < mesh.primitives.size(); primitive_id++)
1431  {
1432  const GLTFPrimitives& primitives= mesh.primitives[primitive_id];
1433 
1434  Color color= Color(0.8);
1435  int material_id= primitives.material_index;
1436  if(material_id != -1)
1437  {
1438  if(m_scene.materials[material_id].emission.max() > 0)
1439  color= Color(2); // blanc !! tres blanc...
1440  else
1441  color= m_scene.materials[material_id].color;
1442  }
1443 
1444  for(unsigned i= 0; i +2 < primitives.indices.size(); i+= 3)
1445  {
1446  // transforme les sommets dans le repere de la scene
1447  Point a= model( Point(primitives.positions[primitives.indices[i]]) );
1448  Point b= model( Point(primitives.positions[primitives.indices[i+1]]) );
1449  Point c= model( Point(primitives.positions[primitives.indices[i+2]]) );
1450 
1451  // verifie que le triangle n'est pas degenere... oui ca arrive... sur robot.gltf, par exemple...
1452  float area= length( cross( Vector(a, b), Vector(a, c) ) );
1453  if(area > 0)
1454  {
1455  colors.push_back(color);
1456  triangles.push_back( Triangle(a, b, c, node_id, mesh_id, primitive_id, i) );
1457  // indice du premier sommet du triangle dans la primitive
1458  }
1459  }
1460  }
1461  }
1462  assert(triangles.size());
1463  }
1464 
1465  // sources
1466  m_objet_sources= Mesh(GL_LINES);
1467  m_objet_sources.color(Red());
1468 
1469  for(unsigned node_id= 0; node_id < m_scene.nodes.size(); node_id++)
1470  {
1471  const GLTFNode& node= m_scene.nodes[node_id];
1472  const GLTFMesh& mesh= m_scene.meshes[node.mesh_index];
1473  const Transform& model= node.model;
1474 
1475  for(unsigned primitive_id= 0; primitive_id < mesh.primitives.size(); primitive_id++)
1476  {
1477  const GLTFPrimitives& primitives= mesh.primitives[primitive_id];
1478  const GLTFMaterial& material= m_scene.materials[primitives.material_index];
1479 
1480  // verifie que la matiere emet de la lumiere...
1481  if(material.emission.r + material.emission.g +material.emission.g > 0)
1482  {
1483  for(unsigned i= 0; i +2 < primitives.indices.size(); i+= 3)
1484  {
1485  // transforme les positions des sommets du triangle
1486  Point a= model( Point(primitives.positions[primitives.indices[i]]) );
1487  Point b= model( Point(primitives.positions[primitives.indices[i+1]]) );
1488  Point c= model( Point(primitives.positions[primitives.indices[i+2]]) );
1489 
1490  // verifie que le triangle n'est pas degenere... oui ca arrive... sur robot.gltf, par exemple...
1491  float area= length( cross( Vector(a, b), Vector(a, c) ) );
1492  if(area > 0)
1493  {
1494  m_sources.push_back( Source(a, b, c, material.emission) );
1495 
1496  m_objet_sources.vertex(a); m_objet_sources.vertex(b);
1497  m_objet_sources.vertex(b); m_objet_sources.vertex(c);
1498  m_objet_sources.vertex(c); m_objet_sources.vertex(a);
1499  }
1500  }
1501  }
1502  }
1503  }
1504 
1505  //
1506  m_bvh.build(triangles);
1507 
1508  // beurk, construit un mesh a partir des triangles de la scene gltf...
1509  m_objet= Mesh(GL_TRIANGLES);
1510  {
1511  for(unsigned i= 0; i < triangles.size(); i++)
1512  {
1513  const Triangle& triangle= triangles[i];
1514 
1515  m_objet.color(colors[i]);
1516  m_objet.vertex(triangle.p);
1517  m_objet.vertex(triangle.p+triangle.e1);
1518  m_objet.vertex(triangle.p+triangle.e2);
1519  }
1520  }
1521 
1522  // ajuste la camera
1523  Point pmin, pmax;
1524  m_objet.bounds(pmin, pmax);
1525 
1526  // decrire un repere / grille
1527  m_repere= make_grid(10);
1528  m_hit= make_grid(0);
1529 
1530  Point gmin, gmax;
1531  m_repere.bounds(gmin, gmax);
1532 
1533  pmin= min(pmin, gmin);
1534  pmax= max(pmax, gmax);
1535  camera().lookat(pmin, pmax);
1536  printf("znear %f, zfar %f\n", camera().znear(), camera().zfar());
1537 
1538  m_directions.create(GL_LINES);
1539  m_directions.default_color(Yellow());
1540  //~ plot_brdf(m_directions, default_material);
1541 
1542  m_camera= make_camera();
1543  m_image_plane= make_image_plane(0.9);
1544  m_frustum= make_frustum();
1545 
1546  m_ray.create(GL_LINES);
1547  m_ray.vertex(0, 0, 0);
1548  m_ray.vertex(0, 0, -1);
1549 
1550  m_scale= 10;
1551  m_La_texture= make_vec4_texture(0, 256, 256);
1552  m_Le_texture= make_vec4_texture(0, 256, 256);
1553  m_Li_texture= make_vec4_texture(0, 256, 256);
1554  m_Fr_texture= make_vec4_texture(0, 256, 256);
1555  m_zoom_texture= make_vec4_texture(0, 512, 512);
1556 
1557  glGenFramebuffers(1, &m_framebuffer);
1558  glBindFramebuffer(GL_READ_FRAMEBUFFER, m_framebuffer);
1559  glFramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_La_texture, /* mipmap */ 0);
1560  glReadBuffer(GL_COLOR_ATTACHMENT0);
1561 
1562  glGenFramebuffers(1, &m_zoom_framebuffer);
1563  glBindFramebuffer(GL_READ_FRAMEBUFFER, m_zoom_framebuffer);
1564  glFramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_zoom_texture, /* mipmap */ 0);
1565  glReadBuffer(GL_COLOR_ATTACHMENT0);
1566 
1567  //
1568  m_widgets= create_widgets();
1569 
1570  // etat openGL par defaut
1571  glClearColor(0.2f, 0.2f, 0.2f, 1.f); // couleur par defaut de la fenetre
1572 
1573  glClearDepth(1.f); // profondeur par defaut
1574  glDepthFunc(GL_LESS); // ztest, conserver l'intersection la plus proche de la camera
1575  glEnable(GL_DEPTH_TEST); // activer le ztest
1576 
1577  // lignes et points msaa
1578  glLineWidth(2);
1579  glEnable(GL_LINE_SMOOTH);
1580  glPointSize(4);
1581 
1582  return 0; // pas d'erreur, sinon renvoyer -1
1583  }
1584 
1585  // destruction des objets de l'application
1586  int quit( )
1587  {
1588  release_widgets(m_widgets);
1589  m_objet.release();
1590  m_repere.release();
1591  return 0; // pas d'erreur
1592  }
1593 
1594  GLuint update( const GLuint texture, const Image& image )
1595  {
1596  glBindTexture(GL_TEXTURE_2D, texture);
1597  glTexImage2D(GL_TEXTURE_2D, 0,
1598  GL_RGBA32F, image.width(), image.height(), 0,
1599  GL_RGBA, GL_FLOAT, image.data());
1600 
1601  return texture;
1602  }
1603 
1604  void display( const GLuint texture, const int x, const int y, const int zoom= 1 )
1605  {
1606  glBindFramebuffer(GL_READ_FRAMEBUFFER, m_framebuffer);
1607  glFramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, /* mipmap */ 0);
1608 
1609  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_zoom_framebuffer);
1610  glBlitFramebuffer(0, 0, 256, 256, 0, 0, 256*zoom, 256*zoom, GL_COLOR_BUFFER_BIT, GL_NEAREST);
1611 
1612  glBindFramebuffer(GL_READ_FRAMEBUFFER, m_zoom_framebuffer);
1613 
1614  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
1615  glBlitFramebuffer(0, 0, 256*zoom, 256*zoom, x*zoom, y*zoom, x*zoom+256*zoom, y*zoom+256*zoom, GL_COLOR_BUFFER_BIT, GL_NEAREST);
1616  }
1617 
1618  // dessiner une nouvelle image
1619  int render( )
1620  {
1621  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1622 
1623  draw(m_repere, /* model */ Identity(), camera());
1624  draw(m_objet, /* model */ Identity(), camera());
1625 
1626  static int capture= 0;
1627  static int export_capture= 0;
1628  static int show_capture= 1;
1629  static int show_frustum= 1;
1630  static int show_brdf= 1;
1631  static int zoom= 0;
1632  static int mode= 0;
1633  static int samples= 64;
1634  static int sample_mode= 0;
1635  static int freeze= 0;
1636 
1637  if(key_state(' '))
1638  {
1639  clear_key_state(' ');
1640  freeze= !freeze;
1641 
1642  if(freeze)
1643  {
1644  m_camera_freeze= camera();
1645  m_camera_view= camera().view();
1646  m_camera_model= Inverse(camera().view());
1647  m_camera_projection= camera().projection();
1648  m_camera_viewport= camera().viewport();
1649 
1650  int x, y;
1651  SDL_GetMouseState(&x, &y);
1652  y= window_height() - 1 - y;
1653 
1654  m_pixelx= x;
1655  m_pixely= y;
1656  capture= 1;
1657  }
1658  }
1659 
1660  if(key_state(SDLK_LEFT)) { m_pixelx-= 4; capture= 1;}
1661  if(key_state(SDLK_RIGHT)) { m_pixelx+= 4; capture= 1; }
1662  if(key_state(SDLK_UP)) { m_pixely+= 4; capture= 1; }
1663  if(key_state(SDLK_DOWN)) { m_pixely-= 4; capture= 1; }
1664 
1665  if(key_state('z'))
1666  {
1667  clear_key_state('z');
1668  zoom= !zoom;
1669  }
1670 
1671  if(key_state('f'))
1672  {
1673  clear_key_state('f');
1674  show_frustum= !show_frustum;
1675  }
1676 
1677  if(key_state('e'))
1678  {
1679  clear_key_state('e');
1680  capture= 1;
1681  export_capture= 1;
1682  }
1683 
1684  if(capture)
1685  {
1686  // rayon primaire
1687  Transform viewport= Viewport(window_width(), window_height());
1688  Transform inv= Inverse(viewport * m_camera_projection * m_camera_view);
1689 
1690  Ray ray( inv( Point(m_pixelx, m_pixely, 0) ), inv( Point(m_pixelx, m_pixely, 1) ) );
1691  if(Hit hit= m_bvh.intersect(ray))
1692  {
1693  const GLTFMaterial& material= hit_material(hit, m_scene);
1694  Point p= hit_position(hit, ray);
1695  Vector n= hit_normal(hit, m_scene);
1696 
1697  Transform hit_inv= Inverse(Translation(p.x, p.y, p.z) * Rotation(Vector(0, 0, 1), n));
1698  plot_brdf(m_directions, hit_inv(ray.o), hit_inv(p), hit_inv(n), material);
1699 
1700  m_hit_model= Translation(p.x, p.y, p.z) * Rotation(Vector(0, 0, 1), n) * Scale(0.4);
1701  m_ray.vertex(0, ray.o);
1702  m_ray.vertex(1, p);
1703 
1704  F f(256, 256, m_scale, samples, m_scene, m_bvh, m_sources);
1705  f.sample(ray.o, p, n, material);
1706 
1707  if(sample_mode == 1) // fibonacci
1708  {
1709  std::random_device hwseed;
1710  Sampler rng( hwseed() );
1711  //~ Sampler rng( 1 );
1712  //~ float u= rng.sample();
1713  float u= 0;
1714 
1715  float gold= (std::sqrt(float(5)) +1) / 2;
1716  for(int i= 0; i < samples; i++)
1717  {
1718  float phi= float(2*M_PI) * ((i+u) / gold - std::floor((i+u) / gold));
1719  float cos_theta= 1 - float(2*i -1) / float(2*samples);
1720  float sin_theta= std::sqrt(1 - cos_theta*cos_theta);
1721 
1722  Vector d= Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta);
1723 
1724  // reprojette sur le disque
1725  float x= d.x / 2 + float(0.5);
1726  float y= d.y / 2 + float(0.5);
1727  plot(f.Le, x, y, Red());
1728  plot(f.Li, x, y, Red());
1729  plot(f.Fr, x, y, Red());
1730  }
1731  }
1732  else if(sample_mode == 2) // cos theta / pi
1733  {
1734  std::random_device hwseed;
1735  Sampler rng( hwseed() );
1736  //~ Sampler rng( 1 );
1737 
1738  for(int i= 0; i < samples; i++)
1739  {
1740  float phi= float(2*M_PI) * rng.sample();
1741  float cos_theta= std::sqrt(rng.sample());
1742  float sin_theta= std::sqrt(1 - cos_theta*cos_theta);
1743 
1744  Vector d= Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta);
1745 
1746  // reprojette sur le disque
1747  float x= d.x / 2 + float(0.5);
1748  float y= d.y / 2 + float(0.5);
1749  plot(f.Le, x, y, Red());
1750  plot(f.Li, x, y, Red());
1751  plot(f.Fr, x, y, Red());
1752  }
1753  }
1754  else if(sample_mode == 4) // sources
1755  {
1756  if(m_sources.size())
1757  {
1758  std::random_device hwseed;
1759  Sampler rng( hwseed() );
1760  //~ Sampler rng( 1 );
1761 
1762  // normalisation pdf source
1763  float total= 0;
1764  for(unsigned i= 0; i < m_sources.size(); i++)
1765  total+= m_sources[i].area;
1766 
1767  World world(n);
1768  for(int i= 0; i < samples; i++)
1769  {
1770  //~ int s= rng.sample_range(m_sources.size());
1771  //~ Point q= m_sources[s].sample(rng.sample(), rng.sample());
1772 
1773  Point q;
1774  // fonction de repartition
1775  float cdf= 0;
1776  float u= rng.sample();
1777  for(unsigned i= 0; i < m_sources.size(); i++)
1778  {
1779  cdf+= m_sources[i].area / total;
1780  if(u < cdf)
1781  {
1782  q= m_sources[i].sample(rng.sample(), rng.sample());
1783  break;
1784  }
1785  }
1786 
1787  Vector l= normalize( world.local(Vector(p, q)) );
1788  if(l.z > 0)
1789  {
1790  // reprojette sur le disque
1791  float x= l.x / 2 + float(0.5);
1792  float y= l.y / 2 + float(0.5);
1793  plot(f.Le, x, y, Red());
1794  plot(f.Fr, x, y, Red());
1795  }
1796  }
1797  }
1798  }
1799  else if(sample_mode == 3) // brdf
1800  {
1801  Brdf pbrdf(n, material.color, 1 - material.color.max(), blinn(material.roughness));
1802 
1803  std::random_device hwseed;
1804  Sampler rng( hwseed() );
1805  //~ Sampler rng( 1 );
1806 
1807  for(int i= 0; i < samples; i++)
1808  {
1809  Vector l= pbrdf.world.local( pbrdf.sample(rng.sample(), rng.sample(), rng.sample(), normalize(ray.o - p)) );
1810  if(l.z > 0)
1811  {
1812  // reprojette sur le disque
1813  float x= l.x / 2 + float(0.5);
1814  float y= l.y / 2 + float(0.5);
1815  plot(f.Fr, x, y, Red());
1816  plot(f.Li, x, y, Red());
1817  plot(f.Le, x, y, Red());
1818  }
1819  }
1820  }
1821 
1822  else // 1 / 2pi sample_mode == 0
1823  {
1824  std::random_device hwseed;
1825  Sampler rng( hwseed() );
1826  //~ Sampler rng( 1 );
1827 
1828  for(int i= 0; i < samples; i++)
1829  {
1830  float phi= float(2*M_PI) * rng.sample();
1831  float cos_theta= rng.sample();
1832  float sin_theta= std::sqrt(1 - cos_theta*cos_theta);
1833 
1834  Vector d= Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta);
1835 
1836  // reprojette sur le disque
1837  float x= d.x / 2 + float(0.5);
1838  float y= d.y / 2 + float(0.5);
1839  plot(f.Fr, x, y, Red());
1840  plot(f.Le, x, y, Red());
1841  plot(f.Li, x, y, Red());
1842  }
1843  }
1844 
1845  update(m_La_texture, f.La);
1846  update(m_Le_texture, f.Le);
1847  update(m_Li_texture, f.Li);
1848  update(m_Fr_texture, f.Fr);
1849 
1850  if(export_capture)
1851  {
1852  F f(512, 512, m_scale, samples*4, m_scene, m_bvh, m_sources);
1853  f.sample(ray.o, p, n, material);
1854 
1855  write_image_pfm(f.La, "La.pfm");
1856  write_image_pfm(f.Le, "Le.pfm");
1857  write_image_pfm(f.Li, "Li.pfm");
1858  write_image_pfm(f.M2, "M2.pfm");
1859  write_image_pfm(f.Fr, "Fr.pfm");
1860 
1861  // render
1862  if(0)
1863  {
1864  Image image= render_sun(m_camera_freeze, m_scene, m_bvh, Vector(1, 10, -2), Color(10));
1865  write_image_hdr(image, "sun.hdr");
1866  //~ write_image(image, "sun.png");
1867  }
1868  if(0)
1869  {
1870  Image image= render_sky(m_camera_freeze, m_scene, m_bvh, Vector(1, 10, -2), Color(10));
1871  write_image_hdr(image, "sky.hdr");
1872  //~ write_image(image, "sky.png");
1873  }
1874 
1875  if(0)
1876  {
1877  char tmp[1024];
1878  for(int n : { 1, 4, 16, 64, 256 })
1879  {
1880  Image image= render_fibonacci(m_camera_freeze, m_scene, m_bvh, n, Color(10));
1881 
1882  //~ sprintf(tmp, "rfibonacci-%03d.hdr", n);
1883  //~ write_image_hdr(image, tmp);
1884  //~ sprintf(tmp, "rfibonacci-%03d.png", n);
1885  //~ write_image(image, tmp);
1886  sprintf(tmp, "fibonacci-%03d.hdr", n);
1887  write_image_hdr(image, tmp);
1888  //~ sprintf(tmp, "fibonacci-%03d.png", n);
1889  //~ write_image(image, tmp);
1890  }
1891  }
1892 
1893  if(0)
1894  {
1895  // disco ball !!
1896  char tmp[1024];
1897  for(int r= 0; r < 64; r++)
1898  {
1899  Image image= render_disco(m_camera_freeze, m_scene, m_bvh, float(r) / float(64), 64, Color(10));
1900 
1901  sprintf(tmp, "disco-%03d.hdr", r);
1902  write_image_hdr(image, tmp);
1903  //~ sprintf(tmp, "disco-%03d.png", r);
1904  //~ write_image(image, tmp);
1905  }
1906  }
1907 
1908  if(0)
1909  {
1910  char tmp[1024];
1911  for(int n : { 1, 4, 16, 64, 256 })
1912  {
1913  Image image= render_uniform(m_camera_freeze, m_scene, m_bvh, n, Color(10));
1914 
1915  sprintf(tmp, "uniform-%03d.hdr", n);
1916  write_image_hdr(image, tmp);
1917  //~ sprintf(tmp, "fibonacci-%03d.png", n);
1918  //~ write_image(image, tmp);
1919  }
1920  }
1921 
1922  if(0)
1923  {
1924  char tmp[1024];
1925  for(int n : { 1, 4, 16, 64, 256 })
1926  //~ for(int n : { 1, 4, 16, 64 })
1927  {
1928  Image image= render_sources(m_camera_freeze, m_scene, m_bvh, n, m_sources);
1929 
1930  //~ sprintf(tmp, "mcrendu_source-%03d.hdr", n);
1931  sprintf(tmp, "mcdirect_n-%03d.hdr", n);
1932  //~ sprintf(tmp, "mcdirect_cdf-%03d.hdr", n);
1933  write_image_hdr(image, tmp);
1934  //~ sprintf(tmp, "fibonacci-%03d.png", n);
1935  //~ write_image(image, tmp);
1936  }
1937  }
1938 
1939  export_capture= 0;
1940  }
1941  }
1942 
1943  capture= 0;
1944  }
1945 
1946 
1947  if(freeze)
1948  {
1949  if(show_frustum)
1950  {
1951  draw(m_image_plane, Inverse(m_camera_projection * m_camera_view), camera());
1952  draw(m_frustum, Inverse(m_camera_projection * m_camera_view), camera());
1953  //~ draw(m_camera, m_camera_model, camera());
1954  }
1955  if(m_ray.has_position()) draw(m_ray, Identity(), camera());
1956  if(m_objet_sources.has_position()) draw(m_objet_sources, Identity(), camera());
1957  if(m_hit.has_position()) draw(m_hit, m_hit_model, camera());
1958  if(m_directions.has_position()) draw(m_directions, m_hit_model, camera());
1959 
1960  if(show_capture)
1961  {
1962  if(mode == 0) display(m_La_texture, 0, 0, zoom+1);
1963  if(mode == 1) display(m_Le_texture, 0, 0, zoom+1);
1964  if(mode == 2) display(m_Li_texture, 0, 0, zoom+1);
1965  if(show_brdf) display(m_Fr_texture, 256, 0, zoom+1);
1966  }
1967  }
1968 
1969  begin(m_widgets);
1970  if(freeze)
1971  {
1972  if(button(m_widgets, "export", export_capture)) capture= 1;
1973  button(m_widgets, "show", show_capture);
1974  button(m_widgets, "show brdf", show_brdf);
1975  button(m_widgets, "show frustum", show_frustum);
1976  }
1977 
1978  begin_line(m_widgets);
1979  if(select(m_widgets, "ambient occlusion", 0, mode)) capture= 1;
1980  if(select(m_widgets, "direct", 1, mode)) capture= 1;
1981  if(select(m_widgets, "indirect", 2, mode)) capture= 1;
1982  if(value(m_widgets, "scale", m_scale, 0, 100, 1)) capture= 1;
1983 
1984  begin_line(m_widgets);
1985  if(select(m_widgets, "fibonacci", 1, sample_mode)) capture= 1;
1986  if(select(m_widgets, "uniform", 0, sample_mode)) capture= 1;
1987  if(select(m_widgets, "cos", 2, sample_mode)) capture= 1;
1988  if(select(m_widgets, "brdf", 3, sample_mode)) capture= 1;
1989  if(mode == 1) if(select(m_widgets, "sources", 4, sample_mode)) capture= 1;
1990  if(value(m_widgets, "samples", samples, 0, 256, 1)) capture= 1;
1991 
1992  end(m_widgets);
1993 
1994  draw(m_widgets, window_width(), window_height());
1995 
1996  return 1;
1997  }
1998 
1999 protected:
2000  GLTFScene m_scene;
2001  BVH m_bvh;
2002  std::vector<Source> m_sources;
2003 
2004  GLuint m_zoom_framebuffer;
2005  GLuint m_zoom_texture;
2006  GLuint m_framebuffer;
2007  GLuint m_La_texture;
2008  GLuint m_Le_texture;
2009  GLuint m_Li_texture;
2010  GLuint m_Fr_texture;
2011 
2012  Mesh m_objet;
2013  Mesh m_objet_sources;
2014  Mesh m_repere;
2015  Mesh m_hit;
2016  Mesh m_camera;
2017  Mesh m_image_plane;
2018  Mesh m_frustum;
2019  Mesh m_ray;
2020  Mesh m_directions;
2021 
2022  Orbiter m_camera_freeze;
2023  Transform m_camera_model;
2024  Transform m_camera_view;
2025  Transform m_camera_projection;
2026  Transform m_camera_viewport;
2027  Transform m_hit_model;
2028  int m_pixelx, m_pixely;
2029  float m_scale;
2030 
2031  Widgets m_widgets;
2032 };
2033 
2034 
2035 int main( int argc, char **argv )
2036 {
2037  const char *filename= "cornell.gltf";
2038  if(argc > 1)
2039  filename= argv[1];
2040 
2041  TP tp(filename);
2042  tp.run();
2043 
2044  return 0;
2045 }
classe application.
Definition: app_camera.h:19
const Orbiter & camera() const
renvoie l'orbiter gere par l'application.
Definition: app_camera.h:37
representation d'une image.
Definition: image.h:21
int height() const
renvoie la hauteur de l'image.
Definition: image.h:100
const void * data() const
renvoie un pointeur sur le stockage des couleurs des pixels.
Definition: image.h:84
int width() const
renvoie la largeur de l'image.
Definition: image.h:98
representation d'un objet / maillage.
Definition: mesh.h:112
unsigned int vertex(const vec3 &p)
insere un sommet de position p, et ses attributs (s'ils sont definis par color(), texcoord(),...
Definition: mesh.cpp:69
void bounds(Point &pmin, Point &pmax) const
renvoie min et max les coordonnees des extremites des positions des sommets de l'objet (boite engloba...
Definition: mesh.cpp:461
Color default_color() const
renvoie la couleur par defaut du mesh, utilisee si les sommets n'ont pas de couleur associee.
Definition: mesh.h:272
int create(const GLenum primitives)
construit les objets openGL.
Definition: mesh.cpp:16
GLenum primitives() const
renvoie le type de primitives.
Definition: mesh.h:324
void release()
detruit les objets openGL.
Definition: mesh.cpp:22
Mesh & color(const vec4 &c)
definit la couleur du prochain sommet.
Definition: mesh.cpp:38
bool has_position() const
verifie que les attributs sont decrits de maniere coherente.
Definition: mesh.h:316
representation de la camera, type orbiter, placee sur une sphere autour du centre de l'objet.
Definition: orbiter.h:17
void lookat(const Point &center, const float size)
observe le point center a une distance size.
Definition: orbiter.cpp:7
Transform viewport() const
renvoie la transformation viewport actuelle. doit etre initialise par projection(width,...
Definition: orbiter.cpp:83
Transform projection(const int width, const int height, const float fov)
fixe la projection reglee pour une image d'aspect width / height, et une demi ouverture de fov degres...
Definition: orbiter.cpp:47
Transform view() const
renvoie la transformation vue.
Definition: orbiter.cpp:40
Definition: alpha.cpp:59
int render()
a deriver pour afficher les objets. renvoie 1 pour continuer, 0 pour fermer l'application.
Definition: tuto_gltf.cpp:1619
int quit()
a deriver pour detruire les objets openGL. renvoie -1 pour indiquer une erreur, 0 sinon.
Definition: tuto_gltf.cpp:1586
int update(const float time, const float delta)
a deriver et redefinir pour animer les objets en fonction du temps.
Definition: tuto8.cpp:48
int init()
a deriver pour creer les objets openGL. renvoie -1 pour indiquer une erreur, 0 sinon.
Definition: tuto_gltf.cpp:1414
scene glTF.
int mesh_index
indice du maillage.
Definition: gltf.h:131
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
Widgets create_widgets()
cree une interface graphique. a detruire avec release_widgets( ).
Definition: widgets.cpp:12
void release_widgets(Widgets &w)
detruit l'interface graphique.
Definition: widgets.cpp:23
bool button(Widgets &w, const char *text, int &status)
Definition: widgets.cpp:155
int window_height()
renvoie la hauteur de la fenetre de l'application.
Definition: window.cpp:29
bool value(Widgets &w, const char *label, int &value, const int value_min, const int value_max, const int value_step)
valeur editable par increment.
Definition: widgets.cpp:191
void clear_key_state(const SDL_Keycode key)
desactive une touche du clavier.
Definition: window.cpp:48
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
bool select(Widgets &w, const char *text, const int option, int &status)
Definition: widgets.cpp:173
void end(Widgets &w)
termine la description des elements de l'interface graphique.
Definition: widgets.cpp:404
int key_state(const SDL_Keycode key)
renvoie l'etat d'une touche du clavier. cf la doc SDL2 pour les codes.
Definition: window.cpp:42
void begin_line(Widgets &w)
place les prochains elements sur une nouvelle ligne.
Definition: widgets.cpp:129
int window_width()
renvoie la largeur de la fenetre de l'application.
Definition: window.cpp:25
Color Red()
utilitaire. renvoie une couleur rouge.
Definition: color.cpp:41
Color Yellow()
utilitaire. renvoie une couleur jaune.
Definition: color.cpp:56
Color Blue()
utilitaire. renvoie une couleur bleue.
Definition: color.cpp:51
Color Black()
utilitaire. renvoie une couleur noire.
Definition: color.cpp:31
Color Green()
utilitaire. renvoie une couleur verte.
Definition: color.cpp:46
Color gamma(const Color &color)
correction gamma : rgb vers srgb
Definition: color.cpp:24
Color White()
utilitaire. renvoie une couleur blanche.
Definition: color.cpp:36
Transform Inverse(const Transform &m)
renvoie l'inverse de la matrice.
Definition: mat.cpp:197
Point Origin()
renvoie le point origine (0, 0, 0)
Definition: vec.cpp:8
Transform Rotation(const Vector &axis, const float angle)
renvoie la matrice representation une rotation de angle degree autour de l'axe axis.
Definition: mat.cpp:266
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 length2(const Vector &v)
renvoie la carre de la longueur d'un vecteur.
Definition: vec.cpp:147
float dot(const Vector &u, const Vector &v)
renvoie le produit scalaire de 2 vecteurs.
Definition: vec.cpp:137
float distance2(const Point &a, const Point &b)
renvoie le carre de la distance etre 2 points.
Definition: vec.cpp:19
Vector normalize(const Vector &v)
renvoie un vecteur unitaire / longueur == 1.
Definition: vec.cpp:123
Transform Translation(const Vector &v)
renvoie la matrice representant une translation par un vecteur.
Definition: mat.cpp:216
float length(const Vector &v)
renvoie la longueur d'un vecteur.
Definition: vec.cpp:142
Vector cross(const Vector &u, const Vector &v)
renvoie le produit vectoriel de 2 vecteurs.
Definition: vec.cpp:129
Transform Scale(const float x, const float y, const float z)
renvoie la matrice representant une mise a l'echelle / etirement.
Definition: mat.cpp:207
int capture(const char *prefix)
Definition: texture.cpp:221
GLuint make_vec4_texture(const int unit, const int width, const int height, const GLenum texel_type)
creation de textures pour stocker des donnees (autres qu'une couleur).
Definition: texture.cpp:187
int write_image_pfm(const Image &image, const char *filename)
enregistre une image dans un fichier .pfm.
Definition: image_hdr.cpp:146
int write_image_hdr(const Image &image, const char *filename)
enregistre une image dans un fichier .hdr.
Definition: image_hdr.cpp:56
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
regroupe tous les parametres de la matiere du point d'intersection.
Color diffuse
color.
Vector n
normale interpolee du point d'intersection
representation d'une couleur (rgba) transparente ou opaque.
Definition: color.h:14
Definition: tuto_gltf.cpp:759
float roughness
rugosite de la micro surface.
Definition: gltf.h:63
Color emission
emission pour les sources de lumieres ou pas (= noir).
Definition: gltf.h:61
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
intersection avec un triangle.
Definition: tuto_bvh2.cpp:33
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
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:146
const GLTFMaterial & hit_material(const Hit &hit, const GLTFScene &scene)
renvoie la matiere du point d'intersection.
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.
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.