gKit2 light
tuto_is.cpp
1 
2 #include <cfloat>
3 #include <cmath>
4 
5 #include "app.h"
6 
7 #include "vec.h"
8 #include "color.h"
9 #include "mat.h"
10 
11 #include "mesh.h"
12 #include "wavefront.h"
13 #include "draw.h"
14 
15 #include "image.h"
16 #include "image_io.h"
17 #include "image_hdr.h"
18 
19 #include "program.h"
20 #include "uniforms.h"
21 #include "texture.h"
22 
23 #include "orbiter.h"
24 
25 #define EPSILON 0.00001f
26 
27 
28 struct Ray
29 {
30  Point o;
31  Vector d;
32  float tmax;
33 
34  Ray( const Point origine, const Point extremite ) : o(origine), d(Vector(origine, extremite)), tmax(1) {}
35  Ray( const Point origine, const Vector direction ) : o(origine), d(direction), tmax(FLT_MAX) {}
36 
37  Point operator( ) ( const float t ) const { return o + t * d; }
38 };
39 
40 struct Hit
41 {
42  Point p;
43  Vector n;
44  float t, u, v;
45  int object_id;
46 
47  Hit( ) : p(), n(), t(FLT_MAX), u(0), v(0), object_id(-1) {}
48 };
49 
50 struct Triangle : public TriangleData
51 {
52  Triangle( ) : TriangleData() {}
53  Triangle( const TriangleData& data ) : TriangleData(data) {}
54 
55  /* calcule l'intersection ray/triangle
56  cf "fast, minimum storage ray-triangle intersection"
57  http://www.graphics.cornell.edu/pubs/1997/MT97.pdf
58 
59  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 htmax] du rayon. \n
60  renvoie vrai + les coordonnees barycentriques (ru, rv) du point d'intersection + sa position le long du rayon (rt). \n
61  convention barycentrique : t(u, v)= (1 - u - v) * a + u * b + v * c \n
62  */
63  bool intersect( const Ray &ray, const float htmax, float &rt, float &ru, float&rv ) const
64  {
65  /* begin calculating determinant - also used to calculate U parameter */
66  Vector ac= Vector(Point(a), Point(c));
67  Vector pvec= cross(ray.d, ac);
68 
69  /* if determinant is near zero, ray lies in plane of triangle */
70  Vector ab= Vector(Point(a), Point(b));
71  float det= dot(ab, pvec);
72  if(det > -EPSILON && det < EPSILON)
73  return false;
74 
75  float inv_det= 1.0f / det;
76 
77  /* calculate distance from vert0 to ray origin */
78  Vector tvec(Point(a), ray.o);
79 
80  /* calculate U parameter and test bounds */
81  float u= dot(tvec, pvec) * inv_det;
82  if(u < 0.0f || u > 1.0f)
83  return false;
84 
85  /* prepare to test V parameter */
86  Vector qvec= cross(tvec, ab);
87 
88  /* calculate V parameter and test bounds */
89  float v= dot(ray.d, qvec) * inv_det;
90  if(v < 0.0f || u + v > 1.0f)
91  return false;
92 
93  /* calculate t, ray intersects triangle */
94  rt= dot(ac, qvec) * inv_det;
95  ru= u;
96  rv= v;
97 
98  // ne renvoie vrai que si l'intersection est valide (comprise entre tmin et tmax du rayon)
99  return (rt < htmax && rt > EPSILON);
100  }
101 
104  Point point( const float u, const float v ) const
105  {
106  float w= 1.f - u - v;
107  return Point(Vector(a) * w + Vector(b) * u + Vector(c) * v);
108  }
109 
112  Vector normal( const float u, const float v ) const
113  {
114  float w= 1.f - u - v;
115  return Vector(na) * w + Vector(nb) * u + Vector(nc) * v;
116  }
117 };
118 
119 
120 //
121 struct Source : public Triangle
122 {
123  Color emission;
124 
125  Source( ) : Triangle(), emission() {}
126  Source( const TriangleData& data, const Color& color ) : Triangle(data), emission(color) {}
127 };
128 
129 
130 // construit un repere ortho tbn, a partir d'un seul vecteur...
131 // cf "generating a consistently oriented tangent space"
132 // http://people.compute.dtu.dk/jerf/papers/abstracts/onb.html
133 struct World
134 {
135  World( const Vector& _n ) : n(_n)
136  {
137  if(n.z < -0.9999999f)
138  {
139  t= Vector(0, -1, 0);
140  b= Vector(-1, 0, 0);
141  }
142  else
143  {
144  float a= 1.f / (1.f + n.z);
145  float d= -n.x * n.y * a;
146  t= Vector(1.f - n.x * n.x * a, d, -n.x);
147  b= Vector(d, 1.f - n.y * n.y * a, -n.y);
148  }
149  }
150 
151  Vector operator( ) ( const Vector& local ) const
152  {
153  return local.x * t + local.y * b + local.z * n;
154  }
155 
156  Vector t;
157  Vector b;
158  Vector n;
159 };
160 
161 
162 GLuint make_texture( const int unit, const int width, const int height )
163 {
164  GLuint texture;
165  glGenTextures(1, &texture);
166  glActiveTexture(GL_TEXTURE0 + unit);
167  glBindTexture(GL_TEXTURE_2D, texture);
168 
169  glTexImage2D(GL_TEXTURE_2D, 0,
170  GL_RGB32F, width, height, 0,
171  GL_RGBA, GL_FLOAT, NULL);
172 
173  // fixe les parametres de filtrage par defaut
174  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
175  glGenerateMipmap(GL_TEXTURE_2D);
176  return texture;
177 }
178 
179 
180 struct IS : public App
181 {
182  // constructeur : donner les dimensions de l'image, et eventuellement la version d'openGL.
183  IS( const char *filename ) : App(1024, 640)
184  {
185  m_mesh= read_mesh(filename);
186  if(m_mesh == Mesh::error())
187  return;
188 
189  build_sources();
190  build_triangles();
191 
192  if(m_camera.read_orbiter("orbiter.txt") < 0)
193  {
194  Point pmin, pmax;
195  m_mesh.bounds(pmin, pmax);
196  m_camera.lookat(pmin, pmax);
197  }
198  }
199 
200  int init( )
201  {
202  if(m_mesh == Mesh::error())
203  return -1;
204 
205  m_ptexture= make_texture(0, window_width(), window_height());
206  m_ntexture= make_texture(1, window_width(), window_height());
207  m_vtexture= make_texture(2, window_width(), window_height());
208 
209  m_hitp= Image(window_width(), window_height());
210  m_hitn= Image(window_width(), window_height());
211  m_hitv= Image(window_width(), window_height());
212 
213  glGenVertexArrays(1, &m_vao);
214  glBindVertexArray(m_vao);
215 
216  m_program= read_program("tutos/M2/is.glsl");
217  program_print_errors(m_program);
218 
219  // etat openGL par defaut
220  glClearColor(0.2f, 0.2f, 0.2f, 1.f); // couleur par defaut de la fenetre
221 
222  glClearDepth(1.f); // profondeur par defaut
223  glDepthFunc(GL_LESS); // ztest, conserver l'intersection la plus proche de la camera
224  glEnable(GL_DEPTH_TEST); // activer le ztest
225 
226  return 0;
227  }
228 
229  int render( )
230  {
231  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
232 
233  if(key_state('r'))
234  {
235  clear_key_state('r');
236  reload_program(m_program, "tutos/M2/is.glsl");
237  program_print_errors(m_program);
238  }
239 
240  if(key_state('f'))
241  {
242  clear_key_state('f');
243  Point pmin, pmax;
244  m_mesh.bounds(pmin, pmax);
245  m_camera.lookat(pmin, pmax);
246  }
247 
248  // deplace la camera
249  int mx, my;
250  unsigned int mb= SDL_GetRelativeMouseState(&mx, &my);
251  if(mb & SDL_BUTTON(1)) // le bouton gauche est enfonce
252  m_camera.rotation(mx, my);
253  else if(mb & SDL_BUTTON(3)) // le bouton droit est enfonce
254  m_camera.move(mx);
255  else if(mb & SDL_BUTTON(2)) // le bouton du milieu est enfonce
256  m_camera.translation((float) mx / (float) window_width(), (float) my / (float) window_height());
257 
258  m_camera.projection(window_width(), window_height(), 45);
259 
260  static int mode= 0;
261  static vec2 texcoord= vec2(0, 0);
262  if(key_state(' '))
263  {
264  clear_key_state(' ');
265  mode= (mode +1) % 2;
266 
267  if(mode == 1)
268  {
269  int mx, my;
270  SDL_GetMouseState(&mx, &my);
271  vec2 pixel= vec2(mx, window_height() - my -1);
272  texcoord= vec2(pixel.x / m_hitp.width(), pixel.y / m_hitp.height());
273  printf("pixel %f %f\n", pixel.x, pixel.y);
274 
275  // go
276  Point d0;
277  Vector dx0, dy0;
278  m_camera.frame(0, d0, dx0, dy0);
279 
280  Point d1;
281  Vector dx1, dy1;
282  m_camera.frame(1, d1, dx1, dy1);
283 
284  // pixel
285  Point o= d0 + pixel.x*dx0 + pixel.y*dy0;
286  Point e= d1 + pixel.x*dx1 + pixel.y*dy1;
287 
288  Point point;
289  Vector normal;
290 
291  Ray ray(o, e);
292  Hit hit;
293  if(intersect(ray, hit))
294  {
295  point= hit.p;
296  normal= hit.n;
297 
298  // frame
299  #pragma omp parallel for schedule(dynamic, 16)
300  for(int y= 0; y < m_hitp.height(); y++)
301  for(int x= 0; x < m_hitp.width(); x++)
302  {
303  // clear
304  m_hitp(x, y)= Black();
305  m_hitn(x, y)= Black();
306  m_hitv(x, y)= Black();
307 
308  Point o= d0 + x*dx0 + y*dy0;
309  Point e= d1 + x*dx1 + y*dy1;
310 
311  Ray ray(o, e);
312  Hit hit;
313  if(intersect(ray, hit))
314  {
315  m_hitp(x, y)= Color(hit.p.x, hit.p.y, hit.p.z);
316  m_hitn(x, y)= Color(hit.n.x, hit.n.y, hit.n.z);
317 
318  Ray shadow(hit.p + hit.n * 0.001f, point + normal * 0.001f);
319  Hit shadow_hit;
320  int v= 1;
321  if(intersect(shadow, shadow_hit))
322  v= 0;
323 
324  m_hitv(x, y)= Color(v, v, v);
325  }
326  }
327 
328  // transferre les donnees
329  glActiveTexture(GL_TEXTURE0);
330  glBindTexture(GL_TEXTURE_2D, m_ptexture);
331  glTexSubImage2D(GL_TEXTURE_2D, 0,
332  0, 0, m_hitp.width(), m_hitp.height(),
333  GL_RGBA, GL_FLOAT, m_hitp.data());
334 
335  glActiveTexture(GL_TEXTURE0 +1);
336  glBindTexture(GL_TEXTURE_2D, m_ntexture);
337  glTexSubImage2D(GL_TEXTURE_2D, 0,
338  0, 0, m_hitn.width(), m_hitn.height(),
339  GL_RGBA, GL_FLOAT, m_hitn.data());
340 
341  glActiveTexture(GL_TEXTURE0 +2);
342  glBindTexture(GL_TEXTURE_2D, m_vtexture);
343  glTexSubImage2D(GL_TEXTURE_2D, 0,
344  0, 0, m_hitv.width(), m_hitv.height(),
345  GL_RGBA, GL_FLOAT, m_hitv.data());
346  }
347  }
348  }
349 
350  if(mode == 1)
351  {
352  glBindVertexArray(m_vao);
353  glUseProgram(m_program);
354 
355  program_uniform(m_program, "ptexture", 0);
356  program_uniform(m_program, "ntexture", 1);
357  program_uniform(m_program, "vtexture", 2);
358  program_uniform(m_program, "pixel", texcoord);
359  program_uniform(m_program, "mode", mode);
360 
361  glActiveTexture(GL_TEXTURE0);
362  glBindTexture(GL_TEXTURE_2D, m_ptexture);
363  glActiveTexture(GL_TEXTURE0 +1);
364  glBindTexture(GL_TEXTURE_2D, m_ntexture);
365  glActiveTexture(GL_TEXTURE0 +2);
366  glBindTexture(GL_TEXTURE_2D, m_vtexture);
367 
368  glDrawArrays(GL_TRIANGLES, 0, 3);
369  }
370 
371  else if(mode == 0)
372  draw(m_mesh, m_camera);
373 
374  //
375  if(key_state('s'))
376  {
377  clear_key_state('s');
378 
379  static int n= 1;
380  char tmp[1024];
381  sprintf(tmp, "screenshot%02d.png", n++);
382  printf("writing %s...\n", tmp);
383  screenshot(tmp);
384  }
385 
386  return 1;
387  }
388 
389  int quit( )
390  {
391  m_mesh.release();
392  return 0;
393  }
394 
395 
396  // recuperer les sources de lumiere du mesh : triangles associee a une matiere qui emet de la lumiere, material.emission != 0
397  int build_sources( )
398  {
399  for(int i= 0; i < m_mesh.triangle_count(); i++)
400  {
401  // recupere la matiere associee a chaque triangle de l'objet
402  Material material= m_mesh.triangle_material(i);
403 
404  if((material.emission.r + material.emission.g + material.emission.b) > 0)
405  // inserer la source de lumiere dans l'ensemble.
406  m_sources.push_back( Source(m_mesh.triangle(i), material.emission) );
407  }
408 
409  printf("%d sources.\n", (int) m_sources.size());
410  return (int) m_sources.size();
411  }
412 
413  bool direct( const Ray& ray )
414  {
415  for(size_t i= 0; i < m_sources.size(); i++)
416  {
417  float t, u, v;
418  if(m_sources[i].intersect(ray, ray.tmax, t, u, v))
419  return true;
420  }
421 
422  return false;
423  }
424 
425 
426  // recuperer les triangles du mesh
427  int build_triangles( )
428  {
429  for(int i= 0; i < m_mesh.triangle_count(); i++)
430  m_triangles.push_back( Triangle(m_mesh.triangle(i)) );
431 
432  printf("%d triangles.\n", (int) m_triangles.size());
433  return (int) m_triangles.size();
434  }
435 
436 
437  // calcule l'intersection d'un rayon et de tous les triangles
438  bool intersect( const Ray& ray, Hit& hit )
439  {
440  hit.t= ray.tmax;
441  for(size_t i= 0; i < m_triangles.size(); i++)
442  {
443  float t, u, v;
444  if(m_triangles[i].intersect(ray, hit.t, t, u, v))
445  {
446  hit.t= t;
447  hit.u= u;
448  hit.v= v;
449 
450  hit.p= ray(t); // evalue la positon du point d'intersection sur le rayon
451  hit.n= m_triangles[i].normal(u, v);
452 
453  hit.object_id= i; // permet de retrouver toutes les infos associees au triangle
454  }
455  }
456 
457  return (hit.object_id != -1);
458  }
459 
460 protected:
461  Mesh m_mesh;
462  Orbiter m_camera;
463 
464  std::vector<Triangle> m_triangles;
465  std::vector<Source> m_sources;
466 
467  Image m_hitp;
468  Image m_hitn;
469  Image m_hitv;
470 
471  GLuint m_vao;
472  GLuint m_program;
473 
474  GLuint m_ptexture;
475  GLuint m_ntexture;
476  GLuint m_vtexture;
477 };
478 
479 
480 int main( int argc, char **argv )
481 {
482  const char *filename= "cornell.obj";
483  if(argc > 1)
484  filename= argv[1];
485 
486  IS app(filename);
487  app.run();
488 
489  return 0;
490 }
classe application.
Definition: app.h:20
App(const int width, const int height, const int major=3, const int minor=3, const int samples=0)
constructeur, dimensions de la fenetre et version d'openGL.
Definition: app.cpp:11
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
static Mesh & error()
Definition: mesh.h:348
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
const Material & triangle_material(const unsigned int id) const
renvoie la matiere d'un triangle.
Definition: mesh.cpp:296
void release()
detruit les objets openGL.
Definition: mesh.cpp:62
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
void frame(const float z, Point &dO, Vector &dx, Vector &dy) const
Definition: orbiter.cpp:89
int read_orbiter(const char *filename)
relit la position de l'orbiter depuis un fichier texte.
Definition: orbiter.cpp:116
void move(const float z)
rapproche / eloigne la camera du centre.
Definition: orbiter.cpp:33
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
void translation(const float x, const float y)
deplace le centre / le point observe.
Definition: orbiter.cpp:27
void rotation(const float x, const float y)
change le point de vue / la direction d'observation.
Definition: orbiter.cpp:21
int window_height()
renvoie la hauteur de la fenetre de l'application.
Definition: window.cpp:29
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
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
int window_width()
renvoie la largeur de la fenetre de l'application.
Definition: window.cpp:25
Color Black()
utilitaire. renvoie une couleur noire.
Definition: color.cpp:31
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
GLuint make_texture(const int unit, const int width, const int height, const GLenum texel_type, const GLenum data_format, const GLenum data_type)
creation de textures filtrables / mipmaps
Definition: texture.cpp:25
int screenshot(const char *filename)
enregistre le contenu de la fenetre dans un fichier. doit etre de type .png / .bmp
Definition: texture.cpp:194
GLuint read_program(const char *filename, const char *definitions)
Definition: program.cpp:204
void program_uniform(const GLuint program, const char *uniform, const std::vector< unsigned > &v)
affecte un tableau de valeurs a un uniform du shader program.
Definition: uniforms.cpp:94
int program_print_errors(const GLuint program)
affiche les erreurs de compilation.
Definition: program.cpp:432
representation d'une couleur (rgba) transparente ou opaque.
Definition: color.h:14
intersection avec un triangle.
Definition: tuto_bvh2.cpp:33
Definition: tuto_is.cpp:181
int render()
a deriver pour afficher les objets. renvoie 1 pour continuer, 0 pour fermer l'application.
Definition: tuto_is.cpp:229
int init()
a deriver pour creer les objets openGL. renvoie -1 pour indiquer une erreur, 0 sinon.
Definition: tuto_is.cpp:200
int quit()
a deriver pour detruire les objets openGL. renvoie -1 pour indiquer une erreur, 0 sinon.
Definition: tuto_is.cpp:389
Color emission
pour une source de lumiere.
Definition: materials.h:19
representation d'un point 3d.
Definition: vec.h:21
rayon.
Definition: tuto_bvh2.cpp:20
representation d'un triangle.
Definition: mesh.h:95
vec3 c
positions
Definition: mesh.h:96
vec3 nc
normales
Definition: mesh.h:97
triangle pour le bvh, cf fonction bounds() et intersect().
Definition: tuto_bvh.cpp:84
Point point(const float u, const float v) const
Definition: tuto_is.cpp:104
Vector normal(const float u, const float v) const
Definition: tuto_is.cpp:112
representation d'un vecteur 3d.
Definition: vec.h:59
vecteur generique, utilitaire.
Definition: vec.h:131