gKit2 light
tuto_mcdirect.cpp
1 
2 #include <cstdio>
3 #include <cassert>
4 #include <vector>
5 #include <random>
6 
7 #include "vec.h"
8 #include "mat.h"
9 #include "orbiter.h"
10 #include "gltf.h"
11 
12 #include "color.h"
13 #include "image.h"
14 #include "image_hdr.h"
15 
16 // rayon
17 struct Ray
18 {
19  Point o; // origine
20  float pad;
21  Vector d; // direction
22  float tmax; // tmax= 1 ou \inf, le rayon est un segment ou une demi droite infinie
23 
24  Ray( const Point& _o, const Point& _e ) : o(_o), d(Vector(_o, _e)), tmax(1) {} // segment, t entre 0 et 1
25  Ray( const Point& _o, const Vector& _d ) : o(_o), d(_d), tmax(FLT_MAX) {} // demi droite, t entre 0 et \inf
26  Ray( const Point& _o, const Vector& _d, const float _tmax ) : o(_o), d(_d), tmax(_tmax) {} // explicite
27 };
28 
29 // intersection avec un triangle
30 struct Hit
31 {
32  float t; // p(t)= o + td, position du point d'intersection sur le rayon
33  float u, v; // p(u, v), position du point d'intersection sur le triangle
34  int node_id; // indice de l'instance, pour retrouver la transformation
35  int mesh_id; // indexation globale du triangle dans la scene gltf
36  int primitive_id;
37  int triangle_id;
38  int pad;
39 
40  Hit( ) : t(FLT_MAX), u(), v(), node_id(-1), mesh_id(-1), primitive_id(-1), triangle_id(-1) {}
41  Hit( const Ray& ray ) : t(ray.tmax), u(), v(), node_id(-1), mesh_id(-1), primitive_id(-1), triangle_id(-1) {}
42 
43  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),
44  node_id(_node_id), mesh_id(_mesh_id), primitive_id(_primitive_id), triangle_id(_id) {}
45 
46  operator bool ( ) { return (triangle_id != -1); } // renvoie vrai si l'intersection est definie / existe
47 };
48 
49 // intersection avec une boite / un englobant
50 struct BBoxHit
51 {
52  float tmin, tmax;
53 
54  BBoxHit() : tmin(FLT_MAX), tmax(-FLT_MAX) {}
55  BBoxHit( const float _tmin, const float _tmax ) : tmin(_tmin), tmax(_tmax) {}
56 
57  operator bool( ) const { return tmin <= tmax; } // renvoie vrai si l'intersection est definie / existe
58 };
59 
60 
61 // boite englobante
62 struct BBox
63 {
64  Point pmin, pmax;
65 
66  BBox( ) : pmin(), pmax() {}
67 
68  BBox( const Point& p ) : pmin(p), pmax(p) {}
69  BBox( const BBox& box ) : pmin(box.pmin), pmax(box.pmax) {}
70  BBox( const BBox& a, const BBox& b ) : pmin(min(a.pmin, b.pmin)), pmax(max(a.pmax, b.pmax)) {}
71 
72  BBox& insert( const Point& p ) { pmin= min(pmin, p); pmax= max(pmax, p); return *this; }
73  BBox& insert( const BBox& box ) { pmin= min(pmin, box.pmin); pmax= max(pmax, box.pmax); return *this; }
74 
75  float centroid( const int axis ) const { return (pmin(axis) + pmax(axis)) / 2; }
76  Point centroid( ) const { return (pmin + pmax) / 2; }
77 
78  BBoxHit intersect( const Ray& ray, const Vector& invd, const float htmax ) const
79  {
80  Point rmin= pmin;
81  Point rmax= pmax;
82  if(ray.d.x < 0) std::swap(rmin.x, rmax.x);
83  if(ray.d.y < 0) std::swap(rmin.y, rmax.y);
84  if(ray.d.z < 0) std::swap(rmin.z, rmax.z);
85  Vector dmin= (rmin - ray.o) * invd;
86  Vector dmax= (rmax - ray.o) * invd;
87 
88  float tmin= std::max(dmin.z, std::max(dmin.y, std::max(dmin.x, 0.f)));
89  float tmax= std::min(dmax.z, std::min(dmax.y, std::min(dmax.x, htmax)));
90  return BBoxHit(tmin, tmax);
91  }
92 };
93 
94 
95 // construction de l'arbre / BVH
96 struct Node
97 {
98  BBox bounds;
99  int left;
100  int right;
101 
102  bool internal( ) const { return right > 0; } // renvoie vrai si le noeud est un noeud interne
103  int internal_left( ) const { assert(internal()); return left; } // renvoie le fils gauche du noeud interne
104  int internal_right( ) const { assert(internal()); return right; } // renvoie le fils droit
105 
106  bool leaf( ) const { return right < 0; } // renvoie vrai si le noeud est une feuille
107  int leaf_begin( ) const { assert(leaf()); return -left; } // renvoie le premier objet de la feuille
108  int leaf_end( ) const { assert(leaf()); return -right; } // renvoie le dernier objet
109 };
110 
111 // creation d'un noeud interne
112 Node make_node( const BBox& bounds, const int left, const int right )
113 {
114  Node node { bounds, left, right };
115  assert(node.internal()); // verifie que c'est bien un noeud...
116  return node;
117 }
118 
119 // creation d'une feuille
120 Node make_leaf( const BBox& bounds, const int begin, const int end )
121 {
122  Node node { bounds, -begin, -end };
123  assert(node.leaf()); // verifie que c'est bien une feuille...
124  return node;
125 }
126 
127 
128 // bvh parametre par le type des primitives, cf triangle et instance...
129 template < typename T >
130 struct BVHT
131 {
132  // construit un bvh pour l'ensemble de primitives
133  int build( const std::vector<T>& _primitives )
134  {
135  primitives= _primitives; // copie les primitives pour les trier
136  nodes.clear(); // efface les noeuds
137  nodes.reserve(primitives.size());
138 
139  // construit l'arbre...
140  root= build(0, primitives.size());
141  return root;
142  }
143 
144  // intersection avec un rayon, entre 0 et htmax
145  Hit intersect( const Ray& ray, const float htmax ) const
146  {
147  Hit hit;
148  hit.t= htmax;
149  Vector invd= Vector(1 / ray.d.x, 1 / ray.d.y, 1 / ray.d.z);
150  intersect_fast(root, ray, invd, hit);
151  return hit;
152  }
153 
154  // intersection avec un rayon, entre 0 et ray.tmax
155  Hit intersect( const Ray& ray ) const { return intersect(ray, ray.tmax); }
156 
157  bool occluded( const Ray& ray ) const
158  {
159  Vector invd= Vector(1 / ray.d.x, 1 / ray.d.y, 1 / ray.d.z);
160  return occluded(root, ray, invd, 1);
161  }
162 
163  bool occluded( const Point& p, const Point& q ) const { Ray ray(p, q); return occluded(ray); }
164 
165  bool visible( const Point& p, const Point& q ) const { return !occluded(p, q); }
166  bool visible( const Ray& ray ) const { return !occluded(ray); }
167 
168 
169 protected:
170  std::vector<Node> nodes;
171  std::vector<T> primitives;
172  int root;
173 
174  int build( const int begin, const int end )
175  {
176  if(end - begin < 2)
177  {
178  // inserer une feuille et renvoyer son indice
179  int index= nodes.size();
180  nodes.push_back( make_leaf( primitive_bounds(begin, end), begin, end ) );
181  return index;
182  }
183 
184  // axe le plus etire de l'englobant des centres des englobants des primitives...
185  BBox cbounds= centroid_bounds(begin, end);
186  Vector d= Vector(cbounds.pmin, cbounds.pmax);
187  int axis;
188  if(d.x > d.y && d.x > d.z) // x plus grand que y et z ?
189  axis= 0;
190  else if(d.y > d.z) // y plus grand que z ? (et que x implicitement)
191  axis= 1;
192  else // x et y ne sont pas les plus grands...
193  axis= 2;
194 
195  // coupe l'englobant au milieu
196  float cut= cbounds.centroid(axis);
197 
198  // repartit les primitives
199  T *pm= std::partition(primitives.data() + begin, primitives.data() + end,
200  [axis, cut]( const T& primitive )
201  {
202  return primitive.bounds().centroid(axis) < cut;
203  }
204  );
205  int m= std::distance(primitives.data(), pm);
206 
207  // la repartition peut echouer, et toutes les primitives sont dans la meme moitiee de l'englobant
208  // forcer quand meme un decoupage en 2 ensembles
209  if(m == begin || m == end)
210  m= (begin + end) / 2;
211  assert(m != begin);
212  assert(m != end);
213 
214  // construire le fils gauche, les triangles se trouvent dans [begin .. m)
215  int left= build(begin, m);
216 
217  // on recommence pour le fils droit, les triangles se trouvent dans [m .. end)
218  int right= build(m, end);
219 
220  // construire le noeud et renvoyer son indice
221  int index= nodes.size();
222  nodes.push_back( make_node( BBox(nodes[left].bounds, nodes[right].bounds), left, right ) );
223  return index;
224  }
225 
226  // englobant des primitives
227  BBox primitive_bounds( const int begin, const int end )
228  {
229  BBox bbox= primitives[begin].bounds();
230  for(int i= begin +1; i < end; i++)
231  bbox.insert(primitives[i].bounds());
232 
233  return bbox;
234  }
235 
236  // englobant des centres des primitives
237  BBox centroid_bounds( const int begin, const int end )
238  {
239  BBox bbox= primitives[begin].bounds().centroid();
240  for(int i= begin +1; i < end; i++)
241  bbox.insert(primitives[i].bounds().centroid());
242 
243  return bbox;
244  }
245 
246  void intersect_fast( const int index, const Ray& ray, const Vector& invd, Hit& hit ) const
247  {
248  const Node& node= nodes[index];
249 
250  if(node.leaf())
251  {
252  //~ if(node.bounds.intersect(ray, invd, hit.t)) // deja teste !!
253  {
254  for(int i= node.leaf_begin(); i < node.leaf_end(); i++)
255  if(Hit h= primitives[i].intersect(ray, hit.t))
256  hit= h;
257  }
258  }
259  else
260  {
261  BBoxHit left= nodes[node.internal_left()].bounds.intersect(ray, invd, hit.t);
262  BBoxHit right= nodes[node.internal_right()].bounds.intersect(ray, invd, hit.t);
263 
264  // visiter les 2 fils
265  if(left && right)
266  {
267  if(left.tmin < right.tmin) // gauche puis droit
268  {
269  intersect_fast(node.internal_left(), ray, invd, hit);
270  intersect_fast(node.internal_right(), ray, invd, hit);
271  }
272  else // d'abord droit
273  {
274  intersect_fast(node.internal_right(), ray, invd, hit);
275  intersect_fast(node.internal_left(), ray, invd, hit);
276  }
277  }
278 
279  // visiter un seul fils
280  else if(left)
281  intersect_fast(node.internal_left(), ray, invd, hit);
282  else if(right)
283  intersect_fast(node.internal_right(), ray, invd, hit);
284  }
285  }
286 
287  bool occluded( const int index, const Ray& ray, const Vector& invd, const float htmax ) const
288  {
289  const Node& node= nodes[index];
290  if(node.bounds.intersect(ray, invd, htmax))
291  {
292  if(node.leaf())
293  {
294  for(int i= node.leaf_begin(); i < node.leaf_end(); i++)
295  if(primitives[i].intersect(ray, htmax))
296  return true;
297  }
298  else // if(node.internal())
299  {
300  if(occluded(node.internal_left(), ray, invd, htmax) || occluded(node.internal_right(), ray, invd, htmax))
301  return true;
302  }
303  }
304 
305  return false;
306  }
307 };
308 
309 
310 // triangle pour le bvh, cf fonction bounds() et intersect()
311 struct Triangle
312 {
313  Point p; // sommet a du triangle
314  Vector e1, e2; // aretes ab, ac du triangle
315  int node_id;
316  int mesh_id;
317  int primitive_id;
318  int triangle_id;
319 
320  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 ) :
321  p(a), e1(Vector(a, b)), e2(Vector(a, c)),
322  node_id(_node_id), mesh_id(_mesh_id), primitive_id(_primitive_id), triangle_id(_id) {}
323 
324  /* calcule l'intersection ray/triangle
325  cf "fast, minimum storage ray-triangle intersection"
326 
327  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.)
328  renvoie vrai + les coordonnees barycentriques (u, v) du point d'intersection + sa position le long du rayon (t).
329  convention barycentrique : p(u, v)= (1 - u - v) * a + u * b + v * c
330  */
331  Hit intersect( const Ray &ray, const float htmax ) const
332  {
333  Vector pvec= cross(ray.d, e2);
334  float det= dot(e1, pvec);
335 
336  float inv_det= 1 / det;
337  Vector tvec(p, ray.o);
338 
339  float u= dot(tvec, pvec) * inv_det;
340  if(u < 0 || u > 1) return Hit();
341 
342  Vector qvec= cross(tvec, e1);
343  float v= dot(ray.d, qvec) * inv_det;
344  if(v < 0 || u + v > 1) return Hit();
345 
346  float t= dot(e2, qvec) * inv_det;
347  if(t < 0 || t > htmax) return Hit();
348 
349  return Hit(t, u, v, node_id, mesh_id, primitive_id, triangle_id);
350  }
351 
352  BBox bounds( ) const
353  {
354  BBox box(p);
355  return box.insert(p+e1).insert(p+e2);
356  }
357 };
358 
359 typedef BVHT<Triangle> BVH;
360 
361 
362 // renvoie la positions sur le rayon
363 Point hit_position( const Hit& hit, const Ray& ray )
364 {
365  assert(hit.triangle_id != -1);
366  return ray.o + hit.t * ray.d;
367 }
368 
369 
370 //
371 static GLTFMaterial default_material= GLTFMaterial();
372 
373 // renvoie la matiere du point d'intersection
374 const GLTFMaterial& hit_material( const Hit& hit, const GLTFScene& scene )
375 {
376  assert(hit.mesh_id != -1);
377  assert(hit.primitive_id != -1);
378 
379  const GLTFMesh& mesh= scene.meshes[hit.mesh_id];
380  const GLTFPrimitives& primitives= mesh.primitives[hit.primitive_id];
381 
382  if(primitives.material_index == -1)
383  return default_material;
384 
385  assert(primitives.material_index < int(scene.materials.size()));
386  return scene.materials[primitives.material_index];
387 }
388 
389 Color material_diffuse( const GLTFMaterial& material )
390 {
391  return (1 - material.metallic) * material.color / float(M_PI);
392 }
393 
394 
395 // renvoie la normale interpolee au point d'intersection dans le repere de la scene
396 Vector hit_normal( const Hit& hit, const GLTFScene& scene )
397 {
398  assert(hit.node_id != -1);
399  assert(hit.mesh_id != -1);
400  assert(hit.primitive_id != -1);
401  assert(hit.triangle_id != -1);
402 
403  const GLTFMesh& mesh= scene.meshes[hit.mesh_id];
404  const GLTFPrimitives& primitives= mesh.primitives[hit.primitive_id];
405 
406  // indice des sommets
407  int a= primitives.indices[hit.triangle_id];
408  int b= primitives.indices[hit.triangle_id+1];
409  int c= primitives.indices[hit.triangle_id+2];
410 
411  // normales des sommets
412  assert(primitives.normals.size());
413  Vector na= primitives.normals[a];
414  Vector nb= primitives.normals[b];
415  Vector nc= primitives.normals[c];
416 
417  // interpole la normale au point d'intersection
418  // attention : il faut utiliser la meme convetion barycentrique que la fonction d'intersection rayon/triangle !!
419  Vector n= (1 - hit.u - hit.v) * na + hit.u * nb + hit.v * nc;
420 
421  // transforme la normale dans le repere de la scene
422  const GLTFNode& node= scene.nodes[hit.node_id];
423  // les normales ne se transforment pas exactement comme les positions...
424  // les sommets sont transformes par node.model, comment transformer la normale pour quelle reste perpendiculaire au triangle
425  Transform T= node.model.normal(); // todo !! a precalculer, inverser la matrice a chaque fois est un peu trop lent...
426 
427  return normalize( T(n) );
428 }
429 
430 
431 // construit un repere tbn, a partir d'un seul vecteur.
432 // cf "generating a consistently oriented tangent space"
433 // http://people.compute.dtu.dk/jerf/papers/abstracts/onb.html
434 // et
435 // cf "Building an Orthonormal Basis, Revisited", Pixar, 2017
436 // http://jcgt.org/published/0006/01/01/
437 // pour corriger un probleme numerique...
438 struct World
439 {
440  World( ) : t(), b(), n() {}
441  World( const Vector& _n ) : n(_n)
442  {
443  float sign= std::copysign(1.0f, n.z);
444  float a= -1.0f / (sign + n.z);
445  float d= n.x * n.y * a;
446  t= Vector(1.0f + sign * n.x * n.x * a, sign * d, -sign * n.x);
447  b= Vector(d, sign + n.y * n.y * a, -n.y);
448  }
449 
450  // transforme le vecteur du repere local vers le repere du monde
451  Vector operator( ) ( const Vector& local ) const { return local.x * t + local.y * b + local.z * n; }
452 
453  // transforme le vecteur du repere du monde vers le repere local
454  Vector local( const Vector& global ) const { return Vector(dot(global, t), dot(global, b), dot(global, n)); }
455 
456  Vector t;
457  Vector b;
458  Vector n;
459 };
460 
461 
462 struct Sampler
463 {
464  std::uniform_real_distribution<float> u01;
465  std::default_random_engine rng;
466 
467  //~ Sampler( const unsigned _seed ) : u01(0, 1 - 2*std::numeric_limits<float>::epsilon()), rng(_seed) {}
468  Sampler( const unsigned _seed ) : u01(), rng(_seed) {}
469  void seed( const unsigned _seed ) { rng.seed(_seed); }
470 
471  float sample( ) { return u01(rng); } // renvoie un reel [0, 1)
472  unsigned sample_binary( ) { return rng(); } // renvoie un entier 32bits
473  int sample_range( const int n ) { return int(sample() * n); } // renvoie un entier [0 n)
474 };
475 
476 
477 struct Source
478 {
479  Point a, b, c;
480  Vector n;
481  Color emission;
482  float area;
483 
484  Source( const Point& _a, const Point& _b, const Point& _c, const Color& _emission ) : a(_a), b(_b), c(_c), n(), emission(_emission), area()
485  {
486  Vector ng= cross( Vector(a, b), Vector(a, c) );
487  area= length(ng) / 2;
488  n= normalize(ng);
489 
490  assert(area * emission.max() > 0);
491  }
492 
493  Point sample( const float u1, const float u2 ) const
494  {
495  #if 0
496  // cf GI compendium eq 18, https://people.cs.kuleuven.be/~philip.dutre/GI/TotalCompendium.pdf
497  // ou PBRT http://www.pbr-book.org/3ed-2018/Monte_Carlo_Integration/2D_Sampling_with_Multidimensional_Transformations.html#SamplingaTriangle
498  float r1= std::sqrt(u1);
499  float alpha= 1 - r1;
500  float beta= (1 - u2) * r1;
501  float gamma= u2 * r1;
502 
503  return alpha*a + beta*b + gamma*c;
504 
505  #else
506  // cf A Low-Distortion Map Between Triangle and Square
507  // https://hal.archives-ouvertes.fr/hal-02073696v2/document
508  float t1= u1 / 2;
509  float t2= u2 / 2;
510  float offset= t2 - t1;
511  if(offset > 0)
512  t2+= offset;
513  else
514  t1-= offset;
515 
516  return t1*a + t2*b + (1 - t1 - t2)*c;
517  #endif
518  }
519 
520  float pdf( const Point& p ) const
521  {
522  return 1 / area;
523  }
524 };
525 
526 
527 float blinn( const float roughness )
528 {
529  if(roughness < 0.001)
530  return 1000;
531 
532  return 2 / (roughness*roughness*roughness*roughness) - 2;
533 }
534 
535 // matiere : melange diffuse + reflet
536 // modele blinn-phong normalise, cf http://perso.univ-lyon1.fr/jean-claude.iehl/Public/educ/M1IMAGE/html/group__brdf.html
537 struct Brdf
538 {
539  World world;
540  Color diffuse;
541  float kd;
542  float ns;
543 
544  Brdf( ) : world(), diffuse(), kd(), ns() {}
545  Brdf( const Vector& ng, const Color& color, const float plastic, const float n ) : world(ng), diffuse(color), kd(1 - plastic), ns(n) {}
546 
547  Color f( const Vector& l, const Vector& o ) const
548  {
549  float cos_theta= dot(world.n, l);
550  if(cos_theta <= 0)
551  return Black();
552 
553  Vector h= normalize(o + l);
554  float cos_theta_h= dot(world.n, h);
555  if(cos_theta_h <= 0)
556  return Black();
557 
558  // evalue la brdf : mixture d'un terme diffus et d'un reflet,
559  Color d= 1 / float(M_PI) * diffuse;
560  Color s= (ns + 8) / float(8*M_PI) * std::pow(cos_theta_h, ns) * White();
561  return kd * d + (1 - kd) * s;
562  }
563 
564  Vector sample( const float u1, const float u2, const float u3, const Vector& o ) const
565  {
566  // echantillonne la mixture diffus + reflet
567  if(u1 <= kd)
568  {
569  // terme diffus
570  // genere une direction cos theta / pi, cf GI compendium, eq 35
571  float phi= float(2*M_PI) * u3;
572  float cos_theta= std::sqrt(u2);
573  float sin_theta= std::sqrt(1 - cos_theta*cos_theta);
574 
575  return world(Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta));
576  }
577  else
578  {
579  // terme reflechissant
580  // genere une direction h
581  // genere une direction cos^n theta / pi, cf GI compendium, eq 35+
582  float phi= float(2*M_PI) * u3;
583  float cos_theta= std::pow(u2, 1 / float(ns +1));
584  assert(cos_theta >= 0);
585  float sin_theta= std::sqrt(1 - cos_theta*cos_theta);
586  Vector h= world(Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta));
587  // genere une direction reflechie, l= reflect(o | h)
588  return -o + 2 * dot(h, o) * h;
589  // attention : peut etre sous le plan tangent...
590  }
591  }
592 
593  float pdf( const Vector& l, const Vector& o ) const
594  {
595  float cos_theta= dot(world.n, l);
596  if(cos_theta <= 0)
597  return 0;
598 
599  // pdf du terme diffus
600  float d= cos_theta / float(M_PI);
601 
602  // pdf du terme reflechissant
603  Vector h= normalize(o + l);
604  if(dot(world.n, h) <= 0)
605  return 0;
606  if(dot(l, h) <= 0)
607  return 0;
608 
609  float cos_theta_h= std::max(float(0), dot(world.n, h));
610  float s= (ns + 1) / float(2*M_PI) * std::pow(cos_theta_h, ns) / (4 * dot(l, h));
611  // le terme 1 / dot(l, h) est introduit par le changement de variable : direction h vers direction reflechie
612 
613  // pdf de la mixture
614  return kd*d + (1 - kd)*s;
615  }
616 };
617 
618 
619 
620 
621 
622 struct MCColor
623 {
624  Color color;
625  Color M;
626  Color M2;
627  int n;
628 };
629 
630 const float epsilon= 0.001;
631 
632 Image direct( const Orbiter& camera, const GLTFScene& scene, const BVH& bvh, const int samples, const std::vector<Source>& sources )
633 {
634  Image image(camera.width(), camera.height());
635 
636  const int AA= 1;
637  const int N= samples;
638 
639  printf("render %dx%d %d samples...\n", image.width(), image.height(), samples);
640 
641  // normalisation pdf source
642  float total= 0;
643  for(unsigned i= 0; i < sources.size(); i++)
644  total+= sources[i].area;
645 
646  Transform model= Identity();
647  Transform view= camera.view();
648  Transform projection= camera.projection();
649  Transform viewport= camera.viewport();
650  Transform inv= Inverse(viewport *projection * view); // image vers scene
651 
652  #pragma omp parallel for schedule(dynamic, 1)
653  for(int py= 0; py < image.height(); py++)
654  {
655  std::random_device hwseed;
656  Sampler rng( hwseed() );
657 
658  for(int px= 0; px < image.width(); px++)
659  {
660  Color M;
661  Color M2;
662  int n= 0;
663 
664  Color emission;
665  for(int pa= 0; pa < AA; pa++)
666  {
667  //~ float ax= rng.sample();
668  //~ float ay= rng.sample();
669  float ax= 0;
670  float ay= 0;
671 
672  Point o= inv( Point(px +ax, py +ay, 0) );
673  Point e= inv( Point(px +ax, py +ay, 1) );
674  Vector d= Vector(o, e);
675 
676  Ray ray(o, d);
677  if(Hit hit= bvh.intersect(ray))
678  {
679  Point p= hit_position(hit, ray);
680  Vector pn= hit_normal(hit, scene);
681  Color diffuse= material_diffuse( hit_material(hit, scene) );
682 
683  if(dot(ray.d, pn) > 0) // retourne la normale si elle n'est pas orientee vers l'origine du rayon...
684  pn= -pn;
685 
686  emission= emission + hit_material(hit, scene).emission;
687 
688  World world(pn);
689  for(int i= 0; i < N; i++)
690  {
691  //~ int id= -1;
692  int id= int(sources.size()) -1;
693  {
694  float u= rng.sample();
695  // construit et parcours la fonction de repartition
696  float cdf= 0;
697  for(unsigned i= 0; i < sources.size(); i++)
698  {
699  cdf+= sources[i].area / total;
700  if(u < cdf)
701  {
702  id= i;
703  break;
704  }
705  }
706 
707  if(u > cdf)
708  {
709  //~ printf("cdf %.9f %.9f / %.9f\n", u, cdf, total);
710  if(id == -1) id= int(sources.size()) -1;
711  }
712 
713  assert(id != -1);
714  assert(id < int(sources.size()));
715  }
716 
717  // construit le point q sur la source selectionnee
718  Point q= sources[id].sample(rng.sample(), rng.sample());
719  float pdf= 1 / total;
720 
721  Color sample;
722  if(bvh.visible( p+epsilon*pn, q+epsilon*sources[id].n ))
723  {
724  Vector l= Vector(p, q);
725  float cos_theta= std::max( float(0), dot( normalize(pn), normalize(l) ) );
726  float cos_theta_q= std::max( float(0), -dot( normalize(sources[id].n), normalize(l) ) );
727 
728  sample= diffuse * sources[id].emission * cos_theta * cos_theta_q / length2(l) / pdf;
729  }
730 
731  // accumule le sample,
732  // cf https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
733  n++;
734  Color Mn1= M;
735  M= Mn1 + (sample - Mn1) / float(n);
736  M2= M2 + (sample - Mn1) * (sample - M);
737  }
738  }
739  }
740 
741  emission= emission / float(AA);
742  image(px, py)= Color(M + emission, 1);
743  //~ image(px, py)= Color(M2 / float(n), 1);
744  }
745  }
746 
747  return image;
748 }
749 
750 Image direct( const Orbiter& camera, const GLTFScene& scene, const BVH& bvh, const int samples )
751 {
752  Image image(camera.width(), camera.height());
753 
754  const int AA= 1;
755  const int N= samples;
756 
757  printf("render %dx%d %d samples...\n", image.width(), image.height(), samples);
758 
759  Transform model= Identity();
760  Transform view= camera.view();
761  Transform projection= camera.projection();
762  Transform viewport= camera.viewport();
763  Transform inv= Inverse(viewport *projection * view); // image vers scene
764 
765  #pragma omp parallel for schedule(dynamic, 1)
766  for(int py= 0; py < image.height(); py++)
767  {
768  std::random_device hwseed;
769  Sampler rng( hwseed() );
770 
771  for(int px= 0; px < image.width(); px++)
772  {
773  Color M;
774  Color M2;
775  int n= 0;
776 
777  Color emission;
778  for(int pa= 0; pa < AA; pa++)
779  {
780  //~ float ax= rng.sample();
781  //~ float ay= rng.sample();
782  float ax= 0;
783  float ay= 0;
784 
785  Point o= inv( Point(px +ax, py +ay, 0) );
786  Point e= inv( Point(px +ax, py +ay, 1) );
787  Vector d= Vector(o, e);
788 
789  Ray ray(o, d);
790  if(Hit hit= bvh.intersect(ray))
791  {
792  Point p= hit_position(hit, ray);
793  Vector pn= hit_normal(hit, scene);
794  Color diffuse= material_diffuse( hit_material(hit, scene) );
795 
796  if(dot(ray.d, pn) > 0) // retourne la normale si elle n'est pas orientee vers l'origine du rayon...
797  pn= -pn;
798 
799  emission= emission + hit_material(hit, scene).emission;
800 
801  World world(pn);
802  for(int i= 0; i < N; i++)
803  {
804  Vector l;
805  float pdf;
806  #if 0
807  {
808  // genere une direction p(l)= 1 / (2pi)
809  float cos_theta= rng.sample();
810  float sin_theta= std::sqrt(1 - cos_theta * cos_theta);
811  float phi= float(2*M_PI) * rng.sample();
812 
813  // construit les composantes de la direction l et transforme dans le repere de la scene
814  l= world( Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta) );
815 
816  // evalue la pdf
817  pdf= 1 / float(2*M_PI);
818  }
819  #else
820  {
821  // genere une direction p(l)= cos / (pi)
822  float cos_theta= std::sqrt( rng.sample() );
823  float sin_theta= std::sqrt(1 - cos_theta * cos_theta);
824  float phi= float(2*M_PI) * rng.sample();
825 
826  // construit les composantes de la direction l et transforme dans le repere de la scene
827  l= world( Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta) );
828 
829  // evalue la pdf
830  pdf= cos_theta / float(M_PI);
831  }
832  #endif
833 
834  Color sample;
835  if(Hit source= bvh.intersect( Ray(p+epsilon*pn, l) ))
836  {
837  Color Le= hit_material(source, scene).emission;
838  float cos_theta= std::max( float(0), dot( normalize(pn), normalize(l) ) );
839 
840  sample= diffuse * Le * cos_theta / pdf;
841  }
842 
843  // accumule le sample,
844  // cf https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
845  n++;
846  Color Mn1= M;
847  M= Mn1 + (sample - Mn1) / float(n);
848  M2= M2 + (sample - Mn1) * (sample - M);
849  }
850  }
851  }
852 
853  emission= emission / float(AA);
854  image(px, py)= Color(M + emission, 1);
855  //~ image(px, py)= Color(M2 / float(n), 1);
856  }
857  }
858 
859  return image;
860 }
861 
862 
863 Color direct( const Point& p, const World& world, const GLTFScene& scene, const BVH& bvh, Sampler& rng, const std::vector<Source>& sources, const float sources_area )
864 {
865  int id= int(sources.size()) -1;
866  {
867  float u= rng.sample();
868  // construit et parcours la fonction de repartition
869  float cdf= 0;
870  for(unsigned i= 0; i < sources.size(); i++)
871  {
872  cdf+= sources[i].area / sources_area;
873  if(u < cdf)
874  {
875  id= i;
876  break;
877  }
878  }
879  }
880 
881  // construit le point q sur la source selectionnee
882  Point q= sources[id].sample(rng.sample(), rng.sample());
883  float pdf= 1 / sources_area;
884 
885  Vector l= Vector(p, q);
886  if(bvh.visible( Ray(p+epsilon*world.n, q+epsilon*sources[id].n) ))
887  {
888  float cos_theta= std::max(float(0), dot( normalize(world.n), normalize(l) ));
889  float cos_theta_q= std::max(float(0), -dot( normalize(sources[id].n), normalize(l) ));
890 
891  return sources[id].emission * cos_theta * cos_theta_q / length2(l) / pdf;
892  }
893 
894  return Black();
895 }
896 
897 Color indirect( const Point& p, const World& world, const GLTFScene& scene, const BVH& bvh, Sampler& rng, const std::vector<Source>& sources, const float sources_area )
898 {
899  // genere une direction p(l)= cos / pi
900  Vector l;
901  float pdf;
902  {
903  float cos_theta= std::sqrt( rng.sample() );
904  float sin_theta= std::sqrt(1 - cos_theta * cos_theta);
905  float phi= float(2*M_PI) * rng.sample();
906 
907  // construit les composantes de la direction l et transforme dans le repere de la scene
908  l= world( Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta) );
909 
910  // evalue la pdf
911  pdf= cos_theta / float(M_PI);
912  }
913 
914  // intersection
915  Ray ray(p+epsilon*world.n, l);
916  if(Hit qhit= bvh.intersect(ray))
917  {
918  // recuperer les infos sur l'intersection
919  Point q= hit_position(qhit, ray);
920  Vector qn= hit_normal(qhit, scene);
921 
922  if(dot(ray.d, qn) > 0) // retourne la normale si elle n'est pas orientee vers l'origine du rayon...
923  qn= -qn;
924  World qworld(qn);
925 
926  float cos_theta= std::max(float(0), dot(world.n, l));
927  Color qdiffuse= material_diffuse( hit_material(qhit, scene) );
928  return qdiffuse * direct(q, qworld, scene, bvh, rng, sources, sources_area) * cos_theta / pdf;
929  }
930 
931  return Black();
932 }
933 
934 
935 Image indirect( const Orbiter& camera, const GLTFScene& scene, const BVH& bvh, const int samples, const std::vector<Source>& sources )
936 {
937  Image image(camera.width(), camera.height());
938 
939  const int AA= 1;
940  const int N= samples;
941 
942  printf("render %dx%d %d samples...\n", image.width(), image.height(), samples);
943 
944  // normalisation pdf source
945  float total= 0;
946  for(unsigned i= 0; i < sources.size(); i++)
947  total+= sources[i].area;
948 
949  Transform model= Identity();
950  Transform view= camera.view();
951  Transform projection= camera.projection();
952  Transform viewport= camera.viewport();
953  Transform inv= Inverse(viewport *projection * view); // image vers scene
954 
955  #pragma omp parallel for schedule(dynamic, 1)
956  for(int py= 0; py < image.height(); py++)
957  {
958  std::random_device hwseed;
959  Sampler rng( hwseed() );
960 
961  for(int px= 0; px < image.width(); px++)
962  {
963  Color M;
964  Color M2;
965  int n= 0;
966 
967  Color emission;
968  for(int pa= 0; pa < AA; pa++)
969  {
970  //~ float ax= rng.sample();
971  //~ float ay= rng.sample();
972  float ax= 0;
973  float ay= 0;
974 
975  Point o= inv( Point(px +ax, py +ay, 0) );
976  Point e= inv( Point(px +ax, py +ay, 1) );
977  Vector d= Vector(o, e);
978 
979  Ray ray(o, d);
980  if(Hit hit= bvh.intersect(ray))
981  {
982  Point p= hit_position(hit, ray);
983  Vector pn= hit_normal(hit, scene);
984  Color diffuse= material_diffuse( hit_material(hit, scene) );
985 
986  emission= emission + hit_material(hit, scene).emission;
987 
988  if(dot(ray.d, pn) > 0) // retourne la normale si elle n'est pas orientee vers l'origine du rayon...
989  pn= -pn;
990 
991  World world(pn);
992  for(int i= 0; i < N; i++)
993  {
994  Color d= diffuse * direct(p, world, scene, bvh, rng, sources, total);
995  Color i1= diffuse * indirect(p, world, scene, bvh, rng, sources, total);
996  Color sample= d + i1;
997  //~ Color sample= d;
998 
999  // accumule le sample,
1000  // cf https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
1001  n++;
1002  Color Mn1= M;
1003  M= Mn1 + (sample - Mn1) / float(n);
1004  M2= M2 + (sample - Mn1) * (sample - M);
1005  }
1006  }
1007  }
1008 
1009  emission= emission / float(AA);
1010 
1011  //~ color= color / float(N);
1012  //~ color= color / float(AA);
1013 
1014  //~ image(px, py)= Color(color + emission, 1);
1015  //~ image(px, py)= Color(M2 / float(n), 1);
1016  image(px, py)= Color(M + emission, 1);
1017  }
1018  }
1019 
1020  return image;
1021 }
1022 
1023 
1024 Color direct( const Point& p, const World& world, const GLTFScene& scene, const BVH& bvh, Sampler& rng )
1025 {
1026  // genere une direction p(l)= cos / pi
1027  Vector l;
1028  float pdf;
1029  {
1030  float cos_theta= std::sqrt( rng.sample() );
1031  float sin_theta= std::sqrt(1 - cos_theta * cos_theta);
1032  float phi= float(2*M_PI) * rng.sample();
1033 
1034  // construit les composantes de la direction l et transforme dans le repere de la scene
1035  l= world( Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta) );
1036 
1037  // evalue la pdf
1038  pdf= cos_theta / float(M_PI);
1039  }
1040 
1041  // intersection
1042  Ray ray(p+epsilon*world.n, l);
1043  if(Hit qhit= bvh.intersect(ray))
1044  {
1045  // recuperer les infos sur l'intersection
1046  Vector qn= hit_normal(qhit, scene);
1047 
1048  Color qemission= Black();
1049  if(dot(l, qn) < 0)
1050  qemission= hit_material(qhit, scene).emission;
1051 
1052  float cos_theta= std::max(float(0), dot(world.n, l));
1053  return qemission * cos_theta / pdf;
1054  }
1055 
1056  return Black();
1057 }
1058 
1059 Color indirect( const Point& p, const World& world, const GLTFScene& scene, const BVH& bvh, Sampler& rng )
1060 {
1061  // genere une direction p(l)= cos / pi
1062  Vector l;
1063  float pdf;
1064  {
1065  float cos_theta= std::sqrt( rng.sample() );
1066  float sin_theta= std::sqrt(1 - cos_theta * cos_theta);
1067  float phi= float(2*M_PI) * rng.sample();
1068 
1069  // construit les composantes de la direction l et transforme dans le repere de la scene
1070  l= world( Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta) );
1071 
1072  // evalue la pdf
1073  pdf= cos_theta / float(M_PI);
1074  }
1075 
1076  // intersection
1077  Ray ray(p+epsilon*world.n, l);
1078  if(Hit qhit= bvh.intersect(ray))
1079  {
1080  // recuperer les infos sur l'intersection
1081  const GLTFMaterial& qmaterial= hit_material(qhit, scene);
1082  if(qmaterial.emission.max() > 0)
1083  return Black();
1084 
1085  Point q= hit_position(qhit, ray);
1086  Vector qn= hit_normal(qhit, scene);
1087 
1088  if(dot(ray.d, qn) > 0) // retourne la normale si elle n'est pas orientee vers l'origine du rayon...
1089  qn= -qn;
1090 
1091  World qworld(qn);
1092  Color qdiffuse= material_diffuse( qmaterial );
1093  float cos_theta= std::max(float(0), dot(world.n, l));
1094  return qdiffuse * direct(q, qworld, scene, bvh, rng) * cos_theta / pdf;
1095  }
1096 
1097  return Black();
1098 }
1099 
1100 Image indirect( const Orbiter& camera, const GLTFScene& scene, const BVH& bvh, const int samples )
1101 {
1102  Image image(camera.width(), camera.height());
1103 
1104  const int AA= 1;
1105  const int N= samples;
1106 
1107  printf("render %dx%d %d samples...\n", image.width(), image.height(), samples);
1108 
1109  Transform model= Identity();
1110  Transform view= camera.view();
1111  Transform projection= camera.projection();
1112  Transform viewport= camera.viewport();
1113  Transform inv= Inverse(viewport *projection * view); // image vers scene
1114 
1115  #pragma omp parallel for schedule(dynamic, 1)
1116  for(int py= 0; py < image.height(); py++)
1117  {
1118  std::random_device hwseed;
1119  Sampler rng( hwseed() );
1120 
1121  for(int px= 0; px < image.width(); px++)
1122  {
1123  Color color;
1124  Color emission;
1125  for(int pa= 0; pa < AA; pa++)
1126  {
1127  //~ float ax= rng.sample();
1128  //~ float ay= rng.sample();
1129  float ax= 0;
1130  float ay= 0;
1131 
1132  Point o= inv( Point(px +ax, py +ay, 0) );
1133  Point e= inv( Point(px +ax, py +ay, 1) );
1134  Vector d= Vector(o, e);
1135 
1136  Ray ray(o, d);
1137  if(Hit hit= bvh.intersect(ray))
1138  {
1139  Point p= hit_position(hit, ray);
1140  Vector pn= hit_normal(hit, scene);
1141  Color diffuse= material_diffuse( hit_material(hit, scene) );
1142 
1143  if(dot(d, pn) < 0)
1144  emission= emission + hit_material(hit, scene).emission;
1145 
1146  if(dot(ray.d, pn) > 0) // retourne la normale si elle n'est pas orientee vers l'origine du rayon...
1147  pn= -pn;
1148 
1149  World world(pn);
1150  for(int i= 0; i < N; i++)
1151  {
1152  Color d= diffuse * direct(p, world, scene, bvh, rng);
1153  Color i1= diffuse * indirect(p, world, scene, bvh, rng);
1154  color= color + d + i1;
1155  }
1156  }
1157  }
1158 
1159  emission= emission / float(AA);
1160  color= color / float(AA*N);
1161 
1162  image(px, py)= Color(color + emission, 1);
1163  }
1164  }
1165 
1166  return image;
1167 }
1168 
1169 Image ao( const Orbiter& camera, const GLTFScene& scene, const BVH& bvh, const int samples, const std::vector<Source>& sources )
1170 {
1171  Image image(camera.width(), camera.height());
1172 
1173  const int AA= 1;
1174  const int N= samples;
1175 
1176  printf("render %dx%d %d samples...\n", image.width(), image.height(), samples);
1177 
1178  // normalisation pdf source
1179  float total= 0;
1180  for(unsigned i= 0; i < sources.size(); i++)
1181  total+= sources[i].area;
1182 
1183  Transform model= Identity();
1184  Transform view= camera.view();
1185  Transform projection= camera.projection();
1186  Transform viewport= camera.viewport();
1187  Transform inv= Inverse(viewport *projection * view); // image vers scene
1188 
1189  #pragma omp parallel for schedule(dynamic, 1)
1190  for(int py= 0; py < image.height(); py++)
1191  {
1192  std::random_device hwseed;
1193  Sampler rng( hwseed() );
1194 
1195  for(int px= 0; px < image.width(); px++)
1196  {
1197  Color M;
1198  Color M2;
1199  int n= 0;
1200 
1201  Color emission;
1202  for(int pa= 0; pa < AA; pa++)
1203  {
1204  //~ float ax= rng.sample();
1205  //~ float ay= rng.sample();
1206  float ax= 0;
1207  float ay= 0;
1208 
1209  Point o= inv( Point(px +ax, py +ay, 0) );
1210  Point e= inv( Point(px +ax, py +ay, 1) );
1211  Vector d= Vector(o, e);
1212 
1213  Ray ray(o, d);
1214  if(Hit hit= bvh.intersect(ray))
1215  {
1216  Point p= hit_position(hit, ray);
1217  Vector pn= hit_normal(hit, scene);
1218  Color diffuse= material_diffuse( hit_material(hit, scene) );
1219 
1220  if(dot(ray.d, pn) > 0) // retourne la normale si elle n'est pas orientee vers l'origine du rayon...
1221  pn= -pn;
1222 
1223  emission= emission + hit_material(hit, scene).emission;
1224 
1225  World world(pn);
1226  for(int i= 0; i < N; i++)
1227  {
1228  Vector l;
1229  float pdf;
1230  #if 1
1231  {
1232  // genere une direction p(l)= 1 / (2pi)
1233  float cos_theta= rng.sample();
1234  float sin_theta= std::sqrt(1 - cos_theta * cos_theta);
1235  float phi= float(2*M_PI) * rng.sample();
1236 
1237  // construit les composantes de la direction l et transforme dans le repere de la scene
1238  l= world( Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta) );
1239 
1240  // evalue la pdf
1241  pdf= 1 / float(2*M_PI);
1242  }
1243  #else
1244  {
1245  // genere une direction p(l)= cos / (pi)
1246  float cos_theta= std::sqrt( rng.sample() );
1247  float sin_theta= std::sqrt(1 - cos_theta * cos_theta);
1248  float phi= float(2*M_PI) * rng.sample();
1249 
1250  // construit les composantes de la direction l et transforme dans le repere de la scene
1251  l= world( Vector(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta, cos_theta) );
1252 
1253  // evalue la pdf
1254  pdf= cos_theta / float(M_PI);
1255  }
1256  #endif
1257 
1258  // verifie la visibilité de la lumière / du ciel dans la direction l
1259  Color sample;
1260  if(!bvh.intersect( Ray(p + epsilon*pn, l)))
1261  {
1262  // V(p, l) = 1
1263  // il ne reste plus qu'à évaluer les autres termes...
1264 
1265  Color Li= Color(1);
1266  float cos_theta= std::max(float(0), dot( normalize(pn), normalize(l) ));
1267  sample= diffuse * Li * cos_theta / pdf;
1268  }
1269 
1270  // accumule le sample,
1271  // cf https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
1272  n++;
1273  Color Mn1= M;
1274  M= Mn1 + (sample - Mn1) / float(n);
1275  M2= M2 + (sample - Mn1) * (sample - M);
1276  }
1277  }
1278  }
1279 
1280  emission= emission / float(AA);
1281 
1282  //~ color= color / float(N);
1283  //~ color= color / float(AA);
1284  //~ image(px, py)= Color(color + emission, 1);
1285 
1286  image(px, py)= Color(M2 / float(n), 1);
1287  //~ image(px, py)= Color(M + emission, 1);
1288  }
1289  }
1290 
1291  return image;
1292 }
1293 
1294 int main( int argc, char **argv )
1295 {
1296  const char *filename= "cornell.gltf";
1297  const char *orbiter_filename= "orbiter.txt";
1298  if(argc > 1) filename= argv[1];
1299  if(argc > 2) orbiter_filename= argv[2];
1300 
1301  Orbiter camera;
1302  if(camera.read_orbiter(orbiter_filename) < 0)
1303  return 1; // pas de camera, pas d'image
1304 
1305  GLTFScene scene= read_gltf_scene(filename);
1306  if(scene.meshes.size() == 0)
1307  return 1; // pas de geometrie, pas d'image
1308 
1309  //
1310  BVH bvh;
1311  std::vector<Source> sources;
1312  {
1313  std::vector<Triangle> triangles;
1314 
1315  for(unsigned node_id= 0; node_id < scene.nodes.size(); node_id++)
1316  {
1317  const GLTFNode& node= scene.nodes[node_id];
1318 
1319  const Transform& model= node.model;
1320  int mesh_id= node.mesh_index;
1321 
1322  const GLTFMesh& mesh= scene.meshes[mesh_id];
1323  for(unsigned primitive_id= 0; primitive_id < mesh.primitives.size(); primitive_id++)
1324  {
1325  const GLTFPrimitives& primitives= mesh.primitives[primitive_id];
1326 
1327  assert(primitives.material_index != -1);
1328  const GLTFMaterial& material= scene.materials[primitives.material_index];
1329 
1330  bool emission= (material.emission.max() > 0);
1331  for(unsigned i= 0; i +2 < primitives.indices.size(); i+= 3)
1332  {
1333  // transforme les sommets dans le repere de la scene
1334  Point a= model( Point(primitives.positions[primitives.indices[i]]) );
1335  Point b= model( Point(primitives.positions[primitives.indices[i+1]]) );
1336  Point c= model( Point(primitives.positions[primitives.indices[i+2]]) );
1337 
1338  // verifie que le triangle n'est pas degenere... oui ca arrive... sur robot.gltf, par exemple...
1339  float area= length( cross( Vector(a, b), Vector(a, c) ) );
1340  if(area > 0)
1341  {
1342  // indice du premier sommet du triangle dans la primitive
1343  triangles.push_back( Triangle(a, b, c, node_id, mesh_id, primitive_id, i) );
1344 
1345  // si le triangle emet de la lumiere, ajouter une source
1346  if(emission)
1347  sources.push_back( Source(a, b, c, material.emission) );
1348  }
1349  }
1350  }
1351  }
1352 
1353  //~ assert(sources.size()); // pas de lumiere, pas d'image
1354  assert(triangles.size()); // pas de triangles, pas d'image
1355 
1356  // construit le bvh avec les triangles
1357  bvh.build(triangles);
1358  }
1359 
1360 
1361  // c'est parti !!
1362  char tmp[1024];
1363  //~ for(int n : { 1, 4, 16, 64 })
1364  //~ for(int n : { 1, 4, 16, 64, 256, 1024, 4096 })
1365  //~ for(int n : { 4096 })
1366  for(int n : { 16384 })
1367  //~ for(int n : { 256, 1024 })
1368  {
1369  //~ Image image= indirect(camera, scene, bvh, n, sources);
1370  Image image= indirect(camera, scene, bvh, n);
1371  //~ Image image= direct(camera, scene, bvh, n, sources);
1372  //~ Image image= direct(camera, scene, bvh, n);
1373  //~ Image image= ao(camera, scene, bvh, n, sources);
1374 
1375  sprintf(tmp, "cornell_direct-%04d.hdr", n); // direct explicite
1376  //~ sprintf(tmp, "cornell_indirect1-%04d.hdr", n); // direct explicite
1377  //~ sprintf(tmp, "cornell_indirect0-%04d.hdr", n); // direct implicite
1378 
1379  //~ sprintf(tmp, "cornell_sources_var-%04d.hdr", n);
1380  //~ sprintf(tmp, "emission_sources-%04d.hdr", n);
1381  //~ sprintf(tmp, "ao_uniform-%04d.hdr", n);
1382  //~ sprintf(tmp, "ao_uniform_var-%04d.hdr", n);
1383  //~ sprintf(tmp, "ao_cos_var-%04d.hdr", n);
1384  //~ sprintf(tmp, "directions-%04d.hdr", n);
1385  //~ sprintf(tmp, "emission_directions_uniform_var-%04d.hdr", n);
1386  //~ sprintf(tmp, "emission_directions_cos_var-%04d.hdr", n);
1387  //~ sprintf(tmp, "emission_sources_var-%04d.hdr", n);
1388  //~ sprintf(tmp, "indirectM2-%04d.hdr", n);
1389  //~ sprintf(tmp, "indirectM21-%04d.hdr", n);
1390  write_image_hdr(image, tmp);
1391  }
1392 
1393  return 0;
1394 }
representation d'une image.
Definition: image.h:21
GLenum primitives() const
renvoie le type de primitives.
Definition: mesh.h:324
representation de la camera, type orbiter, placee sur une sphere autour du centre de l'objet.
Definition: orbiter.h:17
int read_orbiter(const char *filename)
relit la position de l'orbiter depuis un fichier texte.
Definition: orbiter.cpp:116
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
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
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
Color Black()
utilitaire. renvoie une couleur noire.
Definition: color.cpp:31
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 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 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
Vector normalize(const Vector &v)
renvoie un vecteur unitaire / longueur == 1.
Definition: vec.cpp:123
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
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
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.