1 2/* 3 * Specular reflection demo. The specular highlight is modulated by 4 * a sphere-mapped texture. The result is a high-gloss surface. 5 * NOTE: you really need hardware acceleration for this. 6 * Also note, this technique can't be implemented with multi-texture 7 * and separate specular color interpolation because there's no way 8 * to indicate that the second texture unit (the reflection map) 9 * should modulate the specular color and not the base color. 10 * A future multi-texture extension could fix that. 11 * 12 * Command line options: 13 * -info print GL implementation information 14 * 15 * 16 * Brian Paul October 22, 1999 This program is in the public domain. 17 */ 18 19 20#include <assert.h> 21#include <stdio.h> 22#include <stdlib.h> 23#include <math.h> 24#include <string.h> 25#include <GL/glew.h> 26#include "glut_wrap.h" 27 28#include "readtex.h" 29#include "trackball.h" 30 31 32#define SPECULAR_TEXTURE_FILE DEMOS_DATA_DIR "reflect.rgb" 33#define BASE_TEXTURE_FILE DEMOS_DATA_DIR "tile.rgb" 34 35/* Menu items */ 36#define DO_SPEC_TEXTURE 1 37#define OBJECT 2 38#define ANIMATE 3 39#define QUIT 100 40 41/* for convolution */ 42#define FILTER_SIZE 7 43 44static GLint Win; 45static GLint WinWidth = 500, WinHeight = 500; 46static GLuint CylinderObj = 0; 47static GLuint TeapotObj = 0; 48static GLuint Object = 0; 49static GLboolean Animate = GL_TRUE; 50 51static float CurQuat[4] = { 0, 0, 0, 1 }; 52 53static GLfloat Black[4] = { 0, 0, 0, 0 }; 54static GLfloat White[4] = { 1, 1, 1, 1 }; 55static GLfloat Diffuse[4] = { .3, .3, 1.0, 1.0 }; /* blue */ 56static GLfloat Shininess = 6; 57 58static GLuint BaseTexture, SpecularTexture; 59static GLboolean DoSpecTexture = GL_TRUE; 60 61static GLboolean ButtonDown = GL_FALSE; 62static GLint ButtonX, ButtonY; 63 64 65/* performance info */ 66static GLint T0 = 0; 67static GLint Frames = 0; 68 69 70static void Idle( void ) 71{ 72 static const float yAxis[3] = {0, 1, 0}; 73 static double t0 = -1.; 74 float quat[4]; 75 double dt, t = glutGet(GLUT_ELAPSED_TIME) / 1000.0; 76 if (t0 < 0.0) 77 t0 = t; 78 dt = t - t0; 79 t0 = t; 80 81 axis_to_quat(yAxis, 2.0 * dt, quat); 82 add_quats(quat, CurQuat, CurQuat); 83 84 glutPostRedisplay(); 85} 86 87 88static void Display( void ) 89{ 90 GLfloat rot[4][4]; 91 92 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 93 94 glPushMatrix(); 95 build_rotmatrix(rot, CurQuat); 96 glMultMatrixf(&rot[0][0]); 97 98 /* First pass: diffuse lighting with base texture */ 99 glMaterialfv(GL_FRONT, GL_DIFFUSE, Diffuse); 100 glMaterialfv(GL_FRONT, GL_SPECULAR, Black); 101 glEnable(GL_TEXTURE_2D); 102 glBindTexture(GL_TEXTURE_2D, BaseTexture); 103 glCallList(Object); 104 105 /* Second pass: specular lighting with reflection texture */ 106 glEnable(GL_POLYGON_OFFSET_FILL); 107 glBlendFunc(GL_ONE, GL_ONE); /* add */ 108 glEnable(GL_BLEND); 109 glMaterialfv(GL_FRONT, GL_DIFFUSE, Black); 110 glMaterialfv(GL_FRONT, GL_SPECULAR, White); 111 if (DoSpecTexture) { 112 glBindTexture(GL_TEXTURE_2D, SpecularTexture); 113 glEnable(GL_TEXTURE_GEN_S); 114 glEnable(GL_TEXTURE_GEN_T); 115 } 116 else { 117 glDisable(GL_TEXTURE_2D); 118 } 119 glCallList(Object); 120 glDisable(GL_TEXTURE_GEN_S); 121 glDisable(GL_TEXTURE_GEN_T); 122 glDisable(GL_BLEND); 123 glDisable(GL_POLYGON_OFFSET_FILL); 124 125 glPopMatrix(); 126 127 glutSwapBuffers(); 128 129 if (Animate) { 130 GLint t = glutGet(GLUT_ELAPSED_TIME); 131 Frames++; 132 if (t - T0 >= 5000) { 133 GLfloat seconds = (t - T0) / 1000.0; 134 GLfloat fps = Frames / seconds; 135 printf("%d frames in %g seconds = %g FPS\n", Frames, seconds, fps); 136 fflush(stdout); 137 T0 = t; 138 Frames = 0; 139 } 140 } 141} 142 143 144static void Reshape( int width, int height ) 145{ 146 GLfloat h = 30.0; 147 GLfloat w = h * width / height; 148 WinWidth = width; 149 WinHeight = height; 150 glViewport( 0, 0, width, height ); 151 glMatrixMode( GL_PROJECTION ); 152 glLoadIdentity(); 153 glFrustum( -w, w, -h, h, 150.0, 500.0 ); 154 glMatrixMode( GL_MODELVIEW ); 155 glLoadIdentity(); 156 glTranslatef( 0.0, 0.0, -380.0 ); 157} 158 159 160static void ToggleAnimate(void) 161{ 162 Animate = !Animate; 163 if (Animate) { 164 glutIdleFunc( Idle ); 165 T0 = glutGet(GLUT_ELAPSED_TIME); 166 Frames = 0; 167 } 168 else { 169 glutIdleFunc( NULL ); 170 } 171} 172 173 174static void ModeMenu(int entry) 175{ 176 if (entry==ANIMATE) { 177 ToggleAnimate(); 178 } 179 else if (entry==DO_SPEC_TEXTURE) { 180 DoSpecTexture = !DoSpecTexture; 181 } 182 else if (entry==OBJECT) { 183 if (Object == TeapotObj) 184 Object = CylinderObj; 185 else 186 Object = TeapotObj; 187 } 188 else if (entry==QUIT) { 189 exit(0); 190 } 191 glutPostRedisplay(); 192} 193 194 195static void Key( unsigned char key, int x, int y ) 196{ 197 (void) x; 198 (void) y; 199 switch (key) { 200 case 's': 201 Shininess--; 202 if (Shininess < 0.0) 203 Shininess = 0.0; 204 glMaterialf(GL_FRONT, GL_SHININESS, Shininess); 205 printf("Shininess = %g\n", Shininess); 206 break; 207 case 'S': 208 Shininess++; 209 if (Shininess > 128.0) 210 Shininess = 128.0; 211 glMaterialf(GL_FRONT, GL_SHININESS, Shininess); 212 printf("Shininess = %g\n", Shininess); 213 break; 214 case 'a': 215 case ' ': 216 ToggleAnimate(); 217 break; 218 case 'n': 219 Idle(); 220 break; 221 case 27: 222 glutDestroyWindow(Win); 223 exit(0); 224 break; 225 } 226 glutPostRedisplay(); 227} 228 229 230static void 231MouseMotion(int x, int y) 232{ 233 if (ButtonDown) { 234 float x0 = (2.0 * ButtonX - WinWidth) / WinWidth; 235 float y0 = (WinHeight - 2.0 * ButtonY) / WinHeight; 236 float x1 = (2.0 * x - WinWidth) / WinWidth; 237 float y1 = (WinHeight - 2.0 * y) / WinHeight; 238 float q[4]; 239 240 trackball(q, x0, y0, x1, y1); 241 ButtonX = x; 242 ButtonY = y; 243 add_quats(q, CurQuat, CurQuat); 244 245 glutPostRedisplay(); 246 } 247} 248 249 250static void 251MouseButton(int button, int state, int x, int y) 252{ 253 if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { 254 ButtonDown = GL_TRUE; 255 ButtonX = x; 256 ButtonY = y; 257 } 258 else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) { 259 ButtonDown = GL_FALSE; 260 } 261} 262 263 264static void Init( int argc, char *argv[] ) 265{ 266 GLboolean convolve = GL_FALSE; 267 GLboolean fullscreen = GL_FALSE; 268 int i; 269 270 for (i = 1; i < argc; i++) { 271 if (strcmp(argv[i], "-info")==0) { 272 printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER)); 273 printf("GL_VERSION = %s\n", (char *) glGetString(GL_VERSION)); 274 printf("GL_VENDOR = %s\n", (char *) glGetString(GL_VENDOR)); 275 printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS)); 276 } 277 else if (strcmp(argv[i], "-c")==0) { 278 convolve = GL_TRUE; 279 } 280 else if (strcmp(argv[i], "-f")==0) { 281 fullscreen = GL_TRUE; 282 } 283 } 284 285 if (convolve && !glutExtensionSupported("GL_ARB_imaging")) { 286 fprintf(stderr, 287 "GL_ARB_imaging is not supported, disabling convolution.\n"); 288 exit(1); 289 } 290 291 292 if (fullscreen) 293 glutFullScreen(); 294 295 /* Cylinder object */ 296 { 297 static GLfloat height = 100.0; 298 static GLfloat radius = 40.0; 299 static GLint slices = 24; /* pie slices around Z axis */ 300 static GLint stacks = 10; /* subdivisions along length of cylinder */ 301 static GLint rings = 4; /* rings in the end disks */ 302 GLUquadricObj *q = gluNewQuadric(); 303 assert(q); 304 gluQuadricTexture(q, GL_TRUE); 305 306 CylinderObj = glGenLists(1); 307 glNewList(CylinderObj, GL_COMPILE); 308 309 glPushMatrix(); 310 glTranslatef(0.0, 0.0, -0.5 * height); 311 312 glMatrixMode(GL_TEXTURE); 313 glLoadIdentity(); 314 /*glScalef(8.0, 4.0, 2.0);*/ 315 glMatrixMode(GL_MODELVIEW); 316 317 /* cylinder */ 318 gluQuadricNormals(q, GL_SMOOTH); 319 gluQuadricTexture(q, GL_TRUE); 320 gluCylinder(q, radius, radius, height, slices, stacks); 321 322 /* end cap */ 323 glMatrixMode(GL_TEXTURE); 324 glLoadIdentity(); 325 glScalef(3.0, 3.0, 1.0); 326 glMatrixMode(GL_MODELVIEW); 327 328 glTranslatef(0.0, 0.0, height); 329 gluDisk(q, 0.0, radius, slices, rings); 330 331 /* other end cap */ 332 glTranslatef(0.0, 0.0, -height); 333 gluQuadricOrientation(q, GLU_INSIDE); 334 gluDisk(q, 0.0, radius, slices, rings); 335 336 glPopMatrix(); 337 338 glMatrixMode(GL_TEXTURE); 339 glLoadIdentity(); 340 glMatrixMode(GL_MODELVIEW); 341 342 glEndList(); 343 gluDeleteQuadric(q); 344 } 345 346 /* Teapot */ 347 { 348 TeapotObj = glGenLists(1); 349 glNewList(TeapotObj, GL_COMPILE); 350 351 glFrontFace(GL_CW); 352 glutSolidTeapot(40.0); 353 glFrontFace(GL_CCW); 354 355 glEndList(); 356 } 357 358 /* show cylinder by default */ 359 Object = CylinderObj; 360 361 362 /* lighting */ 363 glEnable(GL_LIGHTING); 364 { 365 GLfloat pos[4] = { 3, 3, 3, 1 }; 366 glLightfv(GL_LIGHT0, GL_AMBIENT, Black); 367 glLightfv(GL_LIGHT0, GL_DIFFUSE, White); 368 glLightfv(GL_LIGHT0, GL_SPECULAR, White); 369 glLightfv(GL_LIGHT0, GL_POSITION, pos); 370 glEnable(GL_LIGHT0); 371 glMaterialfv(GL_FRONT, GL_AMBIENT, Black); 372 glMaterialf(GL_FRONT, GL_SHININESS, Shininess); 373 glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1); 374 } 375 376 /* Base texture */ 377 glGenTextures(1, &BaseTexture); 378 glBindTexture(GL_TEXTURE_2D, BaseTexture); 379 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 380 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 381 if (!LoadRGBMipmaps(BASE_TEXTURE_FILE, GL_RGB)) { 382 printf("Error: couldn't load texture image file %s\n", BASE_TEXTURE_FILE); 383 exit(1); 384 } 385 386 /* Specular texture */ 387 glGenTextures(1, &SpecularTexture); 388 glBindTexture(GL_TEXTURE_2D, SpecularTexture); 389 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 390 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 391 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); 392 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); 393 if (convolve) { 394 /* use convolution to blur the texture to simulate a dull finish 395 * on the object. 396 */ 397 GLubyte *img; 398 GLenum format; 399 GLint w, h; 400 GLfloat filter[FILTER_SIZE][FILTER_SIZE][4]; 401 402 for (h = 0; h < FILTER_SIZE; h++) { 403 for (w = 0; w < FILTER_SIZE; w++) { 404 const GLfloat k = 1.0 / (FILTER_SIZE * FILTER_SIZE); 405 filter[h][w][0] = k; 406 filter[h][w][1] = k; 407 filter[h][w][2] = k; 408 filter[h][w][3] = k; 409 } 410 } 411 412 glEnable(GL_CONVOLUTION_2D); 413 glConvolutionParameteri(GL_CONVOLUTION_2D, 414 GL_CONVOLUTION_BORDER_MODE, GL_CONSTANT_BORDER); 415 glConvolutionFilter2D(GL_CONVOLUTION_2D, GL_RGBA, 416 FILTER_SIZE, FILTER_SIZE, 417 GL_RGBA, GL_FLOAT, filter); 418 419 img = LoadRGBImage(SPECULAR_TEXTURE_FILE, &w, &h, &format); 420 if (!img) { 421 printf("Error: couldn't load texture image file %s\n", 422 SPECULAR_TEXTURE_FILE); 423 exit(1); 424 } 425 426 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, 427 format, GL_UNSIGNED_BYTE, img); 428 free(img); 429 } 430 else { 431 /* regular path */ 432 if (!LoadRGBMipmaps(SPECULAR_TEXTURE_FILE, GL_RGB)) { 433 printf("Error: couldn't load texture image file %s\n", 434 SPECULAR_TEXTURE_FILE); 435 exit(1); 436 } 437 } 438 439 /* misc */ 440 glEnable(GL_CULL_FACE); 441 glEnable(GL_TEXTURE_2D); 442 glEnable(GL_DEPTH_TEST); 443 glEnable(GL_NORMALIZE); 444 445 glPolygonOffset( -1, -1 ); 446} 447 448 449int main( int argc, char *argv[] ) 450{ 451 glutInitWindowSize(WinWidth, WinHeight); 452 glutInit( &argc, argv ); 453 glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); 454 Win = glutCreateWindow(argv[0] ); 455 glewInit(); 456 glutReshapeFunc( Reshape ); 457 glutKeyboardFunc( Key ); 458 glutDisplayFunc( Display ); 459 glutMotionFunc(MouseMotion); 460 glutMouseFunc(MouseButton); 461 if (Animate) 462 glutIdleFunc( Idle ); 463 464 glutCreateMenu(ModeMenu); 465 glutAddMenuEntry("Toggle Highlight", DO_SPEC_TEXTURE); 466 glutAddMenuEntry("Toggle Object", OBJECT); 467 glutAddMenuEntry("Toggle Animate", ANIMATE); 468 glutAddMenuEntry("Quit", QUIT); 469 glutAttachMenu(GLUT_RIGHT_BUTTON); 470 471 Init(argc, argv); 472 473 glutMainLoop(); 474 return 0; 475} 476