1/* 2 * Use GL_ARB_fragment_program and GL_ARB_vertex_program to implement 3 * simple per-pixel lighting. 4 * 5 * Brian Paul 6 * 17 April 2003 7 */ 8 9#include <assert.h> 10#include <string.h> 11#include <stdio.h> 12#include <stdlib.h> 13#include <math.h> 14#include <GL/glew.h> 15#include "glut_wrap.h" 16 17 18static GLfloat Diffuse[4] = { 0.5, 0.5, 1.0, 1.0 }; 19static GLfloat Specular[4] = { 0.8, 0.8, 0.8, 1.0 }; 20static GLfloat LightPos[4] = { 0.0, 10.0, 20.0, 1.0 }; 21static GLfloat Delta = 1.0; 22 23static GLuint FragProg; 24static GLuint VertProg; 25static GLboolean Anim = GL_TRUE; 26static GLboolean Wire = GL_FALSE; 27static GLboolean PixelLight = GL_TRUE; 28static GLint Win; 29 30static GLint T0 = 0; 31static GLint Frames = 0; 32 33static GLfloat Xrot = 0, Yrot = 0; 34 35 36/* These must match the indexes used in the fragment program */ 37#define LIGHTPOS 3 38 39/* Set to one to test ARB_fog_linear program option */ 40#define DO_FRAGMENT_FOG 0 41 42static void normalize (GLfloat *dst, const GLfloat *src) 43{ 44 GLfloat len = sqrt (src[0] * src[0] + src[1] * src[1] + src[2] * src[2]); 45 dst[0] = src[0] / len; 46 dst[1] = src[1] / len; 47 dst[2] = src[2] / len; 48} 49 50static void Redisplay( void ) 51{ 52 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 53 54 if (PixelLight) { 55 GLfloat pos[4]; 56 57 normalize( pos, LightPos ); 58 pos[3] = LightPos[3]; 59 glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, 60 LIGHTPOS, pos); 61 glEnable(GL_FRAGMENT_PROGRAM_ARB); 62 glEnable(GL_VERTEX_PROGRAM_ARB); 63 glDisable(GL_LIGHTING); 64 } 65 else { 66 glLightfv(GL_LIGHT0, GL_POSITION, LightPos); 67 glDisable(GL_FRAGMENT_PROGRAM_ARB); 68 glDisable(GL_VERTEX_PROGRAM_ARB); 69 glEnable(GL_LIGHTING); 70 } 71 72 glPushMatrix(); 73 glRotatef(Xrot, 1, 0, 0); 74 glRotatef(Yrot, 0, 1, 0); 75 glutSolidSphere(2.0, 10, 5); 76 glPopMatrix(); 77 78 glutSwapBuffers(); 79 80 Frames++; 81 82 if (Anim) { 83 GLint t = glutGet(GLUT_ELAPSED_TIME); 84 if (t - T0 >= 5000) { 85 GLfloat seconds = (t - T0) / 1000.0; 86 GLfloat fps = Frames / seconds; 87 printf("%d frames in %6.3f seconds = %6.3f FPS\n", Frames, seconds, fps); 88 fflush(stdout); 89 T0 = t; 90 Frames = 0; 91 } 92 } 93} 94 95 96static void Idle(void) 97{ 98 LightPos[0] += Delta; 99 if (LightPos[0] > 25.0) 100 Delta = -1.0; 101 else if (LightPos[0] <- 25.0) 102 Delta = 1.0; 103 glutPostRedisplay(); 104} 105 106 107static void Reshape( int width, int height ) 108{ 109 glViewport( 0, 0, width, height ); 110 glMatrixMode( GL_PROJECTION ); 111 glLoadIdentity(); 112 glFrustum( -1.0, 1.0, -1.0, 1.0, 5.0, 25.0 ); 113 glMatrixMode( GL_MODELVIEW ); 114 glLoadIdentity(); 115 glTranslatef( 0.0, 0.0, -15.0 ); 116} 117 118 119static void Key( unsigned char key, int x, int y ) 120{ 121 (void) x; 122 (void) y; 123 switch (key) { 124 case ' ': 125 case 'a': 126 Anim = !Anim; 127 if (Anim) 128 glutIdleFunc(Idle); 129 else 130 glutIdleFunc(NULL); 131 break; 132 case 'x': 133 LightPos[0] -= 1.0; 134 break; 135 case 'X': 136 LightPos[0] += 1.0; 137 break; 138 case 'w': 139 Wire = !Wire; 140 if (Wire) 141 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 142 else 143 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 144 break; 145 case 'p': 146 PixelLight = !PixelLight; 147 if (PixelLight) { 148 printf("Per-pixel lighting\n"); 149 } 150 else { 151 printf("Conventional lighting\n"); 152 } 153 break; 154 case 27: 155 glDeleteProgramsARB(1, &VertProg); 156 glDeleteProgramsARB(1, &FragProg); 157 glutDestroyWindow(Win); 158 exit(0); 159 break; 160 } 161 glutPostRedisplay(); 162} 163 164static void SpecialKey( int key, int x, int y ) 165{ 166 const GLfloat step = 3.0; 167 (void) x; 168 (void) y; 169 switch (key) { 170 case GLUT_KEY_UP: 171 Xrot -= step; 172 break; 173 case GLUT_KEY_DOWN: 174 Xrot += step; 175 break; 176 case GLUT_KEY_LEFT: 177 Yrot -= step; 178 break; 179 case GLUT_KEY_RIGHT: 180 Yrot += step; 181 break; 182 } 183 glutPostRedisplay(); 184} 185 186 187/* A helper for finding errors in program strings */ 188static int FindLine( const char *program, int position ) 189{ 190 int i, line = 1; 191 for (i = 0; i < position; i++) { 192 if (program[i] == '\n') 193 line++; 194 } 195 return line; 196} 197 198 199static void Init( void ) 200{ 201 GLint errorPos; 202 203 /* Yes, this could be expressed more efficiently */ 204 static const char *fragProgramText = 205 "!!ARBfp1.0\n" 206#if DO_FRAGMENT_FOG 207 "OPTION ARB_fog_linear; \n" 208#endif 209 "PARAM Diffuse = state.material.diffuse; \n" 210 "PARAM Specular = state.material.specular; \n" 211 "PARAM LightPos = program.local[3]; \n" 212 "TEMP normal, len; \n" 213 "TEMP dotProd, specAtten; \n" 214 "TEMP diffuseColor, specularColor; \n" 215 216 "# Compute normalized normal \n" 217 "DP3 len.x, fragment.texcoord[0], fragment.texcoord[0]; \n" 218 "RSQ len.y, len.x; \n" 219 "MUL normal.xyz, fragment.texcoord[0], len.y; \n" 220 221 "# Compute dot product of light direction and normal vector\n" 222 "DP3_SAT dotProd.x, LightPos, normal; # limited to [0,1]\n" 223 224 "MUL diffuseColor.xyz, Diffuse, dotProd.x; # diffuse attenuation\n" 225 226 "POW specAtten.x, dotProd.x, {20.0}.x; # specular exponent\n" 227 228 "MUL specularColor.xyz, Specular, specAtten.x; # specular attenuation\n" 229 230 "MOV result.color.w, Diffuse; \n" 231#if DO_FRAGMENT_FOG 232 "# need to clamp color to [0,1] before fogging \n" 233 "ADD_SAT result.color.xyz, diffuseColor, specularColor; # add colors\n" 234#else 235 "# clamping will be done after program's finished \n" 236 "ADD result.color.xyz, diffuseColor, specularColor; # add colors\n" 237#endif 238 "END \n" 239 ; 240 241 static const char *vertProgramText = 242 "!!ARBvp1.0\n" 243 "ATTRIB pos = vertex.position; \n" 244 "ATTRIB norm = vertex.normal; \n" 245 "PARAM modelview[4] = { state.matrix.modelview }; \n" 246 "PARAM modelviewProj[4] = { state.matrix.mvp }; \n" 247 "PARAM invModelview[4] = { state.matrix.modelview.invtrans }; \n" 248 249 "# typical modelview/projection transform \n" 250 "DP4 result.position.x, pos, modelviewProj[0]; \n" 251 "DP4 result.position.y, pos, modelviewProj[1]; \n" 252 "DP4 result.position.z, pos, modelviewProj[2]; \n" 253 "DP4 result.position.w, pos, modelviewProj[3]; \n" 254 255 "# transform normal by inv transpose of modelview, put in tex0 \n" 256 "DP3 result.texcoord[0].x, norm, invModelview[0]; \n" 257 "DP3 result.texcoord[0].y, norm, invModelview[1]; \n" 258 "DP3 result.texcoord[0].z, norm, invModelview[2]; \n" 259 "DP3 result.texcoord[0].w, norm, invModelview[3]; \n" 260 261#if DO_FRAGMENT_FOG 262 "# compute fog coordinate = vertex eye-space Z coord (negated)\n" 263 "DP4 result.fogcoord, -pos, modelview[2]; \n" 264#endif 265 "END\n"; 266 ; 267 268 if (!glutExtensionSupported("GL_ARB_vertex_program")) { 269 printf("Sorry, this demo requires GL_ARB_vertex_program\n"); 270 exit(1); 271 } 272 if (!glutExtensionSupported("GL_ARB_fragment_program")) { 273 printf("Sorry, this demo requires GL_ARB_fragment_program\n"); 274 exit(1); 275 } 276 277 /* 278 * Fragment program 279 */ 280 glGenProgramsARB(1, &FragProg); 281 assert(FragProg > 0); 282 glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, FragProg); 283 glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, 284 GL_PROGRAM_FORMAT_ASCII_ARB, 285 strlen(fragProgramText), 286 (const GLubyte *) fragProgramText); 287 glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos); 288 if (glGetError() != GL_NO_ERROR || errorPos != -1) { 289 int l = FindLine(fragProgramText, errorPos); 290 printf("Fragment Program Error (pos=%d line=%d): %s\n", errorPos, l, 291 (char *) glGetString(GL_PROGRAM_ERROR_STRING_ARB)); 292 exit(0); 293 } 294 assert(glIsProgramARB(FragProg)); 295 296 /* 297 * Do some sanity tests 298 */ 299 { 300 GLdouble v[4]; 301 glProgramLocalParameter4dARB(GL_FRAGMENT_PROGRAM_ARB, 8, 302 10.0, 20.0, 30.0, 40.0); 303 glGetProgramLocalParameterdvARB(GL_FRAGMENT_PROGRAM_ARB, 8, v); 304 assert(v[0] == 10.0); 305 assert(v[1] == 20.0); 306 assert(v[2] == 30.0); 307 assert(v[3] == 40.0); 308 } 309 310 /* 311 * Vertex program 312 */ 313 glGenProgramsARB(1, &VertProg); 314 assert(VertProg > 0); 315 glBindProgramARB(GL_VERTEX_PROGRAM_ARB, VertProg); 316 glProgramStringARB(GL_VERTEX_PROGRAM_ARB, 317 GL_PROGRAM_FORMAT_ASCII_ARB, 318 strlen(vertProgramText), 319 (const GLubyte *) vertProgramText); 320 glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos); 321 if (glGetError() != GL_NO_ERROR || errorPos != -1) { 322 int l = FindLine(vertProgramText, errorPos); 323 printf("Vertex Program Error (pos=%d line=%d): %s\n", errorPos, l, 324 (char *) glGetString(GL_PROGRAM_ERROR_STRING_ARB)); 325 exit(0); 326 } 327 assert(glIsProgramARB(VertProg)); 328 329 /* 330 * Misc init 331 */ 332 glClearColor(0.3, 0.3, 0.3, 0.0); 333 glEnable(GL_DEPTH_TEST); 334 glEnable(GL_LIGHT0); 335 glEnable(GL_LIGHTING); 336 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Diffuse); 337 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, Specular); 338 glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 20.0); 339 340#if DO_FRAGMENT_FOG 341 { 342 /* Green-ish fog color */ 343 static const GLfloat fogColor[4] = {0.5, 1.0, 0.5, 0}; 344 glFogfv(GL_FOG_COLOR, fogColor); 345 glFogf(GL_FOG_START, 5.0); 346 glFogf(GL_FOG_END, 25.0); 347 } 348#endif 349 350 printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER)); 351 printf("Press p to toggle between per-pixel and per-vertex lighting\n"); 352} 353 354 355int main( int argc, char *argv[] ) 356{ 357 glutInitWindowSize( 200, 200 ); 358 glutInit( &argc, argv ); 359 glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); 360 Win = glutCreateWindow(argv[0]); 361 glewInit(); 362 glutReshapeFunc( Reshape ); 363 glutKeyboardFunc( Key ); 364 glutSpecialFunc( SpecialKey ); 365 glutDisplayFunc( Redisplay ); 366 if (Anim) 367 glutIdleFunc(Idle); 368 Init(); 369 glutMainLoop(); 370 return 0; 371} 372