| /* |
| * Use GL_ARB_fragment_program and GL_ARB_vertex_program to implement |
| * simple per-pixel lighting. |
| * |
| * Brian Paul |
| * 17 April 2003 |
| */ |
| |
| #include <assert.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <math.h> |
| #include <GL/glut.h> |
| |
| |
| static GLfloat Diffuse[4] = { 0.5, 0.5, 1.0, 1.0 }; |
| static GLfloat Specular[4] = { 0.8, 0.8, 0.8, 1.0 }; |
| static GLfloat LightPos[4] = { 0.0, 10.0, 20.0, 1.0 }; |
| static GLfloat Delta = 1.0; |
| |
| static GLuint FragProg; |
| static GLuint VertProg; |
| static GLboolean Anim = GL_TRUE; |
| static GLboolean Wire = GL_FALSE; |
| static GLboolean PixelLight = GL_TRUE; |
| static GLint Win; |
| |
| static GLint T0 = 0; |
| static GLint Frames = 0; |
| |
| static GLfloat Xrot = 0, Yrot = 0; |
| |
| static PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glProgramLocalParameter4fvARB_func; |
| static PFNGLPROGRAMLOCALPARAMETER4DARBPROC glProgramLocalParameter4dARB_func; |
| static PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glGetProgramLocalParameterdvARB_func; |
| static PFNGLGENPROGRAMSARBPROC glGenProgramsARB_func; |
| static PFNGLPROGRAMSTRINGARBPROC glProgramStringARB_func; |
| static PFNGLBINDPROGRAMARBPROC glBindProgramARB_func; |
| static PFNGLISPROGRAMARBPROC glIsProgramARB_func; |
| static PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB_func; |
| |
| /* These must match the indexes used in the fragment program */ |
| #define LIGHTPOS 3 |
| |
| /* Set to one to test ARB_fog_linear program option */ |
| #define DO_FRAGMENT_FOG 0 |
| |
| static void normalize (GLfloat *dst, const GLfloat *src) |
| { |
| GLfloat len = sqrt (src[0] * src[0] + src[1] * src[1] + src[2] * src[2]); |
| dst[0] = src[0] / len; |
| dst[1] = src[1] / len; |
| dst[2] = src[2] / len; |
| } |
| |
| static void Redisplay( void ) |
| { |
| glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); |
| |
| if (PixelLight) { |
| GLfloat pos[4]; |
| |
| normalize( pos, LightPos ); |
| pos[3] = LightPos[3]; |
| glProgramLocalParameter4fvARB_func(GL_FRAGMENT_PROGRAM_ARB, |
| LIGHTPOS, pos); |
| glEnable(GL_FRAGMENT_PROGRAM_ARB); |
| glEnable(GL_VERTEX_PROGRAM_ARB); |
| glDisable(GL_LIGHTING); |
| } |
| else { |
| glLightfv(GL_LIGHT0, GL_POSITION, LightPos); |
| glDisable(GL_FRAGMENT_PROGRAM_ARB); |
| glDisable(GL_VERTEX_PROGRAM_ARB); |
| glEnable(GL_LIGHTING); |
| } |
| |
| glPushMatrix(); |
| glRotatef(Xrot, 1, 0, 0); |
| glRotatef(Yrot, 0, 1, 0); |
| glutSolidSphere(2.0, 10, 5); |
| glPopMatrix(); |
| |
| glutSwapBuffers(); |
| |
| Frames++; |
| |
| if (Anim) { |
| GLint t = glutGet(GLUT_ELAPSED_TIME); |
| if (t - T0 >= 5000) { |
| GLfloat seconds = (t - T0) / 1000.0; |
| GLfloat fps = Frames / seconds; |
| printf("%d frames in %6.3f seconds = %6.3f FPS\n", Frames, seconds, fps); |
| T0 = t; |
| Frames = 0; |
| } |
| } |
| } |
| |
| |
| static void Idle(void) |
| { |
| LightPos[0] += Delta; |
| if (LightPos[0] > 25.0) |
| Delta = -1.0; |
| else if (LightPos[0] <- 25.0) |
| Delta = 1.0; |
| glutPostRedisplay(); |
| } |
| |
| |
| static void Reshape( int width, int height ) |
| { |
| glViewport( 0, 0, width, height ); |
| glMatrixMode( GL_PROJECTION ); |
| glLoadIdentity(); |
| glFrustum( -1.0, 1.0, -1.0, 1.0, 5.0, 25.0 ); |
| glMatrixMode( GL_MODELVIEW ); |
| glLoadIdentity(); |
| glTranslatef( 0.0, 0.0, -15.0 ); |
| } |
| |
| |
| static void Key( unsigned char key, int x, int y ) |
| { |
| (void) x; |
| (void) y; |
| switch (key) { |
| case ' ': |
| case 'a': |
| Anim = !Anim; |
| if (Anim) |
| glutIdleFunc(Idle); |
| else |
| glutIdleFunc(NULL); |
| break; |
| case 'x': |
| LightPos[0] -= 1.0; |
| break; |
| case 'X': |
| LightPos[0] += 1.0; |
| break; |
| case 'w': |
| Wire = !Wire; |
| if (Wire) |
| glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); |
| else |
| glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); |
| break; |
| case 'p': |
| PixelLight = !PixelLight; |
| if (PixelLight) { |
| printf("Per-pixel lighting\n"); |
| } |
| else { |
| printf("Conventional lighting\n"); |
| } |
| break; |
| case 27: |
| glDeleteProgramsARB_func(1, &VertProg); |
| glDeleteProgramsARB_func(1, &FragProg); |
| glutDestroyWindow(Win); |
| exit(0); |
| break; |
| } |
| glutPostRedisplay(); |
| } |
| |
| static void SpecialKey( int key, int x, int y ) |
| { |
| const GLfloat step = 3.0; |
| (void) x; |
| (void) y; |
| switch (key) { |
| case GLUT_KEY_UP: |
| Xrot -= step; |
| break; |
| case GLUT_KEY_DOWN: |
| Xrot += step; |
| break; |
| case GLUT_KEY_LEFT: |
| Yrot -= step; |
| break; |
| case GLUT_KEY_RIGHT: |
| Yrot += step; |
| break; |
| } |
| glutPostRedisplay(); |
| } |
| |
| |
| /* A helper for finding errors in program strings */ |
| static int FindLine( const char *program, int position ) |
| { |
| int i, line = 1; |
| for (i = 0; i < position; i++) { |
| if (program[i] == '\n') |
| line++; |
| } |
| return line; |
| } |
| |
| |
| static void Init( void ) |
| { |
| GLint errorPos; |
| |
| /* Yes, this could be expressed more efficiently */ |
| static const char *fragProgramText = |
| "!!ARBfp1.0\n" |
| #if DO_FRAGMENT_FOG |
| "OPTION ARB_fog_linear; \n" |
| #endif |
| "PARAM Diffuse = state.material.diffuse; \n" |
| "PARAM Specular = state.material.specular; \n" |
| "PARAM LightPos = program.local[3]; \n" |
| "TEMP normal, len; \n" |
| "TEMP dotProd, specAtten; \n" |
| "TEMP diffuseColor, specularColor; \n" |
| |
| "# Compute normalized normal \n" |
| "DP3 len.x, fragment.texcoord[0], fragment.texcoord[0]; \n" |
| "RSQ len.y, len.x; \n" |
| "MUL normal.xyz, fragment.texcoord[0], len.y; \n" |
| |
| "# Compute dot product of light direction and normal vector\n" |
| "DP3_SAT dotProd.x, LightPos, normal; # limited to [0,1]\n" |
| |
| "MUL diffuseColor.xyz, Diffuse, dotProd.x; # diffuse attenuation\n" |
| |
| "POW specAtten.x, dotProd.x, {20.0}.x; # specular exponent\n" |
| |
| "MUL specularColor.xyz, Specular, specAtten.x; # specular attenuation\n" |
| |
| "MOV result.color.w, Diffuse; \n" |
| #if DO_FRAGMENT_FOG |
| "# need to clamp color to [0,1] before fogging \n" |
| "ADD_SAT result.color.xyz, diffuseColor, specularColor; # add colors\n" |
| #else |
| "# clamping will be done after program's finished \n" |
| "ADD result.color.xyz, diffuseColor, specularColor; # add colors\n" |
| #endif |
| "END \n" |
| ; |
| |
| static const char *vertProgramText = |
| "!!ARBvp1.0\n" |
| "ATTRIB pos = vertex.position; \n" |
| "ATTRIB norm = vertex.normal; \n" |
| "PARAM modelview[4] = { state.matrix.modelview }; \n" |
| "PARAM modelviewProj[4] = { state.matrix.mvp }; \n" |
| "PARAM invModelview[4] = { state.matrix.modelview.invtrans }; \n" |
| |
| "# typical modelview/projection transform \n" |
| "DP4 result.position.x, pos, modelviewProj[0]; \n" |
| "DP4 result.position.y, pos, modelviewProj[1]; \n" |
| "DP4 result.position.z, pos, modelviewProj[2]; \n" |
| "DP4 result.position.w, pos, modelviewProj[3]; \n" |
| |
| "# transform normal by inv transpose of modelview, put in tex0 \n" |
| "DP3 result.texcoord[0].x, norm, invModelview[0]; \n" |
| "DP3 result.texcoord[0].y, norm, invModelview[1]; \n" |
| "DP3 result.texcoord[0].z, norm, invModelview[2]; \n" |
| "DP3 result.texcoord[0].w, norm, invModelview[3]; \n" |
| |
| #if DO_FRAGMENT_FOG |
| "# compute fog coordinate = vertex eye-space Z coord (negated)\n" |
| "DP4 result.fogcoord, -pos, modelview[2]; \n" |
| #endif |
| "END\n"; |
| ; |
| |
| if (!glutExtensionSupported("GL_ARB_vertex_program")) { |
| printf("Sorry, this demo requires GL_ARB_vertex_program\n"); |
| exit(1); |
| } |
| if (!glutExtensionSupported("GL_ARB_fragment_program")) { |
| printf("Sorry, this demo requires GL_ARB_fragment_program\n"); |
| exit(1); |
| } |
| |
| /* |
| * Get extension function pointers. |
| */ |
| glProgramLocalParameter4fvARB_func = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) glutGetProcAddress("glProgramLocalParameter4fvARB"); |
| assert(glProgramLocalParameter4fvARB_func); |
| |
| glProgramLocalParameter4dARB_func = (PFNGLPROGRAMLOCALPARAMETER4DARBPROC) glutGetProcAddress("glProgramLocalParameter4dARB"); |
| assert(glProgramLocalParameter4dARB_func); |
| |
| glGetProgramLocalParameterdvARB_func = (PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) glutGetProcAddress("glGetProgramLocalParameterdvARB"); |
| assert(glGetProgramLocalParameterdvARB_func); |
| |
| glGenProgramsARB_func = (PFNGLGENPROGRAMSARBPROC) glutGetProcAddress("glGenProgramsARB"); |
| assert(glGenProgramsARB_func); |
| |
| glProgramStringARB_func = (PFNGLPROGRAMSTRINGARBPROC) glutGetProcAddress("glProgramStringARB"); |
| assert(glProgramStringARB_func); |
| |
| glBindProgramARB_func = (PFNGLBINDPROGRAMARBPROC) glutGetProcAddress("glBindProgramARB"); |
| assert(glBindProgramARB_func); |
| |
| glIsProgramARB_func = (PFNGLISPROGRAMARBPROC) glutGetProcAddress("glIsProgramARB"); |
| assert(glIsProgramARB_func); |
| |
| glDeleteProgramsARB_func = (PFNGLDELETEPROGRAMSARBPROC) glutGetProcAddress("glDeleteProgramsARB"); |
| assert(glDeleteProgramsARB_func); |
| |
| /* |
| * Fragment program |
| */ |
| glGenProgramsARB_func(1, &FragProg); |
| assert(FragProg > 0); |
| glBindProgramARB_func(GL_FRAGMENT_PROGRAM_ARB, FragProg); |
| glProgramStringARB_func(GL_FRAGMENT_PROGRAM_ARB, |
| GL_PROGRAM_FORMAT_ASCII_ARB, |
| strlen(fragProgramText), |
| (const GLubyte *) fragProgramText); |
| glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos); |
| if (glGetError() != GL_NO_ERROR || errorPos != -1) { |
| int l = FindLine(fragProgramText, errorPos); |
| printf("Fragment Program Error (pos=%d line=%d): %s\n", errorPos, l, |
| (char *) glGetString(GL_PROGRAM_ERROR_STRING_ARB)); |
| exit(0); |
| } |
| assert(glIsProgramARB_func(FragProg)); |
| |
| /* |
| * Do some sanity tests |
| */ |
| { |
| GLdouble v[4]; |
| glProgramLocalParameter4dARB_func(GL_FRAGMENT_PROGRAM_ARB, 8, |
| 10.0, 20.0, 30.0, 40.0); |
| glGetProgramLocalParameterdvARB_func(GL_FRAGMENT_PROGRAM_ARB, 8, v); |
| assert(v[0] == 10.0); |
| assert(v[1] == 20.0); |
| assert(v[2] == 30.0); |
| assert(v[3] == 40.0); |
| } |
| |
| /* |
| * Vertex program |
| */ |
| glGenProgramsARB_func(1, &VertProg); |
| assert(VertProg > 0); |
| glBindProgramARB_func(GL_VERTEX_PROGRAM_ARB, VertProg); |
| glProgramStringARB_func(GL_VERTEX_PROGRAM_ARB, |
| GL_PROGRAM_FORMAT_ASCII_ARB, |
| strlen(vertProgramText), |
| (const GLubyte *) vertProgramText); |
| glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos); |
| if (glGetError() != GL_NO_ERROR || errorPos != -1) { |
| int l = FindLine(vertProgramText, errorPos); |
| printf("Vertex Program Error (pos=%d line=%d): %s\n", errorPos, l, |
| (char *) glGetString(GL_PROGRAM_ERROR_STRING_ARB)); |
| exit(0); |
| } |
| assert(glIsProgramARB_func(VertProg)); |
| |
| /* |
| * Misc init |
| */ |
| glClearColor(0.3, 0.3, 0.3, 0.0); |
| glEnable(GL_DEPTH_TEST); |
| glEnable(GL_LIGHT0); |
| glEnable(GL_LIGHTING); |
| glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Diffuse); |
| glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, Specular); |
| glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 20.0); |
| |
| #if DO_FRAGMENT_FOG |
| { |
| /* Green-ish fog color */ |
| static const GLfloat fogColor[4] = {0.5, 1.0, 0.5, 0}; |
| glFogfv(GL_FOG_COLOR, fogColor); |
| glFogf(GL_FOG_START, 5.0); |
| glFogf(GL_FOG_END, 25.0); |
| } |
| #endif |
| |
| printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER)); |
| printf("Press p to toggle between per-pixel and per-vertex lighting\n"); |
| } |
| |
| |
| int main( int argc, char *argv[] ) |
| { |
| glutInit( &argc, argv ); |
| glutInitWindowPosition( 0, 0 ); |
| glutInitWindowSize( 200, 200 ); |
| glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); |
| Win = glutCreateWindow(argv[0]); |
| glutReshapeFunc( Reshape ); |
| glutKeyboardFunc( Key ); |
| glutSpecialFunc( SpecialKey ); |
| glutDisplayFunc( Redisplay ); |
| if (Anim) |
| glutIdleFunc(Idle); |
| Init(); |
| glutMainLoop(); |
| return 0; |
| } |