gKit2 light
tuto5GL_cubemap.cpp
Go to the documentation of this file.
1 
3 
4 #include <stdio.h>
5 
6 #include "window.h"
7 #include "vec.h"
8 #include "mat.h"
9 
10 #include "program.h"
11 #include "uniforms.h"
12 #include "texture.h"
13 
14 #include "mesh.h"
15 #include "wavefront.h"
16 
17 #include "orbiter.h"
18 #include "image_io.h"
19 
20 
21 GLuint program;
22 GLuint program_cubemap;
23 
24 GLuint texture;
25 
26 GLuint vao;
27 GLuint vao_null;
28 GLuint vertex_buffer;
29 GLuint normal_buffer;
30 int vertex_count;
31 
32 Orbiter camera;
33 
34 
35 int init( )
36 {
37  // etape 1 : shaders
38  // . dessiner le reflet de la cubemap sur un objet
39  program= read_program("tutos/tuto5GL_cubemap.glsl");
40  program_print_errors(program);
41 
42  // . dessiner la cubemap a l'infini
43  program_cubemap= read_program("tutos/cubemap.glsl");
44  program_print_errors(program_cubemap);
45 
46  // etape 2 : charger un mesh, (avec des normales), vao + vertex buffer
47  //~ Mesh mesh= read_mesh("data/bigguy.obj");
48  Mesh mesh= read_mesh("data/cube.obj");
49  if(mesh.vertex_count() == 0)
50  return -1; // gros probleme, pas de sommets...
51 
52  vertex_count= mesh.vertex_count();
53 
54  Point pmin, pmax;
55  mesh.bounds(pmin, pmax);
56  camera.lookat(pmin, pmax);
57 
58  // vertex format : position + normal,
59  glGenVertexArrays(1, &vao);
60  glBindVertexArray(vao);
61 
62  // vertex buffer
63  glGenBuffers(1, &vertex_buffer);
64  glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
65  glBufferData(GL_ARRAY_BUFFER, mesh.vertex_buffer_size(), mesh.vertex_buffer(), GL_STATIC_DRAW);
66 
67  // configurer l'attribut position, cf declaration dans le vertex shader : in vec3 position;
68  GLint position= glGetAttribLocation(program, "position");
69  if(position >= 0)
70  {
71  glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 0, 0);
72  glEnableVertexAttribArray(position);
73  }
74 
75  // normal buffer
76  if(!mesh.normal_buffer_size())
77  {
78  printf("[oops] pas de normales...\n");
79  return -1;
80  }
81 
82  glGenBuffers(1, &normal_buffer);
83  glBindBuffer(GL_ARRAY_BUFFER, normal_buffer);
84  glBufferData(GL_ARRAY_BUFFER, mesh.normal_buffer_size(), mesh.normal_buffer(), GL_STATIC_DRAW);
85 
86  GLint normal= glGetAttribLocation(program, "normal");
87  if(normal >= 0)
88  {
89  glVertexAttribPointer(normal, 3, GL_FLOAT, GL_FALSE, 0, 0);
90  glEnableVertexAttribArray(normal);
91  }
92 
93  // nettoyage
94  mesh.release();
95  glBindVertexArray(0);
96  glBindBuffer(GL_ARRAY_BUFFER, 0);
97 
98  // etape 3 : texture
99 #if 0
100  ImageData tmp= read_image_data("data/debug2x2red.png");
101  //~ ImageData image= flipX(flipY(tmp));
102  ImageData image= flipY(tmp);
103 
104  // solution 1, utiliser une seule texture *carree* et la copier sur les 6 faces de la cubemap
105  int size= image.width;
106 
107  GLenum data_format;
108  GLenum data_type= GL_UNSIGNED_BYTE;
109  if(image.channels == 3)
110  data_format= GL_RGB;
111  else // par defaut
112  data_format= GL_RGBA;
113 
114  // creer la texture
115  glGenTextures(1, &texture);
116  glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
117 
118  // creer les 6 faces
119  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0,
120  GL_RGBA, size, size, 0,
121  data_format, data_type, image.data());
122 
123  glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0,
124  GL_RGBA, size, size, 0,
125  data_format, data_type, image.data());
126 
127  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0,
128  GL_RGBA, size, size, 0,
129  data_format, data_type, image.data());
130 
131  glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0,
132  GL_RGBA, size, size, 0,
133  data_format, data_type, image.data());
134 
135  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0,
136  GL_RGBA, size, size, 0,
137  data_format, data_type, image.data());
138 
139  glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0,
140  GL_RGBA, size, size, 0,
141  data_format, data_type, image.data());
142 #endif
143 
144 #if 0
145  // les 6 faces sur une bande
146  ImageData image= read_image_data("tutos/cubemap_debug.png");
147 
148  // les 6 images sont regroupees sur une bande horizontale.
149  int size= image.width / 6;
150 
151  GLenum data_format;
152  GLenum data_type= GL_UNSIGNED_BYTE;
153  if(image.channels == 3)
154  data_format= GL_RGB;
155  else // par defaut
156  data_format= GL_RGBA;
157 
158  // creer la texture
159  glGenTextures(1, &texture);
160  glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
161 
162  // creer les 6 faces
163  // chaque face de la cubemap est un bloc [image.width/6 x image.height] dans l'image originale
164  int faces[]= { 0, 1, 2, 3, 4, 5 };
165 
166  // largeur totale de l'image
167  for(int i= 0; i < 6; i++)
168  {
169  // extrait la face
170  ImageData face= flipX(flipY(copy(image, faces[i]*size, 0, size, size)));
171  //~ ImageData face= copy(image, faces[i]*size, 0, size, size);
172 
173  // transferer les pixels
174  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X +i, 0,
175  GL_RGBA, size, size, 0,
176  data_format, data_type, face.data());
177  }
178 #endif
179 
180 #if 1
181 // http://paulbourke.net/miscellaneous/cubemaps/
182 
183  // les 6 faces sur une croix
184  ImageData image= read_image_data("canyon2.jpg");
185  //~ ImageData image= read_image_data("html/cubemap_debug_cross.png");
186 
187  int w= image.width / 4;
188  int h= image.height / 3;
189  assert(w == h);
190 
191  GLenum data_format;
192  GLenum data_type= GL_UNSIGNED_BYTE;
193  if(image.channels == 3)
194  data_format= GL_RGB;
195  else // par defaut
196  data_format= GL_RGBA;
197 
198  // creer la texture
199  glGenTextures(1, &texture);
200  glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
201 
202  // creer les 6 faces
203  // chaque face de la cubemap est un rectangle [image.width/4 x image.height/3] dans l'image originale
204  struct { int x, y; } faces[]= {
205  {0, 1}, // X+
206  {2, 1}, // X-
207  {1, 2}, // Y+
208  {1, 0}, // Y-
209  {1, 1}, // Z+
210  {3, 1}, // Z-
211  };
212 
213  for(int i= 0; i < 6; i++)
214  {
215  ImageData face= flipX(flipY(copy(image, faces[i].x*w, faces[i].y*h, w, h)));
216  //~ ImageData face= copy(image, faces[i].x*w, faces[i].y*h, w, h);
217 
218  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X +i, 0,
219  GL_RGBA, w, h, 0,
220  data_format, data_type, face.data());
221  }
222 #endif
223 
224 #if 0
225  // 6 images
226  const char *filenames[]= {
227  "data/cubemap/cubemap_opensea/opensea_posx.png",
228  "data/cubemap/cubemap_opensea/opensea_negx.png",
229  "data/cubemap/cubemap_opensea/opensea_posy.png",
230  "data/cubemap/cubemap_opensea/opensea_negy.png",
231  "data/cubemap/cubemap_opensea/opensea_posz.png",
232  "data/cubemap/cubemap_opensea/opensea_negz.png"
233  //~ "data/cubemap/skybox/left.jpg",
234  //~ "data/cubemap/skybox/right.jpg",
235  //~ "data/cubemap/skybox/top.jpg",
236  //~ "data/cubemap/skybox/bottom.jpg",
237  //~ "data/cubemap/skybox/back.jpg",
238  //~ "data/cubemap/skybox/front.jpg",
239  };
240 
241  // creer la texture
242  glGenTextures(1, &texture);
243  glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
244 
245  for(int i= 0; i < 6; i++)
246  {
247  ImageData tmp= read_image_data(filenames[i]);
248  //~ ImageData image= flipX(flipY(tmp));
249  ImageData image= flipY(tmp); // les faces haut/bas sont retouchees a la main...
250 
251  GLenum data_format;
252  GLenum data_type= GL_UNSIGNED_BYTE;
253  if(image.channels == 3)
254  data_format= GL_RGB;
255  else // par defaut
256  data_format= GL_RGBA;
257 
258  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X +i, 0,
259  GL_RGBA, image.width, image.height, 0,
260  data_format, data_type, image.data());
261  }
262 #endif
263 
264  glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
265 
266  // filtrage "correct" sur les bords du cube...
267  glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
268  //~ glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
269  glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
270 
271  // etape 4 : vao pour dessiner la cubemap a l'infini
272  glGenVertexArrays(1, &vao_null);
273  glBindVertexArray(vao_null);
274  // pas de buffer, c'est le vertex shader qui genere directement les positions des sommets
275 
276  glUseProgram(0);
277  glBindVertexArray(0);
278 
279  // etat par defaut
280  glClearColor(0.2f, 0.2f, 0.2f, 1);
281  glClearDepthf(1);
282 
283  glDepthFunc(GL_LEQUAL); // !! attention !! le support de la cube map est dessine exactement sur le plan far
284  // et le test "classique" GL_LESS est toujours faux, la cubemap ne sera pas dessinee.
285 
286  glEnable(GL_DEPTH_TEST);
287 
288  glFrontFace(GL_CCW);
289  glCullFace(GL_BACK);
290  //~ glEnable(GL_CULL_FACE);
291  glDisable(GL_CULL_FACE);
292  return 0;
293 }
294 
295 int quit( )
296 {
297  release_program(program);
298  release_program(program_cubemap);
299  glDeleteVertexArrays(1, &vao);
300  glDeleteVertexArrays(1, &vao_null);
301  glDeleteBuffers(1, &vertex_buffer);
302  glDeleteBuffers(1, &normal_buffer);
303  glDeleteTextures(1, &texture);
304  return 0;
305 }
306 
307 int draw( )
308 {
309  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
310 
311 #if 1
312  // recupere les mouvements de la souris, utilise directement SDL2
313  int mx, my;
314  unsigned int mb= SDL_GetRelativeMouseState(&mx, &my);
315 
316  // deplace la camera
317  if(mb & SDL_BUTTON(1)) // le bouton gauche est enfonce
318  // tourne autour de l'objet
319  camera.rotation(mx, my);
320 
321  else if(mb & SDL_BUTTON(3)) // le bouton droit est enfonce
322  // approche / eloigne l'objet
323  camera.move(mx);
324 
325  else if(mb & SDL_BUTTON(2)) // le bouton du milieu est enfonce
326  // deplace le point de rotation
327  camera.translation((float) mx / (float) window_width(), (float) my / (float) window_height());
328 #endif
329 
330  /* config pipeline :
331  vertex array object
332  program
333  uniforms
334  texture
335  */
336  // recupere le point de vue et la projection de la camera
337  Transform model= Identity();
338  Transform view= camera.view();
339  Transform projection= camera.projection(window_width(), window_height(), 45);
340 
341  // compose les matrices pour passer du repere local de l'objet au repere projectif
342  Transform mvp= projection * view * model;
343 
344  // dessine l'objet avec le reflet de la cubemap
345  {
346  glBindVertexArray(vao);
347  glUseProgram(program);
348 
349  program_uniform(program, "mvpMatrix", mvp);
350  program_uniform(program, "mMatrix", model);
351  program_uniform(program, "camera_position", Inverse(view)(Point(0, 0, 0)));
352 
353  // texture
354  glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
355 
356  // sampler2D declare par le fragment shader
357  GLint location= glGetUniformLocation(program, "texture0");
358  glUniform1i(location, 0);
359  // ou program_uniform(program, "texture0", texture);
360 
361  glDrawArrays(GL_TRIANGLES, 0, vertex_count);
362  }
363 
364  // dessine la cubemap sur les autres pixels...
365  {
366  glBindVertexArray(vao_null);
367  glUseProgram(program_cubemap);
368 
369  // texture
370  glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
371 
372  // sampler2D declare par le fragment shader
373  GLint location= glGetUniformLocation(program_cubemap, "texture0");
374  glUniform1i(location, 0);
375  // ou program_uniform(program, "texture0", texture);
376 
377  program_uniform(program_cubemap, "vpInvMatrix", Inverse(projection * view));
378  program_uniform(program_cubemap, "camera_position", Inverse(view)(Point(0, 0, 0)));
379 
380  // dessine un triangle qui couvre tous les pixels de l'image
381  glDrawArrays(GL_TRIANGLES, 0, 3);
382  }
383 
384  if(key_state('s'))
385  {
386  clear_key_state('s');
387  static int calls= 0;
388  screenshot("cubemaps", ++calls);
389  }
390 
391  // nettoyage
392  glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
393  glUseProgram(0);
394  glBindVertexArray(0);
395 
396  return 1;
397 }
398 
399 
400 int main( int argc, char **argv )
401 {
402  // etape 1 : creer la fenetre
403  Window window= create_window(1024, 640);
404  if(window == NULL)
405  return 1; // erreur lors de la creation de la fenetre ou de l'init de sdl2
406 
407  // etape 2 : creer un contexte opengl pour pouvoir dessiner
408  Context context= create_context(window);
409  if(context == NULL)
410  return 1; // erreur lors de la creation du contexte opengl
411 
412  // etape 3 : creation des objets
413  if(init() < 0)
414  {
415  printf("[error] init failed.\n");
416  return 1;
417  }
418 
419  // etape 4 : affichage de l'application, tant que la fenetre n'est pas fermee. ou que draw() ne renvoie pas 0
420  run(window, draw);
421 
422  // etape 5 : nettoyage
423  quit();
424  release_context(context);
425  release_window(window);
426  return 0;
427 }
representation d'un objet / maillage.
Definition: mesh.h:112
const float * vertex_buffer() const
renvoie l'adresse de la position du premier sommet. permet de construire les vertex buffers openGL....
Definition: mesh.h:296
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
std::size_t vertex_buffer_size() const
renvoie la longueur (en octets) du vertex buffer.
Definition: mesh.h:298
int vertex_count() const
renvoie le nombre de sommets.
Definition: mesh.h:291
void release()
detruit les objets openGL.
Definition: mesh.cpp:62
std::size_t normal_buffer_size() const
renvoie la longueur (en octets) du normal buffer.
Definition: mesh.h:303
const float * normal_buffer() const
renvoie l'adresse de la normale du premier sommet. par convention, la normale est un vec3,...
Definition: mesh.h:301
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 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
Transform view() const
renvoie la transformation vue.
Definition: orbiter.cpp:40
Context create_context(Window window)
cree et configure un contexte opengl
Definition: window.cpp:356
int window_height()
renvoie la hauteur de la fenetre de l'application.
Definition: window.cpp:29
void release_window(Window window)
destruction de la fenetre.
Definition: window.cpp:325
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
Window create_window(const int w, const int h, const int major, const int minor, const int samples)
creation d'une fenetre pour l'application.
Definition: window.cpp:259
void release_context(Context context)
detruit le contexte openGL.
Definition: window.cpp:422
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
ImageData read_image_data(const char *filename)
charge les donnees d'un fichier png. renvoie une image initialisee par defaut en cas d'echec.
Definition: image_io.cpp:216
Image flipY(const Image &image)
retourne l'image
Definition: image_io.cpp:295
Image flipX(const Image &image)
retourne l'image
Definition: image_io.cpp:312
Image copy(const Image &image, const int xmin, const int ymin, const int width, const int height)
renvoie un bloc de l'image
Definition: image_io.cpp:328
Transform Inverse(const Transform &m)
renvoie l'inverse de la matrice.
Definition: mat.cpp:197
Transform Identity()
construit la transformation identite.
Definition: mat.cpp:187
Mesh read_mesh(const char *filename)
charge un fichier wavefront .obj et renvoie un mesh compose de triangles non indexes....
Definition: wavefront.cpp:14
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
int release_program(const GLuint program)
detruit les shaders et le program.
Definition: program.cpp:211
int init(std::vector< const char * > &options)
Definition: shader_kit.cpp:92
stockage temporaire des donnees d'une image.
Definition: image_io.h:38
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
int run(Window window, int(*draw)())
boucle de gestion des evenements de l'application.
Definition: window.cpp:147