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