132001f49Smrg/*
232001f49Smrg * Use GL_ARB_fragment_program and GL_ARB_vertex_program to implement
332001f49Smrg * simple per-pixel lighting.
432001f49Smrg *
532001f49Smrg * Brian Paul
632001f49Smrg * 17 April 2003
732001f49Smrg */
832001f49Smrg
932001f49Smrg#include <assert.h>
1032001f49Smrg#include <string.h>
1132001f49Smrg#include <stdio.h>
1232001f49Smrg#include <stdlib.h>
1332001f49Smrg#include <math.h>
1432001f49Smrg#include <GL/glew.h>
1532001f49Smrg#include "glut_wrap.h"
1632001f49Smrg
1732001f49Smrg
1832001f49Smrgstatic GLfloat Diffuse[4] = { 0.5, 0.5, 1.0, 1.0 };
1932001f49Smrgstatic GLfloat Specular[4] = { 0.8, 0.8, 0.8, 1.0 };
2032001f49Smrgstatic GLfloat LightPos[4] = { 0.0, 10.0, 20.0, 1.0 };
2132001f49Smrgstatic GLfloat Delta = 1.0;
2232001f49Smrg
2332001f49Smrgstatic GLuint FragProg;
2432001f49Smrgstatic GLuint VertProg;
2532001f49Smrgstatic GLboolean Anim = GL_TRUE;
2632001f49Smrgstatic GLboolean Wire = GL_FALSE;
2732001f49Smrgstatic GLboolean PixelLight = GL_TRUE;
2832001f49Smrgstatic GLint Win;
2932001f49Smrg
3032001f49Smrgstatic GLint T0 = 0;
3132001f49Smrgstatic GLint Frames = 0;
3232001f49Smrg
3332001f49Smrgstatic GLfloat Xrot = 0, Yrot = 0;
3432001f49Smrg
3532001f49Smrg
3632001f49Smrg/* These must match the indexes used in the fragment program */
3732001f49Smrg#define LIGHTPOS 3
3832001f49Smrg
3932001f49Smrg/* Set to one to test ARB_fog_linear program option */
4032001f49Smrg#define DO_FRAGMENT_FOG 0
4132001f49Smrg
4232001f49Smrgstatic void normalize (GLfloat *dst, const GLfloat *src)
4332001f49Smrg{
4432001f49Smrg   GLfloat len = sqrt (src[0] * src[0] + src[1] * src[1] + src[2] * src[2]);
4532001f49Smrg   dst[0] = src[0] / len;
4632001f49Smrg   dst[1] = src[1] / len;
4732001f49Smrg   dst[2] = src[2] / len;
4832001f49Smrg}
4932001f49Smrg
5032001f49Smrgstatic void Redisplay( void )
5132001f49Smrg{
5232001f49Smrg   glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
5332001f49Smrg
5432001f49Smrg   if (PixelLight) {
5532001f49Smrg      GLfloat pos[4];
5632001f49Smrg
5732001f49Smrg      normalize( pos, LightPos );
5832001f49Smrg      pos[3] = LightPos[3];
5932001f49Smrg      glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB,
6032001f49Smrg                                         LIGHTPOS, pos);
6132001f49Smrg      glEnable(GL_FRAGMENT_PROGRAM_ARB);
6232001f49Smrg      glEnable(GL_VERTEX_PROGRAM_ARB);
6332001f49Smrg      glDisable(GL_LIGHTING);
6432001f49Smrg   }
6532001f49Smrg   else {
6632001f49Smrg      glLightfv(GL_LIGHT0, GL_POSITION, LightPos);
6732001f49Smrg      glDisable(GL_FRAGMENT_PROGRAM_ARB);
6832001f49Smrg      glDisable(GL_VERTEX_PROGRAM_ARB);
6932001f49Smrg      glEnable(GL_LIGHTING);
7032001f49Smrg   }
7132001f49Smrg
7232001f49Smrg   glPushMatrix();
7332001f49Smrg   glRotatef(Xrot, 1, 0, 0);
7432001f49Smrg   glRotatef(Yrot, 0, 1, 0);
7532001f49Smrg   glutSolidSphere(2.0, 10, 5);
7632001f49Smrg   glPopMatrix();
7732001f49Smrg
7832001f49Smrg   glutSwapBuffers();
7932001f49Smrg
8032001f49Smrg   Frames++;
8132001f49Smrg
8232001f49Smrg   if (Anim) {
8332001f49Smrg      GLint t = glutGet(GLUT_ELAPSED_TIME);
8432001f49Smrg      if (t - T0 >= 5000) {
8532001f49Smrg	 GLfloat seconds = (t - T0) / 1000.0;
8632001f49Smrg	 GLfloat fps = Frames / seconds;
8732001f49Smrg	 printf("%d frames in %6.3f seconds = %6.3f FPS\n", Frames, seconds, fps);
8832001f49Smrg         fflush(stdout);
8932001f49Smrg	 T0 = t;
9032001f49Smrg	 Frames = 0;
9132001f49Smrg      }
9232001f49Smrg   }
9332001f49Smrg}
9432001f49Smrg
9532001f49Smrg
9632001f49Smrgstatic void Idle(void)
9732001f49Smrg{
9832001f49Smrg   LightPos[0] += Delta;
9932001f49Smrg   if (LightPos[0] > 25.0)
10032001f49Smrg      Delta = -1.0;
10132001f49Smrg   else if (LightPos[0] <- 25.0)
10232001f49Smrg      Delta = 1.0;
10332001f49Smrg   glutPostRedisplay();
10432001f49Smrg}
10532001f49Smrg
10632001f49Smrg
10732001f49Smrgstatic void Reshape( int width, int height )
10832001f49Smrg{
10932001f49Smrg   glViewport( 0, 0, width, height );
11032001f49Smrg   glMatrixMode( GL_PROJECTION );
11132001f49Smrg   glLoadIdentity();
11232001f49Smrg   glFrustum( -1.0, 1.0, -1.0, 1.0, 5.0, 25.0 );
11332001f49Smrg   glMatrixMode( GL_MODELVIEW );
11432001f49Smrg   glLoadIdentity();
11532001f49Smrg   glTranslatef( 0.0, 0.0, -15.0 );
11632001f49Smrg}
11732001f49Smrg
11832001f49Smrg
11932001f49Smrgstatic void Key( unsigned char key, int x, int y )
12032001f49Smrg{
12132001f49Smrg   (void) x;
12232001f49Smrg   (void) y;
12332001f49Smrg   switch (key) {
12432001f49Smrg     case ' ':
12532001f49Smrg     case 'a':
12632001f49Smrg        Anim = !Anim;
12732001f49Smrg        if (Anim)
12832001f49Smrg           glutIdleFunc(Idle);
12932001f49Smrg        else
13032001f49Smrg           glutIdleFunc(NULL);
13132001f49Smrg        break;
13232001f49Smrg      case 'x':
13332001f49Smrg         LightPos[0] -= 1.0;
13432001f49Smrg         break;
13532001f49Smrg      case 'X':
13632001f49Smrg         LightPos[0] += 1.0;
13732001f49Smrg         break;
13832001f49Smrg      case 'w':
13932001f49Smrg         Wire = !Wire;
14032001f49Smrg         if (Wire)
14132001f49Smrg            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
14232001f49Smrg         else
14332001f49Smrg            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
14432001f49Smrg         break;
14532001f49Smrg      case 'p':
14632001f49Smrg         PixelLight = !PixelLight;
14732001f49Smrg         if (PixelLight) {
14832001f49Smrg            printf("Per-pixel lighting\n");
14932001f49Smrg         }
15032001f49Smrg         else {
15132001f49Smrg            printf("Conventional lighting\n");
15232001f49Smrg         }
15332001f49Smrg         break;
15432001f49Smrg      case 27:
15532001f49Smrg         glDeleteProgramsARB(1, &VertProg);
15632001f49Smrg         glDeleteProgramsARB(1, &FragProg);
15732001f49Smrg         glutDestroyWindow(Win);
15832001f49Smrg         exit(0);
15932001f49Smrg         break;
16032001f49Smrg   }
16132001f49Smrg   glutPostRedisplay();
16232001f49Smrg}
16332001f49Smrg
16432001f49Smrgstatic void SpecialKey( int key, int x, int y )
16532001f49Smrg{
16632001f49Smrg   const GLfloat step = 3.0;
16732001f49Smrg   (void) x;
16832001f49Smrg   (void) y;
16932001f49Smrg   switch (key) {
17032001f49Smrg      case GLUT_KEY_UP:
17132001f49Smrg         Xrot -= step;
17232001f49Smrg         break;
17332001f49Smrg      case GLUT_KEY_DOWN:
17432001f49Smrg         Xrot += step;
17532001f49Smrg         break;
17632001f49Smrg      case GLUT_KEY_LEFT:
17732001f49Smrg         Yrot -= step;
17832001f49Smrg         break;
17932001f49Smrg      case GLUT_KEY_RIGHT:
18032001f49Smrg         Yrot += step;
18132001f49Smrg         break;
18232001f49Smrg   }
18332001f49Smrg   glutPostRedisplay();
18432001f49Smrg}
18532001f49Smrg
18632001f49Smrg
18732001f49Smrg/* A helper for finding errors in program strings */
18832001f49Smrgstatic int FindLine( const char *program, int position )
18932001f49Smrg{
19032001f49Smrg   int i, line = 1;
19132001f49Smrg   for (i = 0; i < position; i++) {
19232001f49Smrg      if (program[i] == '\n')
19332001f49Smrg         line++;
19432001f49Smrg   }
19532001f49Smrg   return line;
19632001f49Smrg}
19732001f49Smrg
19832001f49Smrg
19932001f49Smrgstatic void Init( void )
20032001f49Smrg{
20132001f49Smrg   GLint errorPos;
20232001f49Smrg
20332001f49Smrg   /* Yes, this could be expressed more efficiently */
20432001f49Smrg   static const char *fragProgramText =
20532001f49Smrg      "!!ARBfp1.0\n"
20632001f49Smrg#if DO_FRAGMENT_FOG
20732001f49Smrg      "OPTION ARB_fog_linear; \n"
20832001f49Smrg#endif
20932001f49Smrg      "PARAM Diffuse = state.material.diffuse; \n"
21032001f49Smrg      "PARAM Specular = state.material.specular; \n"
21132001f49Smrg      "PARAM LightPos = program.local[3]; \n"
21232001f49Smrg      "TEMP normal, len; \n"
21332001f49Smrg      "TEMP dotProd, specAtten; \n"
21432001f49Smrg      "TEMP diffuseColor, specularColor; \n"
21532001f49Smrg
21632001f49Smrg      "# Compute normalized normal \n"
21732001f49Smrg      "DP3 len.x, fragment.texcoord[0], fragment.texcoord[0]; \n"
21832001f49Smrg      "RSQ len.y, len.x; \n"
21932001f49Smrg      "MUL normal.xyz, fragment.texcoord[0], len.y; \n"
22032001f49Smrg
22132001f49Smrg      "# Compute dot product of light direction and normal vector\n"
22232001f49Smrg      "DP3_SAT dotProd.x, LightPos, normal;             # limited to [0,1]\n"
22332001f49Smrg
22432001f49Smrg      "MUL diffuseColor.xyz, Diffuse, dotProd.x;            # diffuse attenuation\n"
22532001f49Smrg
22632001f49Smrg      "POW specAtten.x, dotProd.x, {20.0}.x;          # specular exponent\n"
22732001f49Smrg
22832001f49Smrg      "MUL specularColor.xyz, Specular, specAtten.x;      # specular attenuation\n"
22932001f49Smrg
23032001f49Smrg      "MOV result.color.w, Diffuse; \n"
23132001f49Smrg#if DO_FRAGMENT_FOG
23232001f49Smrg      "# need to clamp color to [0,1] before fogging \n"
23332001f49Smrg      "ADD_SAT result.color.xyz, diffuseColor, specularColor; # add colors\n"
23432001f49Smrg#else
23532001f49Smrg      "# clamping will be done after program's finished \n"
23632001f49Smrg      "ADD result.color.xyz, diffuseColor, specularColor; # add colors\n"
23732001f49Smrg#endif
23832001f49Smrg      "END \n"
23932001f49Smrg      ;
24032001f49Smrg
24132001f49Smrg   static const char *vertProgramText =
24232001f49Smrg      "!!ARBvp1.0\n"
24332001f49Smrg      "ATTRIB pos = vertex.position; \n"
24432001f49Smrg      "ATTRIB norm = vertex.normal; \n"
24532001f49Smrg      "PARAM modelview[4] = { state.matrix.modelview }; \n"
24632001f49Smrg      "PARAM modelviewProj[4] = { state.matrix.mvp }; \n"
24732001f49Smrg      "PARAM invModelview[4] = { state.matrix.modelview.invtrans }; \n"
24832001f49Smrg
24932001f49Smrg      "# typical modelview/projection transform \n"
25032001f49Smrg      "DP4 result.position.x, pos, modelviewProj[0]; \n"
25132001f49Smrg      "DP4 result.position.y, pos, modelviewProj[1]; \n"
25232001f49Smrg      "DP4 result.position.z, pos, modelviewProj[2]; \n"
25332001f49Smrg      "DP4 result.position.w, pos, modelviewProj[3]; \n"
25432001f49Smrg
25532001f49Smrg      "# transform normal by inv transpose of modelview, put in tex0 \n"
25632001f49Smrg      "DP3 result.texcoord[0].x, norm, invModelview[0]; \n"
25732001f49Smrg      "DP3 result.texcoord[0].y, norm, invModelview[1]; \n"
25832001f49Smrg      "DP3 result.texcoord[0].z, norm, invModelview[2]; \n"
25932001f49Smrg      "DP3 result.texcoord[0].w, norm, invModelview[3]; \n"
26032001f49Smrg
26132001f49Smrg#if DO_FRAGMENT_FOG
26232001f49Smrg      "# compute fog coordinate = vertex eye-space Z coord (negated)\n"
26332001f49Smrg      "DP4 result.fogcoord, -pos, modelview[2]; \n"
26432001f49Smrg#endif
26532001f49Smrg      "END\n";
26632001f49Smrg      ;
26732001f49Smrg
26832001f49Smrg   if (!glutExtensionSupported("GL_ARB_vertex_program")) {
26932001f49Smrg      printf("Sorry, this demo requires GL_ARB_vertex_program\n");
27032001f49Smrg      exit(1);
27132001f49Smrg   }
27232001f49Smrg   if (!glutExtensionSupported("GL_ARB_fragment_program")) {
27332001f49Smrg      printf("Sorry, this demo requires GL_ARB_fragment_program\n");
27432001f49Smrg      exit(1);
27532001f49Smrg   }
27632001f49Smrg
27732001f49Smrg   /*
27832001f49Smrg    * Fragment program
27932001f49Smrg    */
28032001f49Smrg   glGenProgramsARB(1, &FragProg);
28132001f49Smrg   assert(FragProg > 0);
28232001f49Smrg   glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, FragProg);
28332001f49Smrg   glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB,
28432001f49Smrg                           GL_PROGRAM_FORMAT_ASCII_ARB,
28532001f49Smrg                           strlen(fragProgramText),
28632001f49Smrg                           (const GLubyte *) fragProgramText);
28732001f49Smrg   glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos);
28832001f49Smrg   if (glGetError() != GL_NO_ERROR || errorPos != -1) {
28932001f49Smrg      int l = FindLine(fragProgramText, errorPos);
29032001f49Smrg      printf("Fragment Program Error (pos=%d line=%d): %s\n", errorPos, l,
29132001f49Smrg             (char *) glGetString(GL_PROGRAM_ERROR_STRING_ARB));
29232001f49Smrg      exit(0);
29332001f49Smrg   }
29432001f49Smrg   assert(glIsProgramARB(FragProg));
29532001f49Smrg
29632001f49Smrg   /*
29732001f49Smrg    * Do some sanity tests
29832001f49Smrg    */
29932001f49Smrg   {
30032001f49Smrg      GLdouble v[4];
30132001f49Smrg      glProgramLocalParameter4dARB(GL_FRAGMENT_PROGRAM_ARB, 8,
30232001f49Smrg                                   10.0, 20.0, 30.0, 40.0);
30332001f49Smrg      glGetProgramLocalParameterdvARB(GL_FRAGMENT_PROGRAM_ARB, 8, v);
30432001f49Smrg      assert(v[0] == 10.0);
30532001f49Smrg      assert(v[1] == 20.0);
30632001f49Smrg      assert(v[2] == 30.0);
30732001f49Smrg      assert(v[3] == 40.0);
30832001f49Smrg   }
30932001f49Smrg
31032001f49Smrg   /*
31132001f49Smrg    * Vertex program
31232001f49Smrg    */
31332001f49Smrg   glGenProgramsARB(1, &VertProg);
31432001f49Smrg   assert(VertProg > 0);
31532001f49Smrg   glBindProgramARB(GL_VERTEX_PROGRAM_ARB, VertProg);
31632001f49Smrg   glProgramStringARB(GL_VERTEX_PROGRAM_ARB,
31732001f49Smrg                           GL_PROGRAM_FORMAT_ASCII_ARB,
31832001f49Smrg                           strlen(vertProgramText),
31932001f49Smrg                           (const GLubyte *) vertProgramText);
32032001f49Smrg   glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos);
32132001f49Smrg   if (glGetError() != GL_NO_ERROR || errorPos != -1) {
32232001f49Smrg      int l = FindLine(vertProgramText, errorPos);
32332001f49Smrg      printf("Vertex Program Error (pos=%d line=%d): %s\n", errorPos, l,
32432001f49Smrg             (char *) glGetString(GL_PROGRAM_ERROR_STRING_ARB));
32532001f49Smrg      exit(0);
32632001f49Smrg   }
32732001f49Smrg   assert(glIsProgramARB(VertProg));
32832001f49Smrg
32932001f49Smrg   /*
33032001f49Smrg    * Misc init
33132001f49Smrg    */
33232001f49Smrg   glClearColor(0.3, 0.3, 0.3, 0.0);
33332001f49Smrg   glEnable(GL_DEPTH_TEST);
33432001f49Smrg   glEnable(GL_LIGHT0);
33532001f49Smrg   glEnable(GL_LIGHTING);
33632001f49Smrg   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Diffuse);
33732001f49Smrg   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, Specular);
33832001f49Smrg   glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 20.0);
33932001f49Smrg
34032001f49Smrg#if DO_FRAGMENT_FOG
34132001f49Smrg   {
34232001f49Smrg      /* Green-ish fog color */
34332001f49Smrg      static const GLfloat fogColor[4] = {0.5, 1.0, 0.5, 0};
34432001f49Smrg      glFogfv(GL_FOG_COLOR, fogColor);
34532001f49Smrg      glFogf(GL_FOG_START, 5.0);
34632001f49Smrg      glFogf(GL_FOG_END, 25.0);
34732001f49Smrg   }
34832001f49Smrg#endif
34932001f49Smrg
35032001f49Smrg   printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER));
35132001f49Smrg   printf("Press p to toggle between per-pixel and per-vertex lighting\n");
35232001f49Smrg}
35332001f49Smrg
35432001f49Smrg
35532001f49Smrgint main( int argc, char *argv[] )
35632001f49Smrg{
35732001f49Smrg   glutInitWindowSize( 200, 200 );
35832001f49Smrg   glutInit( &argc, argv );
35932001f49Smrg   glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH );
36032001f49Smrg   Win = glutCreateWindow(argv[0]);
36132001f49Smrg   glewInit();
36232001f49Smrg   glutReshapeFunc( Reshape );
36332001f49Smrg   glutKeyboardFunc( Key );
36432001f49Smrg   glutSpecialFunc( SpecialKey );
36532001f49Smrg   glutDisplayFunc( Redisplay );
36632001f49Smrg   if (Anim)
36732001f49Smrg      glutIdleFunc(Idle);
36832001f49Smrg   Init();
36932001f49Smrg   glutMainLoop();
37032001f49Smrg   return 0;
37132001f49Smrg}
372