gKit2 light
tuto_englobant.cpp
Go to the documentation of this file.
1 
3 
4 #include <algorithm>
5 #include <vector>
6 #include <cfloat>
7 #include <chrono>
8 
9 #include "vec.h"
10 #include "mat.h"
11 #include "color.h"
12 #include "image.h"
13 #include "image_io.h"
14 #include "image_hdr.h"
15 #include "orbiter.h"
16 #include "mesh.h"
17 #include "wavefront.h"
18 
19 
20 struct RayHit
21 {
22  Point o; // origine
23  float t; // p(t)= o + td, position du point d'intersection sur le rayon
24  Vector d; // direction
25  int triangle_id; // indice du triangle dans le mesh
26  float u, v;
27  int x, y;
28 
29  RayHit( const Point& _o, const Point& _e ) : o(_o), t(1), d(Vector(_o, _e)), triangle_id(-1), u(), v(), x(), y() {}
30  RayHit( const Point& _o, const Point& _e, const int _x, const int _y ) : o(_o), t(1), d(Vector(_o, _e)), triangle_id(-1), u(), v(), x(_x), y(_y) {}
31  operator bool ( ) { return (triangle_id != -1); }
32 };
33 
34 
35 struct BBox
36 {
37  Point pmin, pmax;
38 
39  BBox( ) : pmin(), pmax() {}
40 
41  BBox( const Point& p ) : pmin(p), pmax(p) {}
42  BBox( const BBox& box ) : pmin(box.pmin), pmax(box.pmax) {}
43 
44  BBox& insert( const Point& p ) { pmin= min(pmin, p); pmax= max(pmax, p); return *this; }
45  BBox& insert( const BBox& box ) { pmin= min(pmin, box.pmin); pmax= max(pmax, box.pmax); return *this; }
46 
47  float centroid( const int axis ) const { return (pmin(axis) + pmax(axis)) / 2; }
48 
49  bool intersect( const RayHit& ray ) const
50  {
51  Vector invd= Vector(1 / ray.d.x, 1 / ray.d.y, 1 / ray.d.z);
52  return intersect(ray, invd);
53  }
54 
55  bool intersect( const RayHit& ray, const Vector& invd ) const
56  {
57  Point rmin= pmin;
58  Point rmax= pmax;
59  if(ray.d.x < 0) std::swap(rmin.x, rmax.x);
60  if(ray.d.y < 0) std::swap(rmin.y, rmax.y);
61  if(ray.d.z < 0) std::swap(rmin.z, rmax.z);
62  Vector dmin= (rmin - ray.o) * invd;
63  Vector dmax= (rmax - ray.o) * invd;
64 
65  float tmin= std::max(dmin.z, std::max(dmin.y, std::max(dmin.x, 0.f)));
66  float tmax= std::min(dmax.z, std::min(dmax.y, std::min(dmax.x, ray.t)));
67  return (tmin <= tmax);
68  }
69 };
70 
71 
72 struct Triangle
73 {
74  Point p; // sommet a du triangle
75  Vector e1, e2; // aretes ab, ac du triangle
76  int id;
77 
78  Triangle( const TriangleData& data, const int _id ) : p(data.a), e1(Vector(data.a, data.b)), e2(Vector(data.a, data.c)), id(_id) {}
79 
80  /* calcule l'intersection ray/triangle
81  cf "fast, minimum storage ray-triangle intersection"
82 
83  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.)
84  renvoie vrai + les coordonnees barycentriques (u, v) du point d'intersection + sa position le long du rayon (t).
85  convention barycentrique : p(u, v)= (1 - u - v) * a + u * b + v * c
86  */
87  void intersect( RayHit &ray ) const
88  {
89  Vector pvec= cross(ray.d, e2);
90  float det= dot(e1, pvec);
91 
92  float inv_det= 1 / det;
93  Vector tvec(p, ray.o);
94 
95  float u= dot(tvec, pvec) * inv_det;
96  if(u < 0 || u > 1) return;
97 
98  Vector qvec= cross(tvec, e1);
99  float v= dot(ray.d, qvec) * inv_det;
100  if(v < 0 || u + v > 1) return;
101 
102  float t= dot(e2, qvec) * inv_det;
103  if(t < 0 || t > ray.t) return;
104 
105  // touche !!
106  ray.t= t;
107  ray.triangle_id= id;
108  ray.u= u;
109  ray.v= v;
110  }
111 
112  BBox bounds( ) const
113  {
114  BBox box(p);
115  return box.insert(p+e1).insert(p+e2);
116  }
117 };
118 
119 
120 
121 void direct(
122  const std::vector<Triangle>& triangles, const int tbegin, const int tend,
123  std::vector<RayHit>& rays, const int rbegin, const int rend )
124 {
125  for(int i= rbegin; i < rend; i++)
126  for(int k= tbegin; k < tend; k++)
127  triangles[k].intersect(rays[i]);
128 }
129 
130 struct triangle_less1
131 {
132  int axis;
133  float cut;
134 
135  triangle_less1( const int _axis, const float _cut ) : axis(_axis), cut(_cut) {}
136 
137  bool operator() ( const Triangle& triangle ) const
138  {
139  // re-construit l'englobant du triangle
140  BBox bounds= triangle.bounds();
141  return bounds.centroid(axis) < cut;
142  }
143 };
144 
145 struct ray_less1
146 {
147  BBox bounds;
148 
149  ray_less1( const BBox& _bounds ) : bounds(_bounds) {}
150 
151  bool operator() ( const RayHit& ray ) const
152  {
153  return bounds.intersect(ray);
154  }
155 };
156 
157 
158 void divide( const BBox& bounds,
159  std::vector<Triangle>& triangles, const int tbegin, const int tend,
160  std::vector<RayHit>& rays, const int rbegin, const int rend )
161 {
162  if(tbegin == tend || rbegin == rend)
163  // plus de triangles ou de rayons, rien a faire...
164  return;
165 
166  // il ne reste plus que quelques triangles, finir les calculs d'intersection...
167  if(tend - tbegin <= 4)
168  {
169  direct(triangles, tbegin, tend, rays, rbegin, rend);
170  return;
171  }
172 
173  // axe le plus etire de l'englobant
174  Vector d= Vector(bounds.pmin, bounds.pmax);
175  int axis;
176  if(d.x > d.y && d.x > d.z) // x plus grand que y et z ?
177  axis= 0;
178  else if(d.y > d.z) // y plus grand que z ? (et que x implicitement)
179  axis= 1;
180  else // x et y ne sont pas les plus grands...
181  axis= 2;
182 
183  // coupe l'englobant au milieu
184  float cut= bounds.centroid(axis);
185 
186  // repartit les triangles
187  Triangle *pm= std::partition(triangles.data() + tbegin, triangles.data() + tend, triangle_less1(axis, cut));
188  int m= std::distance(triangles.data(), pm);
189 
190  // la repartition des triangles peut echouer, et tous les triangles sont dans la meme partie...
191  // forcer quand meme un decoupage en 2 ensembles
192  if(m == tbegin || m == tend)
193  m= (tbegin + tend) / 2;
194  assert(m != tbegin);
195  assert(m != tend);
196 
197  // construit les englobants
198  BBox left= triangles[tbegin].bounds();
199  for(int i= tbegin+1; i < m; i++)
200  left.insert(triangles[i].bounds());
201 
202  // repartit les rayons
203  RayHit *prleft= std::partition(rays.data() + rbegin, rays.data() + rend, ray_less1(left));
204  int rleft= std::distance(rays.data(), prleft);
205 
206  divide(left, triangles, tbegin, m, rays, rbegin, rleft);
207 
208  // on recommence pour la droite
209  BBox right= triangles[m].bounds();
210  for(int i= m+1; i < tend; i++)
211  right.insert(triangles[i].bounds());
212 
213  RayHit *prright= std::partition(rays.data() + rbegin, rays.data() + rend, ray_less1(right));
214  int rright= std::distance(rays.data(), prright);
215 
216  divide(right, triangles, m, tend, rays, rbegin, rright);
217 }
218 
219 
220 int main( const int argc, const char **argv )
221 {
222  const char *mesh_filename= "data/cornell.obj";
223  if(argc > 1)
224  mesh_filename= argv[1];
225 
226  const char *orbiter_filename= "data/cornell_orbiter.txt";
227  if(argc > 2)
228  orbiter_filename= argv[2];
229 
230  Orbiter camera;
231  if(camera.read_orbiter(orbiter_filename) < 0)
232  return 1;
233 
234  Mesh mesh= read_mesh(mesh_filename);
235  BBox bounds;
236  mesh.bounds(bounds.pmin, bounds.pmax);
237 
238  // recupere les triangles
239  std::vector<Triangle> triangles;
240  {
241  int n= mesh.triangle_count();
242  for(int i= 0; i < n; i++)
243  triangles.emplace_back(mesh.triangle(i), i);
244  }
245 
246  Image image(1024, 768);
247 
248  // recupere les transformations
249  camera.projection(image.width(), image.height(), 45);
250  Transform model= Identity();
251  Transform view= camera.view();
252  Transform projection= camera.projection();
253  Transform viewport= camera.viewport();
254  Transform inv= Inverse(viewport * projection * view * model);
255 
256  // genere un rayon par pixel de l'image
257  std::vector<RayHit> rays;
258  for(int y= 0; y < image.height(); y++)
259  for(int x= 0; x < image.width(); x++)
260  {
261  // generer le rayon
262  Point origine= inv(Point(x + .5f, y + .5f, 0));
263  Point extremite= inv(Point(x + .5f, y + .5f, 1));
264 
265  rays.emplace_back(origine, extremite, x, y);
266  }
267 
268 // mesure les temps d'execution
269 #if 0
270  {
271  auto start= std::chrono::high_resolution_clock::now();
272  direct(triangles, 0, int(triangles.size()), rays, 0, int(rays.size()));
273 
274  auto stop= std::chrono::high_resolution_clock::now();
275  int cpu= std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
276  printf("direct %dms\n", cpu);
277  }
278 #endif
279 
280  {
281  auto start= std::chrono::high_resolution_clock::now();
282  divide(bounds, triangles, 0, int(triangles.size()), rays, 0, int(rays.size()));
283 
284  auto stop= std::chrono::high_resolution_clock::now();
285  int cpu= std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
286  printf("divide %dms\n", cpu);
287  }
288 
289  // reconstruit l'image
290  for(int i= 0; i < rays.size(); i++)
291  {
292  if(rays[i])
293  {
294  int x= rays[i].x;
295  int y= rays[i].y;
296  float u= rays[i].u;
297  float v= rays[i].v;
298  float w= 1 - u - v;
299  image(x, y)= Color(w, u, v);
300  }
301  }
302  write_image(image, "divide.png");
303 
304 
305  return 0;
306 }
representation d'une image.
Definition: image.h:21
representation d'un objet / maillage.
Definition: mesh.h:112
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:501
Mesh & triangle(const unsigned int a, const unsigned int b, const unsigned int c)
Definition: mesh.cpp:190
int triangle_count() const
renvoie le nombre de triangles.
Definition: mesh.cpp:433
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
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
int write_image(const Image &image, const char *filename)
enregistre une image dans un fichier png.
Definition: image_io.cpp:85
Transform Inverse(const Transform &m)
renvoie l'inverse de la matrice.
Definition: mat.cpp:197
Point max(const Point &a, const Point &b)
renvoie la plus grande composante de chaque point. x, y, z= max(a.x, b.x), max(a.y,...
Definition: vec.cpp:35
Transform Identity()
construit la transformation identite.
Definition: mat.cpp:187
float distance(const Point &a, const Point &b)
renvoie la distance etre 2 points.
Definition: vec.cpp:14
Point min(const Point &a, const Point &b)
renvoie la plus petite composante de chaque point. x, y, z= min(a.x, b.x), min(a.y,...
Definition: vec.cpp:30
float dot(const Vector &u, const Vector &v)
renvoie le produit scalaire de 2 vecteurs.
Definition: vec.cpp:137
Vector cross(const Vector &u, const Vector &v)
renvoie le produit vectoriel de 2 vecteurs.
Definition: vec.cpp:129
Mesh read_mesh(const char *filename)
charge un fichier wavefront .obj et renvoie un mesh compose de triangles non indexes....
Definition: wavefront.cpp:14
void bounds(const MeshData &data, Point &pmin, Point &pmax)
renvoie l'englobant.
Definition: mesh_data.cpp:290
boite englobante.
Definition: tuto_bvh.cpp:47
representation d'une couleur (rgba) transparente ou opaque.
Definition: color.h:14
representation d'un point 3d.
Definition: vec.h:21
representation d'une transformation, une matrice 4x4, organisee par ligne / row major.
Definition: mat.h:21
representation d'un triangle.
Definition: mesh.h:95
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