blob: 2173ae6fedc9aa3c224ba1f941b6165a8acedb0c [file] [log] [blame]
Brian Paul24339172003-04-17 19:20:54 +00001/*
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/glut.h>
15
16
17static GLfloat Diffuse[4] = { 0.5, 0.5, 1.0, 1.0 };
18static GLfloat Specular[4] = { 0.8, 0.8, 0.8, 1.0 };
19static GLfloat LightPos[4] = { 0.0, 10.0, 20.0, 1.0 };
20static GLfloat Delta = 1.0;
21
22static GLuint FragProg;
23static GLuint VertProg;
24static GLboolean Anim = GL_TRUE;
25static GLboolean Wire = GL_FALSE;
26static GLboolean PixelLight = GL_TRUE;
27
Keith Whitwell180e28e2004-04-23 14:06:02 +000028static GLint T0 = 0;
29static GLint Frames = 0;
30
Brian Paul24339172003-04-17 19:20:54 +000031static GLfloat Xrot = 0, Yrot = 0;
32
33static PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glProgramLocalParameter4fvARB_func;
Brian Paul3ca3ab02003-04-17 21:43:55 +000034static PFNGLPROGRAMLOCALPARAMETER4DARBPROC glProgramLocalParameter4dARB_func;
35static PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glGetProgramLocalParameterdvARB_func;
Brian Paul24339172003-04-17 19:20:54 +000036static PFNGLGENPROGRAMSARBPROC glGenProgramsARB_func;
37static PFNGLPROGRAMSTRINGARBPROC glProgramStringARB_func;
38static PFNGLBINDPROGRAMARBPROC glBindProgramARB_func;
39static PFNGLISPROGRAMARBPROC glIsProgramARB_func;
Brian Paula7e6f7b2004-01-13 16:17:21 +000040static PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB_func;
Brian Paul24339172003-04-17 19:20:54 +000041
42/* These must match the indexes used in the fragment program */
Brian Paul24339172003-04-17 19:20:54 +000043#define LIGHTPOS 3
44
Brian Paul9a389d42004-02-17 17:59:59 +000045/* Set to one to test ARB_fog_linear program option */
46#define DO_FRAGMENT_FOG 0
Brian Paul24339172003-04-17 19:20:54 +000047
48
49static void Redisplay( void )
50{
51 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
52
53 if (PixelLight) {
Brian Paulb58091a2005-01-09 17:00:57 +000054#if defined(GL_ARB_fragment_program)
Brian Paul3ca3ab02003-04-17 21:43:55 +000055 glProgramLocalParameter4fvARB_func(GL_FRAGMENT_PROGRAM_ARB,
56 LIGHTPOS, LightPos);
Brian Paul24339172003-04-17 19:20:54 +000057 glEnable(GL_FRAGMENT_PROGRAM_ARB);
58 glEnable(GL_VERTEX_PROGRAM_ARB);
Brian Paulb58091a2005-01-09 17:00:57 +000059#endif
Brian Paul24339172003-04-17 19:20:54 +000060 glDisable(GL_LIGHTING);
61 }
62 else {
63 glLightfv(GL_LIGHT0, GL_POSITION, LightPos);
Brian Paulb58091a2005-01-09 17:00:57 +000064#if defined(GL_ARB_fragment_program)
Brian Paul24339172003-04-17 19:20:54 +000065 glDisable(GL_FRAGMENT_PROGRAM_ARB);
66 glDisable(GL_VERTEX_PROGRAM_ARB);
Brian Paulb58091a2005-01-09 17:00:57 +000067#endif
Brian Paul24339172003-04-17 19:20:54 +000068 glEnable(GL_LIGHTING);
69 }
70
71 glPushMatrix();
72 glRotatef(Xrot, 1, 0, 0);
73 glRotatef(Yrot, 0, 1, 0);
74 glutSolidSphere(2.0, 10, 5);
75 glPopMatrix();
76
77 glutSwapBuffers();
Keith Whitwell180e28e2004-04-23 14:06:02 +000078
79 Frames++;
80
81 if (Anim) {
82 GLint t = glutGet(GLUT_ELAPSED_TIME);
83 if (t - T0 >= 5000) {
84 GLfloat seconds = (t - T0) / 1000.0;
85 GLfloat fps = Frames / seconds;
86 printf("%d frames in %6.3f seconds = %6.3f FPS\n", Frames, seconds, fps);
87 T0 = t;
88 Frames = 0;
89 }
90 }
Brian Paul24339172003-04-17 19:20:54 +000091}
92
93
94static void Idle(void)
95{
96 LightPos[0] += Delta;
97 if (LightPos[0] > 25.0)
98 Delta = -1.0;
99 else if (LightPos[0] <- 25.0)
100 Delta = 1.0;
101 glutPostRedisplay();
102}
103
104
105static void Reshape( int width, int height )
106{
107 glViewport( 0, 0, width, height );
108 glMatrixMode( GL_PROJECTION );
109 glLoadIdentity();
110 glFrustum( -1.0, 1.0, -1.0, 1.0, 5.0, 25.0 );
111 glMatrixMode( GL_MODELVIEW );
112 glLoadIdentity();
113 glTranslatef( 0.0, 0.0, -15.0 );
114}
115
116
117static void Key( unsigned char key, int x, int y )
118{
119 (void) x;
120 (void) y;
121 switch (key) {
122 case ' ':
Brian Paul4e41eb12004-02-17 22:00:45 +0000123 case 'a':
Brian Paul24339172003-04-17 19:20:54 +0000124 Anim = !Anim;
125 if (Anim)
126 glutIdleFunc(Idle);
127 else
128 glutIdleFunc(NULL);
129 break;
130 case 'x':
131 LightPos[0] -= 1.0;
132 break;
133 case 'X':
134 LightPos[0] += 1.0;
135 break;
136 case 'w':
137 Wire = !Wire;
138 if (Wire)
139 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
140 else
141 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
142 break;
143 case 'p':
144 PixelLight = !PixelLight;
145 if (PixelLight) {
146 printf("Per-pixel lighting\n");
147 }
148 else {
149 printf("Conventional lighting\n");
150 }
151 break;
152 case 27:
Brian Paula7e6f7b2004-01-13 16:17:21 +0000153 glDeleteProgramsARB_func(1, &VertProg);
154 glDeleteProgramsARB_func(1, &FragProg);
Brian Paul24339172003-04-17 19:20:54 +0000155 exit(0);
156 break;
157 }
158 glutPostRedisplay();
159}
160
161static void SpecialKey( int key, int x, int y )
162{
163 const GLfloat step = 3.0;
164 (void) x;
165 (void) y;
166 switch (key) {
167 case GLUT_KEY_UP:
168 Xrot -= step;
169 break;
170 case GLUT_KEY_DOWN:
171 Xrot += step;
172 break;
173 case GLUT_KEY_LEFT:
174 Yrot -= step;
175 break;
176 case GLUT_KEY_RIGHT:
177 Yrot += step;
178 break;
179 }
180 glutPostRedisplay();
181}
182
183
184/* A helper for finding errors in program strings */
185static int FindLine( const char *program, int position )
186{
187 int i, line = 1;
188 for (i = 0; i < position; i++) {
189 if (program[i] == '\n')
190 line++;
191 }
192 return line;
193}
194
195
196static void Init( void )
197{
198 GLint errorPos;
199
200 /* Yes, this could be expressed more efficiently */
201 static const char *fragProgramText =
202 "!!ARBfp1.0\n"
Brian Paul9a389d42004-02-17 17:59:59 +0000203#if DO_FRAGMENT_FOG
204 "OPTION ARB_fog_linear; \n"
205#endif
Brian Paulf6a93d32004-03-22 16:27:13 +0000206 "PARAM Diffuse = state.material.diffuse; \n"
207 "PARAM Specular = state.material.specular; \n"
Brian Paul24339172003-04-17 19:20:54 +0000208 "PARAM LightPos = program.local[3]; \n"
209 "TEMP lightDir, normal, len; \n"
210 "TEMP dotProd, specAtten; \n"
211 "TEMP diffuseColor, specularColor; \n"
212
213 "# Compute normalized light direction \n"
214 "DP3 len.x, LightPos, LightPos; \n"
215 "RSQ len.y, len.x; \n"
216 "MUL lightDir, LightPos, len.y; \n"
217
218 "# Compute normalized normal \n"
219 "DP3 len.x, fragment.texcoord[0], fragment.texcoord[0]; \n"
220 "RSQ len.y, len.x; \n"
221 "MUL normal, fragment.texcoord[0], len.y; \n"
222
223 "# Compute dot product of light direction and normal vector\n"
Brian Paul244adeb2004-12-15 00:54:17 +0000224 "DP3_SAT dotProd, lightDir, normal; # limited to [0,1]\n"
Brian Paul24339172003-04-17 19:20:54 +0000225
226 "MUL diffuseColor, Diffuse, dotProd; # diffuse attenuation\n"
227
228 "POW specAtten.x, dotProd.x, {20.0}.x; # specular exponent\n"
229
230 "MUL specularColor, Specular, specAtten.x; # specular attenuation\n"
231
Brian Paul9a389d42004-02-17 17:59:59 +0000232#if DO_FRAGMENT_FOG
233 "# need to clamp color to [0,1] before fogging \n"
234 "ADD_SAT result.color, diffuseColor, specularColor; # add colors\n"
235#else
236 "# clamping will be done after program's finished \n"
Brian Paul24339172003-04-17 19:20:54 +0000237 "ADD result.color, diffuseColor, specularColor; # add colors\n"
Brian Paul9a389d42004-02-17 17:59:59 +0000238#endif
Brian Paul24339172003-04-17 19:20:54 +0000239 "END \n"
240 ;
241
242 static const char *vertProgramText =
243 "!!ARBvp1.0\n"
244 "ATTRIB pos = vertex.position; \n"
245 "ATTRIB norm = vertex.normal; \n"
Brian Paul4e41eb12004-02-17 22:00:45 +0000246 "PARAM modelview[4] = { state.matrix.modelview }; \n"
Brian Paul24339172003-04-17 19:20:54 +0000247 "PARAM modelviewProj[4] = { state.matrix.mvp }; \n"
248 "PARAM invModelview[4] = { state.matrix.modelview.invtrans }; \n"
249
250 "# typical modelview/projection transform \n"
251 "DP4 result.position.x, pos, modelviewProj[0]; \n"
252 "DP4 result.position.y, pos, modelviewProj[1]; \n"
253 "DP4 result.position.z, pos, modelviewProj[2]; \n"
254 "DP4 result.position.w, pos, modelviewProj[3]; \n"
255
256 "# transform normal by inv transpose of modelview, put in tex0 \n"
Brian Paulb3680df2003-08-31 19:06:10 +0000257 "DP3 result.texcoord[0].x, norm, invModelview[0]; \n"
258 "DP3 result.texcoord[0].y, norm, invModelview[1]; \n"
259 "DP3 result.texcoord[0].z, norm, invModelview[2]; \n"
260 "DP3 result.texcoord[0].w, norm, invModelview[3]; \n"
Brian Paul24339172003-04-17 19:20:54 +0000261
Brian Paul9a389d42004-02-17 17:59:59 +0000262#if DO_FRAGMENT_FOG
Brian Paul4e41eb12004-02-17 22:00:45 +0000263 "# compute fog coordinate = vertex eye-space Z coord (negated)\n"
264 "DP4 result.fogcoord, -pos, modelview[2]; \n"
Brian Paul9a389d42004-02-17 17:59:59 +0000265#endif
Brian Paul24339172003-04-17 19:20:54 +0000266 "END\n";
267 ;
268
269 if (!glutExtensionSupported("GL_ARB_vertex_program")) {
270 printf("Sorry, this demo requires GL_ARB_vertex_program\n");
271 exit(1);
272 }
273 if (!glutExtensionSupported("GL_ARB_fragment_program")) {
274 printf("Sorry, this demo requires GL_ARB_fragment_program\n");
275 exit(1);
276 }
277
278 /*
279 * Get extension function pointers.
280 */
281 glProgramLocalParameter4fvARB_func = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) glutGetProcAddress("glProgramLocalParameter4fvARB");
282 assert(glProgramLocalParameter4fvARB_func);
283
Brian Paul3ca3ab02003-04-17 21:43:55 +0000284 glProgramLocalParameter4dARB_func = (PFNGLPROGRAMLOCALPARAMETER4DARBPROC) glutGetProcAddress("glProgramLocalParameter4dARB");
285 assert(glProgramLocalParameter4dARB_func);
286
287 glGetProgramLocalParameterdvARB_func = (PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) glutGetProcAddress("glGetProgramLocalParameterdvARB");
288 assert(glGetProgramLocalParameterdvARB_func);
289
Brian Paul24339172003-04-17 19:20:54 +0000290 glGenProgramsARB_func = (PFNGLGENPROGRAMSARBPROC) glutGetProcAddress("glGenProgramsARB");
291 assert(glGenProgramsARB_func);
292
293 glProgramStringARB_func = (PFNGLPROGRAMSTRINGARBPROC) glutGetProcAddress("glProgramStringARB");
294 assert(glProgramStringARB_func);
295
296 glBindProgramARB_func = (PFNGLBINDPROGRAMARBPROC) glutGetProcAddress("glBindProgramARB");
297 assert(glBindProgramARB_func);
298
299 glIsProgramARB_func = (PFNGLISPROGRAMARBPROC) glutGetProcAddress("glIsProgramARB");
300 assert(glIsProgramARB_func);
301
Brian Paula7e6f7b2004-01-13 16:17:21 +0000302 glDeleteProgramsARB_func = (PFNGLDELETEPROGRAMSARBPROC) glutGetProcAddress("glDeleteProgramsARB");
303 assert(glDeleteProgramsARB_func);
304
Brian Paulb58091a2005-01-09 17:00:57 +0000305#if defined(GL_ARB_fragment_program)
Brian Paul24339172003-04-17 19:20:54 +0000306 /*
307 * Fragment program
308 */
309 glGenProgramsARB_func(1, &FragProg);
310 assert(FragProg > 0);
311 glBindProgramARB_func(GL_FRAGMENT_PROGRAM_ARB, FragProg);
312 glProgramStringARB_func(GL_FRAGMENT_PROGRAM_ARB,
313 GL_PROGRAM_FORMAT_ASCII_ARB,
314 strlen(fragProgramText),
315 (const GLubyte *) fragProgramText);
316 glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos);
317 if (glGetError() != GL_NO_ERROR || errorPos != -1) {
318 int l = FindLine(fragProgramText, errorPos);
319 printf("Fragment Program Error (pos=%d line=%d): %s\n", errorPos, l,
320 (char *) glGetString(GL_PROGRAM_ERROR_STRING_ARB));
321 exit(0);
322 }
323 assert(glIsProgramARB_func(FragProg));
324
Brian Paul24339172003-04-17 19:20:54 +0000325 /*
326 * Do some sanity tests
327 */
328 {
329 GLdouble v[4];
Brian Paul3ca3ab02003-04-17 21:43:55 +0000330 glProgramLocalParameter4dARB_func(GL_FRAGMENT_PROGRAM_ARB, 8,
Brian Paul24339172003-04-17 19:20:54 +0000331 10.0, 20.0, 30.0, 40.0);
Brian Paul3ca3ab02003-04-17 21:43:55 +0000332 glGetProgramLocalParameterdvARB_func(GL_FRAGMENT_PROGRAM_ARB, 8, v);
Brian Paul24339172003-04-17 19:20:54 +0000333 assert(v[0] == 10.0);
334 assert(v[1] == 20.0);
335 assert(v[2] == 30.0);
336 assert(v[3] == 40.0);
Brian Paul24339172003-04-17 19:20:54 +0000337 }
338
339 /*
340 * Vertex program
341 */
342 glGenProgramsARB_func(1, &VertProg);
343 assert(VertProg > 0);
344 glBindProgramARB_func(GL_VERTEX_PROGRAM_ARB, VertProg);
345 glProgramStringARB_func(GL_VERTEX_PROGRAM_ARB,
346 GL_PROGRAM_FORMAT_ASCII_ARB,
347 strlen(vertProgramText),
348 (const GLubyte *) vertProgramText);
349 glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos);
350 if (glGetError() != GL_NO_ERROR || errorPos != -1) {
351 int l = FindLine(fragProgramText, errorPos);
352 printf("Vertex Program Error (pos=%d line=%d): %s\n", errorPos, l,
353 (char *) glGetString(GL_PROGRAM_ERROR_STRING_ARB));
354 exit(0);
355 }
356 assert(glIsProgramARB_func(VertProg));
Brian Paulb58091a2005-01-09 17:00:57 +0000357#endif
Brian Paul24339172003-04-17 19:20:54 +0000358
359 /*
360 * Misc init
361 */
362 glClearColor(0.3, 0.3, 0.3, 0.0);
363 glEnable(GL_DEPTH_TEST);
364 glEnable(GL_LIGHT0);
365 glEnable(GL_LIGHTING);
366 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Diffuse);
367 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, Specular);
368 glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 20.0);
369
Brian Paul9a389d42004-02-17 17:59:59 +0000370#if DO_FRAGMENT_FOG
371 {
372 /* Green-ish fog color */
373 static const GLfloat fogColor[4] = {0.5, 1.0, 0.5, 0};
374 glFogi(GL_FOG_MODE, GL_LINEAR);
375 glFogfv(GL_FOG_COLOR, fogColor);
376 glFogf(GL_FOG_START, 5.0);
Brian Paul4e41eb12004-02-17 22:00:45 +0000377 glFogf(GL_FOG_END, 25.0);
Brian Paul9a389d42004-02-17 17:59:59 +0000378 glEnable(GL_FOG);
379 }
380#endif
381
Brian Paul24339172003-04-17 19:20:54 +0000382 printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER));
383 printf("Press p to toggle between per-pixel and per-vertex lighting\n");
384}
385
386
387int main( int argc, char *argv[] )
388{
389 glutInit( &argc, argv );
390 glutInitWindowPosition( 0, 0 );
391 glutInitWindowSize( 200, 200 );
392 glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH );
393 glutCreateWindow(argv[0]);
394 glutReshapeFunc( Reshape );
395 glutKeyboardFunc( Key );
396 glutSpecialFunc( SpecialKey );
397 glutDisplayFunc( Redisplay );
398 if (Anim)
399 glutIdleFunc(Idle);
400 Init();
401 glutMainLoop();
402 return 0;
403}