1/* 2 * Test rendering into a cube map texture with FBOs. 3 * 4 * Brian Paul 5 * May 2011 6 */ 7 8#include <assert.h> 9#include <stdio.h> 10#include <stdlib.h> 11#include <string.h> 12#include <math.h> 13#include <GL/glew.h> 14#include "glut_wrap.h" 15#include "shaderutil.h" 16 17 18#define DEG_TO_RAD (3.14159 / 180.0) 19 20#define ELEMENTS(array) (sizeof(array) / sizeof(array[0])) 21 22static GLint WindowWidth = 750, WindowHeight = 450; 23static GLint Win; 24 25static GLfloat Xrot = 10, Yrot = 0, Zrot = 0; 26 27static GLfloat GroundColor[4] = {0.5, 0.5, 0.75, 1}; 28static GLfloat GroundY = -1.0f; 29 30static GLfloat ViewDist = 30.0; 31 32static GLfloat SpherePos[4] = { 0, 2.5, 0, 1 }; 33static GLfloat LightDist = 15; 34static GLfloat LightLatitude = 30.0; 35static GLfloat LightLongitude = 45.0; 36static GLfloat LightPos[4]; 37 38static GLboolean Anim = GL_TRUE; 39 40static GLboolean NeedNewCubeMap = GL_TRUE; 41static GLuint CubeTexture; 42 43 44struct cylinder 45{ 46 float PosX, PosY; 47 float Radius, Height; 48 float Color[4]; 49}; 50 51#define NUM_CYLINDERS 30 52 53static struct cylinder Cylinders[NUM_CYLINDERS]; 54 55 56static float 57RandomFloat(float min, float max) 58{ 59 const int k = 10000; 60 float t = (rand() % k) / (float) (k - 1); /* t in [0,1] */ 61 float r = min + t * (max - min); 62 return r; 63} 64 65 66static void 67CheckError(int line) 68{ 69 GLenum err = glGetError(); 70 if (err) { 71 printf("GL Error 0x%x at line %d\n", (int) err, line); 72 } 73} 74 75 76static void 77GenerateCylinders(void) 78{ 79 int i; 80 for (i = 0; i < NUM_CYLINDERS; i++) { 81 float r = RandomFloat(5.0, 9.0); 82 float a = RandomFloat(0, 2 * M_PI); 83 84 Cylinders[i].PosX = r * cos(a); 85 Cylinders[i].PosY = r * sin(a); 86 Cylinders[i].Radius = RandomFloat(0.25, 1.0); 87 Cylinders[i].Height = RandomFloat(1.0, 7.0); 88 Cylinders[i].Color[0] = RandomFloat(0.25, 1.0); 89 Cylinders[i].Color[1] = RandomFloat(0.25, 1.0); 90 Cylinders[i].Color[2] = RandomFloat(0.25, 1.0); 91 Cylinders[i].Color[3] = RandomFloat(0.25, 1.0); 92 } 93} 94 95static void 96DrawCylinder(const struct cylinder *cyl) 97{ 98 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, cyl->Color); 99 glPushMatrix(); 100 glTranslatef(cyl->PosX, GroundY, cyl->PosY); 101 glRotatef(-90, 1, 0, 0); 102 glutSolidCone(cyl->Radius, cyl->Height, 12, 2); 103 glPopMatrix(); 104} 105 106 107static void 108DrawLightSource(void) 109{ 110 glPushMatrix(); 111 glTranslatef(LightPos[0], LightPos[1], LightPos[2]); 112 glColor3f(1, 1, 1); 113 glutSolidSphere(0.25, 8, 8); 114 glPopMatrix(); 115} 116 117 118static void 119DrawScene(void) 120{ 121 int i; 122 123 glEnable(GL_LIGHTING); 124 125 for (i = 0; i < NUM_CYLINDERS; i++) { 126 DrawCylinder(&Cylinders[i]); 127 } 128 129 glDisable(GL_LIGHTING); 130 131 if (1) 132 DrawLightSource(); 133 134 /* ground plane */ 135 if (1) { 136 GLfloat k = 10.0, g = GroundY; 137 CheckError(__LINE__); 138 glPushMatrix(); 139 glColor3fv(GroundColor); 140 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, GroundColor); 141 glNormal3f(0, 1, 0); 142 glBegin(GL_POLYGON); 143 glVertex3f(-k, g, -k); 144 glVertex3f( k, g, -k); 145 glVertex3f( k, g, k); 146 glVertex3f(-k, g, k); 147 glEnd(); 148 glPopMatrix(); 149 } 150 151 CheckError(__LINE__); 152} 153 154 155/* 156 * Draw sphere with cube map texture that reflects rest of the scene. 157 */ 158static void 159DrawShinySphere(void) 160{ 161 glEnable(GL_TEXTURE_CUBE_MAP); 162 163#if 0 164 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB); 165 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB); 166 glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB); 167#else 168 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB); 169 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB); 170 glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB); 171#endif 172 glEnable(GL_TEXTURE_GEN_S); 173 glEnable(GL_TEXTURE_GEN_T); 174 glEnable(GL_TEXTURE_GEN_R); 175 176 glPushMatrix(); 177 glTranslatef(SpherePos[0], SpherePos[1], SpherePos[2]); 178 glColor3f(0.75, 0.75, 0.75); 179 180 if (1) { 181 glMatrixMode(GL_TEXTURE); 182 glLoadIdentity(); 183 glRotatef(-Yrot, 0, 1, 0); 184 glRotatef(-Xrot, 1, 0, 0); 185 } 186 187 glutSolidSphere(2.5, 16, 16); 188 189 glMatrixMode(GL_MODELVIEW); 190 191 glPopMatrix(); 192 193 glDisable(GL_TEXTURE_CUBE_MAP); 194 glDisable(GL_TEXTURE_GEN_S); 195 glDisable(GL_TEXTURE_GEN_T); 196 glDisable(GL_TEXTURE_GEN_R); 197} 198 199 200/** 201 * Setup modelview and projection for drawing a cube face. 202 */ 203static void 204SetupCubeFaceView(GLenum face, const GLfloat centerPos[4]) 205{ 206 GLfloat near_val = 0.5, far_val = 20.0; 207 208 glMatrixMode(GL_PROJECTION); 209 glLoadIdentity(); 210 glFrustum(-near_val, near_val, -near_val, near_val, near_val, far_val); 211 212 glMatrixMode(GL_MODELVIEW); 213 glLoadIdentity(); 214 215 switch (face) { 216 case GL_TEXTURE_CUBE_MAP_POSITIVE_X: 217 glRotatef(180, 1, 0, 0); 218 glRotatef(-90, 0, 1, 0); 219 break; 220 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: 221 glRotatef(180, 1, 0, 0); 222 glRotatef(90, 0, 1, 0); 223 break; 224 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: 225 glRotatef(-90, 1, 0, 0); 226 break; 227 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: 228 glRotatef(90, 1, 0, 0); 229 break; 230 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: 231 glRotatef(180, 1, 0, 0); 232 break; 233 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: 234 glRotatef(0, 1, 0, 0); 235 glRotatef(180, 0, 0, 1); 236 break; 237 } 238 239 glTranslatef(-centerPos[0], -centerPos[1], -centerPos[2]); 240 241 glLightfv(GL_LIGHT0, GL_POSITION, LightPos); 242} 243 244 245 246static void 247ComputeLightPos(GLfloat dist, GLfloat latitude, GLfloat longitude, 248 GLfloat pos[4]) 249{ 250 pos[0] = dist * sin(longitude * DEG_TO_RAD); 251 pos[1] = dist * sin(latitude * DEG_TO_RAD); 252 pos[2] = dist * cos(latitude * DEG_TO_RAD) * cos(longitude * DEG_TO_RAD); 253 pos[3] = 1; 254} 255 256 257static GLint 258ChooseCubeSize(void) 259{ 260 if (WindowWidth >= 1024 && WindowHeight >= 1024) { 261 return 1024; 262 } 263 else if (WindowWidth >= 512 && WindowHeight >= 512) { 264 return 512; 265 } 266 else if (WindowWidth >= 256 && WindowHeight >= 256) { 267 return 256; 268 } 269 else { 270 return 128; 271 } 272} 273 274 275static GLuint 276CreateCubeTexture(GLint size) 277{ 278 GLboolean linearFilter = GL_TRUE; 279 GLuint cube, face; 280 281 glGenTextures(1, &cube); 282 glBindTexture(GL_TEXTURE_CUBE_MAP, cube); 283 284 /* Set the filter mode so that the texture is texture-complete. 285 * Otherwise it will cause the framebuffer to fail the framebuffer 286 * completeness test. 287 */ 288 if (linearFilter) { 289 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 290 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 291 } 292 else { 293 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 294 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 295 } 296 297 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 298 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 299 300 for (face = 0; face < 6; face++) { 301 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_RGBA, 302 size, size, 0, 303 GL_RGBA, GL_UNSIGNED_BYTE, NULL); 304 } 305 306 CheckError(__LINE__); 307 308 return cube; 309} 310 311 312/** 313 * Render the six faces of a cube map. 314 */ 315static void 316RenderCubeMap(void) 317{ 318 GLuint fbo, face, depthStencilRb; 319 GLint cubeSize; 320 321 CheckError(__LINE__); 322 323 cubeSize = ChooseCubeSize(); 324 printf("Rendering %d x %d cube texture\n", cubeSize, cubeSize); 325 326 CubeTexture = CreateCubeTexture(cubeSize); 327 328 /* 329 * Make FBO. 330 */ 331 glGenFramebuffersEXT(1, &fbo); 332 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); 333 334 /* 335 * Make depth/stencil renderbuffer. 336 */ 337 glGenRenderbuffersEXT(1, &depthStencilRb); 338 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthStencilRb); 339 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL, 340 cubeSize, cubeSize); 341 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, 342 GL_DEPTH_ATTACHMENT_EXT, 343 GL_RENDERBUFFER_EXT, depthStencilRb); 344 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, 345 GL_STENCIL_ATTACHMENT_EXT, 346 GL_RENDERBUFFER_EXT, depthStencilRb); 347 348 glViewport(0, 0, cubeSize, cubeSize); 349 350 /* 351 * Render into cube faces. 352 */ 353 for (face = 0; face < 6; face++) { 354 GLenum status; 355 356 /* Render color into face of cubemap */ 357 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, 358 GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 359 CubeTexture, 0); 360 361 status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); 362 CheckError(__LINE__); 363 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { 364 fprintf(stderr, "FBO not complete! status = 0x%04x\n", status); 365 assert(status == GL_FRAMEBUFFER_COMPLETE_EXT); 366 } 367 368 CheckError(__LINE__); 369 370 /* Render the depth image into cube face */ 371 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 372 373 SetupCubeFaceView(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, SpherePos); 374 375 DrawScene(); 376 } 377 378 glDeleteRenderbuffersEXT(1, &depthStencilRb); 379 glDeleteFramebuffersEXT(1, &fbo); 380 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 381 382 CheckError(__LINE__); 383} 384 385 386/** 387 * Redraw window image 388 */ 389static void 390Display(void) 391{ 392 const GLfloat ar = (GLfloat) WindowWidth / (GLfloat) WindowHeight; 393 394 ComputeLightPos(LightDist, LightLatitude, LightLongitude, LightPos); 395 396 if (NeedNewCubeMap) { 397 /* regenerate cube map faces */ 398 RenderCubeMap(); 399 NeedNewCubeMap = GL_FALSE; 400 } 401 402 glViewport(0, 0, WindowWidth, WindowHeight); 403 404 /* draw scene from camera's view */ 405 glMatrixMode(GL_PROJECTION); 406 glLoadIdentity(); 407 glFrustum(-ar, ar, -1.0, 1.0, 4.0, 50.0); 408 409 glMatrixMode(GL_MODELVIEW); 410 glLoadIdentity(); 411 glTranslatef(0.0, 0.0, -ViewDist); 412 glRotatef(Xrot, 1, 0, 0); 413 glRotatef(Yrot, 0, 1, 0); 414 glRotatef(Zrot, 0, 0, 1); 415 416 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 417 418 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); 419 glLightfv(GL_LIGHT0, GL_POSITION, LightPos); 420 421 DrawScene(); 422 DrawShinySphere(); 423 424 glutSwapBuffers(); 425} 426 427 428static void 429Reshape(int width, int height) 430{ 431 WindowWidth = width; 432 WindowHeight = height; 433 NeedNewCubeMap = GL_TRUE; 434} 435 436 437static void 438Idle(void) 439{ 440 static double t0 = -1.; 441 double dt, t = glutGet(GLUT_ELAPSED_TIME) / 1000.0; 442 if (t0 < 0.0) 443 t0 = t; 444 dt = t - t0; 445 t0 = t; 446 Yrot += 40.0 * dt; 447 glutPostRedisplay(); 448} 449 450 451static void 452Key(unsigned char key, int x, int y) 453{ 454 const GLfloat step = 3.0; 455 (void) x; 456 (void) y; 457 switch (key) { 458 case 'a': 459 Anim = !Anim; 460 if (Anim) 461 glutIdleFunc(Idle); 462 else 463 glutIdleFunc(NULL); 464 break; 465 case 'r': 466 GenerateCylinders(); 467 NeedNewCubeMap = GL_TRUE; 468 break; 469 case 'z': 470 Zrot -= step; 471 break; 472 case 'Z': 473 Zrot += step; 474 break; 475 case 27: 476 glutDestroyWindow(Win); 477 exit(0); 478 break; 479 } 480 fflush(stdout); 481 glutPostRedisplay(); 482} 483 484 485static void 486SpecialKey(int key, int x, int y) 487{ 488 const GLfloat step = 3.0; 489 const int mod = glutGetModifiers(); 490 (void) x; 491 (void) y; 492 switch (key) { 493 case GLUT_KEY_UP: 494 if (mod) 495 //LightLatitude += step; 496 SpherePos[1] += .1*step; 497 else 498 Xrot += step; 499 break; 500 case GLUT_KEY_DOWN: 501 if (mod) 502 //LightLatitude -= step; 503 SpherePos[1] -= .1*step; 504 else 505 Xrot -= step; 506 break; 507 case GLUT_KEY_LEFT: 508 if (mod) 509 LightLongitude += step; 510 else 511 Yrot += step; 512 break; 513 case GLUT_KEY_RIGHT: 514 if (mod) 515 LightLongitude -= step; 516 else 517 Yrot -= step; 518 break; 519 } 520 if (mod) 521 NeedNewCubeMap = GL_TRUE; 522 523 glutPostRedisplay(); 524} 525 526 527static void 528Init(void) 529{ 530 const char *extensions[3] = { 531 "GL_ARB_depth_texture", 532 "GL_ARB_texture_cube_map", 533 "GL_EXT_framebuffer_object" 534 }; 535 int i; 536 537 for (i = 0; i < ELEMENTS(extensions); i++) { 538 if (!glutExtensionSupported(extensions[i])) { 539 printf("Sorry, this demo requires %s\n", extensions[i]); 540 exit(1); 541 } 542 } 543 544 glClearColor(0.25, 0.25, 0.25, 1.0); 545 546 glEnable(GL_DEPTH_TEST); 547 glEnable(GL_LIGHTING); 548 glEnable(GL_LIGHT0); 549 550 GenerateCylinders(); 551 552 printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER)); 553} 554 555 556static void 557PrintHelp(void) 558{ 559 printf("Keys:\n"); 560 printf(" a = toggle animation\n"); 561 printf(" cursor keys = rotate scene\n"); 562 printf(" <shift> + up/down = move sphere\n"); 563 printf(" <shift> + left/right = move light\n"); 564 fflush(stdout); 565} 566 567 568int 569main(int argc, char *argv[]) 570{ 571 glutInitWindowSize(WindowWidth, WindowHeight); 572 glutInit(&argc, argv); 573 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL); 574 Win = glutCreateWindow(argv[0]); 575 glewInit(); 576 glutReshapeFunc(Reshape); 577 glutKeyboardFunc(Key); 578 glutSpecialFunc(SpecialKey); 579 glutDisplayFunc(Display); 580 if (Anim) 581 glutIdleFunc(Idle); 582 Init(); 583 PrintHelp(); 584 glutMainLoop(); 585 return 0; 586} 587