Brian Paul | 24ea2c2 | 2006-11-03 00:04:06 +0000 | [diff] [blame^] | 1 | /** |
| 2 | * Test OpenGL 2.0 vertex/fragment shaders. |
| 3 | * Brian Paul |
| 4 | * 1 November 2006 |
| 5 | * |
| 6 | * Based on ARB version by: |
| 7 | * Michal Krol |
| 8 | * 20 February 2006 |
| 9 | * |
| 10 | * Based on the original demo by: |
| 11 | * Brian Paul |
| 12 | * 17 April 2003 |
| 13 | */ |
| 14 | |
| 15 | #include <assert.h> |
| 16 | #include <string.h> |
| 17 | #include <stdio.h> |
| 18 | #include <stdlib.h> |
| 19 | #include <math.h> |
| 20 | #include <GL/gl.h> |
| 21 | #include <GL/glut.h> |
| 22 | #include <GL/glext.h> |
| 23 | |
| 24 | static GLfloat diffuse[4] = { 0.5f, 0.5f, 1.0f, 1.0f }; |
| 25 | static GLfloat specular[4] = { 0.8f, 0.8f, 0.8f, 1.0f }; |
| 26 | static GLfloat lightPos[4] = { 0.0f, 10.0f, 20.0f, 1.0f }; |
| 27 | static GLfloat delta = 1.0f; |
| 28 | |
| 29 | static GLuint fragShader; |
| 30 | static GLuint vertShader; |
| 31 | static GLuint program; |
| 32 | |
| 33 | static GLint uLightPos; |
| 34 | static GLint uDiffuse; |
| 35 | static GLint uSpecular; |
| 36 | |
| 37 | static GLint win = 0; |
| 38 | static GLboolean anim = GL_TRUE; |
| 39 | static GLboolean wire = GL_FALSE; |
| 40 | static GLboolean pixelLight = GL_TRUE; |
| 41 | |
| 42 | static GLint t0 = 0; |
| 43 | static GLint frames = 0; |
| 44 | |
| 45 | static GLfloat xRot = 0.0f, yRot = 0.0f; |
| 46 | |
| 47 | static PFNGLCREATESHADERPROC glCreateShader_func = NULL; |
| 48 | static PFNGLSHADERSOURCEPROC glShaderSource_func = NULL; |
| 49 | static PFNGLGETSHADERSOURCEPROC glGetShaderSource_func = NULL; |
| 50 | static PFNGLCOMPILESHADERPROC glCompileShader_func = NULL; |
| 51 | static PFNGLCREATEPROGRAMPROC glCreateProgram_func = NULL; |
| 52 | static PFNGLDELETEPROGRAMPROC glDeleteProgram_func = NULL; |
| 53 | static PFNGLDELETESHADERPROC glDeleteShader_func = NULL; |
| 54 | static PFNGLATTACHSHADERPROC glAttachShader_func = NULL; |
| 55 | static PFNGLLINKPROGRAMPROC glLinkProgram_func = NULL; |
| 56 | static PFNGLUSEPROGRAMPROC glUseProgram_func = NULL; |
| 57 | static PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation_func = NULL; |
| 58 | static PFNGLISPROGRAMPROC glIsProgram_func = NULL; |
| 59 | static PFNGLISSHADERPROC glIsShader_func = NULL; |
| 60 | static PFNGLUNIFORM3FVPROC glUniform3fv_func = NULL; |
| 61 | static PFNGLUNIFORM3FVPROC glUniform4fv_func = NULL; |
| 62 | |
| 63 | |
| 64 | |
| 65 | static void |
| 66 | normalize(GLfloat *dst, const GLfloat *src) |
| 67 | { |
| 68 | GLfloat len = sqrtf(src[0] * src[0] + src[1] * src[1] + src[2] * src[2]); |
| 69 | dst[0] = src[0] / len; |
| 70 | dst[1] = src[1] / len; |
| 71 | dst[2] = src[2] / len; |
| 72 | } |
| 73 | |
| 74 | |
| 75 | static void |
| 76 | Redisplay(void) |
| 77 | { |
| 78 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| 79 | |
| 80 | if (pixelLight) { |
| 81 | GLfloat vec[3]; |
| 82 | glUseProgram_func(program); |
| 83 | normalize(vec, lightPos); |
| 84 | glUniform3fv_func(uLightPos, 1, vec); |
| 85 | glDisable(GL_LIGHTING); |
| 86 | } |
| 87 | else { |
| 88 | glUseProgram_func(0); |
| 89 | glLightfv(GL_LIGHT0, GL_POSITION, lightPos); |
| 90 | glEnable(GL_LIGHTING); |
| 91 | } |
| 92 | |
| 93 | glPushMatrix(); |
| 94 | glRotatef(xRot, 1.0f, 0.0f, 0.0f); |
| 95 | glRotatef(yRot, 0.0f, 1.0f, 0.0f); |
| 96 | glutSolidSphere(2.0, 10, 5); |
| 97 | glPopMatrix(); |
| 98 | |
| 99 | glutSwapBuffers(); |
| 100 | frames++; |
| 101 | |
| 102 | if (anim) { |
| 103 | GLint t = glutGet(GLUT_ELAPSED_TIME); |
| 104 | if (t - t0 >= 5000) { |
| 105 | GLfloat seconds =(GLfloat)(t - t0) / 1000.0f; |
| 106 | GLfloat fps = frames / seconds; |
| 107 | printf("%d frames in %6.3f seconds = %6.3f FPS\n", |
| 108 | frames, seconds, fps); |
| 109 | t0 = t; |
| 110 | frames = 0; |
| 111 | } |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | |
| 116 | static void |
| 117 | Idle(void) |
| 118 | { |
| 119 | lightPos[0] += delta; |
| 120 | if (lightPos[0] > 25.0f || lightPos[0] < -25.0f) |
| 121 | delta = -delta; |
| 122 | glutPostRedisplay(); |
| 123 | } |
| 124 | |
| 125 | |
| 126 | static void |
| 127 | Reshape(int width, int height) |
| 128 | { |
| 129 | glViewport(0, 0, width, height); |
| 130 | glMatrixMode(GL_PROJECTION); |
| 131 | glLoadIdentity(); |
| 132 | glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 25.0); |
| 133 | glMatrixMode(GL_MODELVIEW); |
| 134 | glLoadIdentity(); |
| 135 | glTranslatef(0.0f, 0.0f, -15.0f); |
| 136 | } |
| 137 | |
| 138 | |
| 139 | static void |
| 140 | CleanUp(void) |
| 141 | { |
| 142 | glDeleteShader_func(fragShader); |
| 143 | glDeleteShader_func(vertShader); |
| 144 | glDeleteProgram_func(program); |
| 145 | glutDestroyWindow(win); |
| 146 | } |
| 147 | |
| 148 | |
| 149 | static void |
| 150 | Key(unsigned char key, int x, int y) |
| 151 | { |
| 152 | (void) x; |
| 153 | (void) y; |
| 154 | |
| 155 | switch(key) { |
| 156 | case ' ': |
| 157 | case 'a': |
| 158 | anim = !anim; |
| 159 | if (anim) |
| 160 | glutIdleFunc(Idle); |
| 161 | else |
| 162 | glutIdleFunc(NULL); |
| 163 | break; |
| 164 | case 'x': |
| 165 | lightPos[0] -= 1.0f; |
| 166 | break; |
| 167 | case 'X': |
| 168 | lightPos[0] += 1.0f; |
| 169 | break; |
| 170 | case 'w': |
| 171 | wire = !wire; |
| 172 | if (wire) |
| 173 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); |
| 174 | else |
| 175 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); |
| 176 | break; |
| 177 | case 'p': |
| 178 | pixelLight = !pixelLight; |
| 179 | if (pixelLight) |
| 180 | printf("Per-pixel lighting\n"); |
| 181 | else |
| 182 | printf("Conventional lighting\n"); |
| 183 | break; |
| 184 | case 27: |
| 185 | CleanUp(); |
| 186 | exit(0); |
| 187 | break; |
| 188 | } |
| 189 | glutPostRedisplay(); |
| 190 | } |
| 191 | |
| 192 | |
| 193 | static void |
| 194 | SpecialKey(int key, int x, int y) |
| 195 | { |
| 196 | const GLfloat step = 3.0f; |
| 197 | |
| 198 | (void) x; |
| 199 | (void) y; |
| 200 | |
| 201 | switch(key) { |
| 202 | case GLUT_KEY_UP: |
| 203 | xRot -= step; |
| 204 | break; |
| 205 | case GLUT_KEY_DOWN: |
| 206 | xRot += step; |
| 207 | break; |
| 208 | case GLUT_KEY_LEFT: |
| 209 | yRot -= step; |
| 210 | break; |
| 211 | case GLUT_KEY_RIGHT: |
| 212 | yRot += step; |
| 213 | break; |
| 214 | } |
| 215 | glutPostRedisplay(); |
| 216 | } |
| 217 | |
| 218 | |
| 219 | static void |
| 220 | Init(void) |
| 221 | { |
| 222 | static const char *fragShaderText = |
| 223 | "uniform vec3 lightPos;\n" |
| 224 | "uniform vec4 diffuse;\n" |
| 225 | "uniform vec4 specular;\n" |
| 226 | "varying vec3 normal;\n" |
| 227 | "void main() {\n" |
| 228 | " // Compute dot product of light direction and normal vector\n" |
| 229 | " float dotProd = max(dot(lightPos, normalize(normal)), 0.0);\n" |
| 230 | " // Compute diffuse and specular contributions\n" |
| 231 | " gl_FragColor = diffuse * dotProd + specular * pow(dotProd, 20.0);\n" |
| 232 | "}\n"; |
| 233 | static const char *vertShaderText = |
| 234 | "varying vec3 normal;\n" |
| 235 | "void main() {\n" |
| 236 | " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" |
| 237 | " normal = gl_NormalMatrix * gl_Normal;\n" |
| 238 | "}\n"; |
| 239 | |
| 240 | |
| 241 | const char *version; |
| 242 | |
| 243 | version = (const char *) glGetString(GL_VERSION); |
| 244 | if (version[0] != '2' || version[1] != '.') { |
| 245 | printf("Warning: this program expects OpenGL 2.0\n"); |
| 246 | /*exit(1);*/ |
| 247 | } |
| 248 | |
| 249 | |
| 250 | glCreateShader_func = (PFNGLCREATESHADERPROC) glutGetProcAddress("glCreateShader"); |
| 251 | glDeleteShader_func = (PFNGLDELETESHADERPROC) glutGetProcAddress("glDeleteShader"); |
| 252 | glDeleteProgram_func = (PFNGLDELETEPROGRAMPROC) glutGetProcAddress("glDeleteProgram"); |
| 253 | glShaderSource_func = (PFNGLSHADERSOURCEPROC) glutGetProcAddress("glShaderSource"); |
| 254 | glGetShaderSource_func = (PFNGLGETSHADERSOURCEPROC) glutGetProcAddress("glGetShaderSource"); |
| 255 | glCompileShader_func = (PFNGLCOMPILESHADERPROC) glutGetProcAddress("glCompileShader"); |
| 256 | glCreateProgram_func = (PFNGLCREATEPROGRAMPROC) glutGetProcAddress("glCreateProgram"); |
| 257 | glAttachShader_func = (PFNGLATTACHSHADERPROC) glutGetProcAddress("glAttachShader"); |
| 258 | glLinkProgram_func = (PFNGLLINKPROGRAMPROC) glutGetProcAddress("glLinkProgram"); |
| 259 | glUseProgram_func = (PFNGLUSEPROGRAMPROC) glutGetProcAddress("glUseProgram"); |
| 260 | glGetUniformLocation_func = (PFNGLGETUNIFORMLOCATIONPROC) glutGetProcAddress("glGetUniformLocation"); |
| 261 | glIsProgram_func = (PFNGLISPROGRAMPROC) glutGetProcAddress("glIsProgram"); |
| 262 | glIsShader_func = (PFNGLISSHADERPROC) glutGetProcAddress("glIsShader"); |
| 263 | glUniform3fv_func = (PFNGLUNIFORM3FVPROC) glutGetProcAddress("glUniform3fv"); |
| 264 | glUniform4fv_func = (PFNGLUNIFORM3FVPROC) glutGetProcAddress("glUniform4fv"); |
| 265 | |
| 266 | fragShader = glCreateShader_func(GL_FRAGMENT_SHADER); |
| 267 | glShaderSource_func(fragShader, 1, &fragShaderText, NULL); |
| 268 | glCompileShader_func(fragShader); |
| 269 | |
| 270 | vertShader = glCreateShader_func(GL_VERTEX_SHADER); |
| 271 | glShaderSource_func(vertShader, 1, &vertShaderText, NULL); |
| 272 | glCompileShader_func(vertShader); |
| 273 | |
| 274 | program = glCreateProgram_func(); |
| 275 | glAttachShader_func(program, fragShader); |
| 276 | glAttachShader_func(program, vertShader); |
| 277 | glLinkProgram_func(program); |
| 278 | glUseProgram_func(program); |
| 279 | |
| 280 | uLightPos = glGetUniformLocation_func(program, "lightPos"); |
| 281 | uDiffuse = glGetUniformLocation_func(program, "diffuse"); |
| 282 | uSpecular = glGetUniformLocation_func(program, "specular"); |
| 283 | |
| 284 | glUniform4fv_func(uDiffuse, 1, diffuse); |
| 285 | glUniform4fv_func(uSpecular, 1, specular); |
| 286 | |
| 287 | glClearColor(0.3f, 0.3f, 0.3f, 0.0f); |
| 288 | glEnable(GL_DEPTH_TEST); |
| 289 | glEnable(GL_LIGHT0); |
| 290 | glEnable(GL_LIGHTING); |
| 291 | glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse); |
| 292 | glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular); |
| 293 | glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 20.0f); |
| 294 | |
| 295 | printf("GL_RENDERER = %s\n",(const char *) glGetString(GL_RENDERER)); |
| 296 | printf("Press p to toggle between per-pixel and per-vertex lighting\n"); |
| 297 | |
| 298 | /* test glGetShaderSource() */ |
| 299 | { |
| 300 | GLsizei len = strlen(fragShaderText) + 1; |
| 301 | GLsizei lenOut; |
| 302 | GLchar *src =(GLchar *) malloc(len * sizeof(GLchar)); |
| 303 | glGetShaderSource_func(fragShader, 0, NULL, src); |
| 304 | glGetShaderSource_func(fragShader, len, &lenOut, src); |
| 305 | assert(len == lenOut + 1); |
| 306 | assert(strcmp(src, fragShaderText) == 0); |
| 307 | free(src); |
| 308 | } |
| 309 | |
| 310 | assert(glIsProgram_func(program)); |
| 311 | assert(glIsShader_func(fragShader)); |
| 312 | assert(glIsShader_func(vertShader)); |
| 313 | } |
| 314 | |
| 315 | |
| 316 | int |
| 317 | main(int argc, char *argv[]) |
| 318 | { |
| 319 | glutInit(&argc, argv); |
| 320 | glutInitWindowPosition( 0, 0); |
| 321 | glutInitWindowSize(200, 200); |
| 322 | glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); |
| 323 | win = glutCreateWindow(argv[0]); |
| 324 | glutReshapeFunc(Reshape); |
| 325 | glutKeyboardFunc(Key); |
| 326 | glutSpecialFunc(SpecialKey); |
| 327 | glutDisplayFunc(Redisplay); |
| 328 | if (anim) |
| 329 | glutIdleFunc(Idle); |
| 330 | Init(); |
| 331 | glutMainLoop(); |
| 332 | return 0; |
| 333 | } |
| 334 | |