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