2 * Simple shader test harness.
7 * shtest --vs vertShaderFile --fs fragShaderFile
9 * In this case the given vertex/frag shaders are read and compiled.
10 * Random values are assigned to the uniforms.
15 * In this case a config file is read that specifies the file names
16 * of the shaders plus initial values for uniforms.
18 * Example config file:
22 * uniform GL_FLOAT pi 3.14159
23 * uniform GL_FLOAT_VEC4 v1 1.0 0.5 0.2 0.3
24 * texture 0 2D texture0.rgb
25 * texture 1 CUBE texture1.rgb
26 * texture 2 RECT texture2.rgb
37 #include "glut_wrap.h"
38 #include "shaderutil.h"
50 static char *FragShaderFile
= NULL
;
51 static char *VertShaderFile
= NULL
;
52 static char *ConfigFile
= NULL
;
54 /* program/shader objects */
55 static GLuint fragShader
;
56 static GLuint vertShader
;
57 static GLuint Program
;
60 #define MAX_UNIFORMS 100
61 static struct uniform_info Uniforms
[MAX_UNIFORMS
];
62 static GLuint NumUniforms
= 0;
65 #define MAX_ATTRIBS 100
66 static struct attrib_info Attribs
[MAX_ATTRIBS
];
67 static GLuint NumAttribs
= 0;
87 static GLboolean Anim
= GL_FALSE
;
88 static GLfloat TexRot
= 0.0;
89 static GLfloat xRot
= 0.0f
, yRot
= 0.0f
, zRot
= 0.0f
;
90 static shape Object
= SPHERE
;
94 RandomFloat(float min
, float max
)
96 int k
= rand() % 10000;
97 float x
= min
+ (max
- min
) * k
/ 10000.0;
102 /** Set new random values for uniforms */
104 RandomUniformValues(void)
107 for (i
= 0; i
< NumUniforms
; i
++) {
108 switch (Uniforms
[i
].type
) {
110 Uniforms
[i
].value
[0] = RandomFloat(0.0, 1.0);
115 case GL_SAMPLER_CUBE
:
116 case GL_SAMPLER_2D_RECT_ARB
:
117 /* don't change sampler values - random values are bad */
120 Uniforms
[i
].value
[0] = RandomFloat(-1.0, 2.0);
121 Uniforms
[i
].value
[1] = RandomFloat(-1.0, 2.0);
122 Uniforms
[i
].value
[2] = RandomFloat(-1.0, 2.0);
123 Uniforms
[i
].value
[3] = RandomFloat(-1.0, 2.0);
141 SquareVertex(GLfloat s
, GLfloat t
, GLfloat size
)
143 GLfloat x
= -size
+ s
* 2.0 * size
;
144 GLfloat y
= -size
+ t
* 2.0 * size
;
147 glMultiTexCoord2f(GL_TEXTURE0
, s
, t
);
148 glMultiTexCoord2f(GL_TEXTURE1
, s
, t
);
149 glMultiTexCoord2f(GL_TEXTURE2
, s
, t
);
150 glMultiTexCoord2f(GL_TEXTURE3
, s
, t
);
152 /* assign (s,t) to the generic attributes */
153 for (i
= 0; i
< NumAttribs
; i
++) {
154 if (Attribs
[i
].location
>= 0) {
155 glVertexAttrib2f(Attribs
[i
].location
, s
, t
);
164 * Draw a square, specifying normal and tangent vectors.
169 GLint tangentAttrib
= 1;
171 glVertexAttrib3f(tangentAttrib
, 1, 0, 0);
174 SquareVertex(0, 0, size
);
175 SquareVertex(1, 0, size
);
176 SquareVertex(1, 1, size
);
177 SquareVertex(0, 1, size
);
179 glTexCoord2f(0, 0); glVertex2f(-size
, -size
);
180 glTexCoord2f(1, 0); glVertex2f( size
, -size
);
181 glTexCoord2f(1, 1); glVertex2f( size
, size
);
182 glTexCoord2f(0, 1); glVertex2f(-size
, size
);
193 glRotatef(90, 0, 1, 0);
194 glTranslatef(0, 0, size
);
200 glRotatef(-90, 0, 1, 0);
201 glTranslatef(0, 0, size
);
207 glRotatef(90, 1, 0, 0);
208 glTranslatef(0, 0, size
);
214 glRotatef(-90, 1, 0, 0);
215 glTranslatef(0, 0, size
);
222 glTranslatef(0, 0, size
);
228 glRotatef(180, 0, 1, 0);
229 glTranslatef(0, 0, size
);
236 Sphere(GLfloat radius
, GLint slices
, GLint stacks
)
238 static GLUquadricObj
*q
= NULL
;
242 gluQuadricDrawStyle(q
, GLU_FILL
);
243 gluQuadricNormals(q
, GLU_SMOOTH
);
244 gluQuadricTexture(q
, GL_TRUE
);
247 gluSphere(q
, radius
, slices
, stacks
);
254 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
257 glRotatef(xRot
, 1.0f
, 0.0f
, 0.0f
);
258 glRotatef(yRot
, 0.0f
, 1.0f
, 0.0f
);
259 glRotatef(zRot
, 0.0f
, 0.0f
, 1.0f
);
261 glMatrixMode(GL_TEXTURE
);
263 glRotatef(TexRot
, 0.0f
, 1.0f
, 0.0f
);
264 glMatrixMode(GL_MODELVIEW
);
266 if (Object
== SPHERE
) {
269 else if (Object
== CUBE
) {
280 Reshape(int width
, int height
)
282 glViewport(0, 0, width
, height
);
283 glMatrixMode(GL_PROJECTION
);
285 glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 25.0);
286 glMatrixMode(GL_MODELVIEW
);
288 glTranslatef(0.0f
, 0.0f
, -15.0f
);
295 glDeleteShader(fragShader
);
296 glDeleteShader(vertShader
);
297 glDeleteProgram(Program
);
298 glutDestroyWindow(win
);
303 Key(unsigned char key
, int x
, int y
)
305 const GLfloat step
= 2.0;
324 Object
= (Object
+ 1) % NUM_SHAPES
;
327 RandomUniformValues();
328 SetUniformValues(Program
, Uniforms
);
329 PrintUniforms(Uniforms
);
341 SpecialKey(int key
, int x
, int y
)
343 const GLfloat step
= 2.0;
367 InitUniforms(const struct config_file
*conf
,
368 struct uniform_info uniforms
[])
372 for (i
= 0; i
< conf
->num_uniforms
; i
++) {
374 for (j
= 0; uniforms
[j
].name
; j
++) {
375 if (strcmp(uniforms
[j
].name
, conf
->uniforms
[i
].name
) == 0) {
376 uniforms
[j
].type
= conf
->uniforms
[i
].type
;
377 uniforms
[j
].value
[0] = conf
->uniforms
[i
].value
[0];
378 uniforms
[j
].value
[1] = conf
->uniforms
[i
].value
[1];
379 uniforms
[j
].value
[2] = conf
->uniforms
[i
].value
[2];
380 uniforms
[j
].value
[3] = conf
->uniforms
[i
].value
[3];
388 LoadTexture(GLint unit
, GLenum target
, const char *texFileName
)
390 GLint imgWidth
, imgHeight
;
392 GLubyte
*image
= NULL
;
394 GLenum filter
= GL_LINEAR
;
397 image
= LoadRGBImage(texFileName
, &imgWidth
, &imgHeight
, &imgFormat
);
399 printf("Couldn't read %s\n", texFileName
);
403 printf("Load Texture: unit %d, target 0x%x: %s %d x %d\n",
404 unit
, target
, texFileName
, imgWidth
, imgHeight
);
406 if (target
>= GL_TEXTURE_CUBE_MAP_POSITIVE_X
&&
407 target
<= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
) {
408 objTarget
= GL_TEXTURE_CUBE_MAP
;
414 glActiveTexture(GL_TEXTURE0
+ unit
);
415 glGenTextures(1, &tex
);
416 glBindTexture(objTarget
, tex
);
418 if (target
== GL_TEXTURE_3D
) {
419 #ifdef GLU_VERSION_1_3
421 gluBuild3DMipmaps(target
, 4, imgWidth
, imgHeight
, 1,
422 imgFormat
, GL_UNSIGNED_BYTE
, image
);
424 fprintf(stderr
, "Error: GLU 1.3 not available\n");
428 else if (target
== GL_TEXTURE_1D
) {
429 gluBuild1DMipmaps(target
, 4, imgWidth
,
430 imgFormat
, GL_UNSIGNED_BYTE
, image
);
433 gluBuild2DMipmaps(target
, 4, imgWidth
, imgHeight
,
434 imgFormat
, GL_UNSIGNED_BYTE
, image
);
439 glTexParameteri(objTarget
, GL_TEXTURE_WRAP_S
, GL_REPEAT
);
440 glTexParameteri(objTarget
, GL_TEXTURE_WRAP_T
, GL_REPEAT
);
441 glTexParameteri(objTarget
, GL_TEXTURE_MIN_FILTER
, filter
);
442 glTexParameteri(objTarget
, GL_TEXTURE_MAG_FILTER
, filter
);
447 TypeFromName(const char *n
)
449 static const struct {
453 { "GL_FLOAT", GL_FLOAT
},
454 { "GL_FLOAT_VEC2", GL_FLOAT_VEC2
},
455 { "GL_FLOAT_VEC3", GL_FLOAT_VEC3
},
456 { "GL_FLOAT_VEC4", GL_FLOAT_VEC4
},
457 { "GL_INT", GL_INT
},
458 { "GL_INT_VEC2", GL_INT_VEC2
},
459 { "GL_INT_VEC3", GL_INT_VEC3
},
460 { "GL_INT_VEC4", GL_INT_VEC4
},
461 { "GL_SAMPLER_1D", GL_SAMPLER_1D
},
462 { "GL_SAMPLER_2D", GL_SAMPLER_2D
},
463 { "GL_SAMPLER_3D", GL_SAMPLER_3D
},
464 { "GL_SAMPLER_CUBE", GL_SAMPLER_CUBE
},
465 { "GL_SAMPLER_2D_RECT", GL_SAMPLER_2D_RECT_ARB
},
470 for (i
= 0; types
[i
].name
; i
++) {
471 if (strcmp(types
[i
].name
, n
) == 0)
472 return types
[i
].type
;
481 * Read a config file.
484 ReadConfigFile(const char *filename
, struct config_file
*conf
)
489 f
= fopen(filename
, "r");
491 fprintf(stderr
, "Unable to open config file %s\n", filename
);
495 conf
->num_uniforms
= 0;
497 /* ugly but functional parser */
498 while (fgets(line
, sizeof(line
), f
) != NULL
) {
500 if (strncmp(line
, "vs ", 3) == 0) {
501 VertShaderFile
= strdup(line
+ 3);
502 VertShaderFile
[strlen(VertShaderFile
) - 1] = 0;
504 else if (strncmp(line
, "fs ", 3) == 0) {
505 FragShaderFile
= strdup(line
+ 3);
506 FragShaderFile
[strlen(FragShaderFile
) - 1] = 0;
508 else if (strncmp(line
, "texture ", 8) == 0) {
509 char target
[100], texFileName
[100];
511 k
= sscanf(line
+ 8, "%d %s %s", &unit
, target
, texFileName
);
512 assert(k
== 3 || k
== 8);
513 if (strcmp(target
, "CUBE") == 0) {
514 char texFileNames
[6][100];
515 k
= sscanf(line
+ 8, "%d %s %s %s %s %s %s %s",
523 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_POSITIVE_X
, texFileNames
[0]);
524 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_NEGATIVE_X
, texFileNames
[1]);
525 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_POSITIVE_Y
, texFileNames
[2]);
526 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
, texFileNames
[3]);
527 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_POSITIVE_Z
, texFileNames
[4]);
528 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
, texFileNames
[5]);
530 else if (!strcmp(target
, "2D")) {
531 LoadTexture(unit
, GL_TEXTURE_2D
, texFileName
);
533 else if (!strcmp(target
, "3D")) {
534 LoadTexture(unit
, GL_TEXTURE_3D
, texFileName
);
536 else if (!strcmp(target
, "RECT")) {
537 LoadTexture(unit
, GL_TEXTURE_RECTANGLE_ARB
, texFileName
);
540 printf("Bad texture target: %s\n", target
);
544 else if (strncmp(line
, "uniform ", 8) == 0) {
545 char name
[1000], typeName
[100];
547 float v1
= 0.0F
, v2
= 0.0F
, v3
= 0.0F
, v4
= 0.0F
;
550 k
= sscanf(line
+ 8, "%s %s %f %f %f %f", typeName
, name
,
553 type
= TypeFromName(typeName
);
555 if (strlen(name
) + 1 > sizeof(conf
->uniforms
[conf
->num_uniforms
].name
)) {
556 fprintf(stderr
, "string overflow\n");
559 strcpy(conf
->uniforms
[conf
->num_uniforms
].name
, name
);
560 conf
->uniforms
[conf
->num_uniforms
].value
[0] = v1
;
561 conf
->uniforms
[conf
->num_uniforms
].value
[1] = v2
;
562 conf
->uniforms
[conf
->num_uniforms
].value
[2] = v3
;
563 conf
->uniforms
[conf
->num_uniforms
].value
[3] = v4
;
564 conf
->uniforms
[conf
->num_uniforms
].type
= type
;
565 conf
->num_uniforms
++;
568 if (strlen(line
) > 1) {
569 fprintf(stderr
, "syntax error in: %s\n", line
);
583 GLdouble vertTime
= 0.0, fragTime
= 0.0, linkTime
= 0.0;
584 struct config_file config
;
586 memset(&config
, 0, sizeof(config
));
589 ReadConfigFile(ConfigFile
, &config
);
591 if (!ShadersSupported())
594 if (VertShaderFile
) {
595 printf("Read vert shader %s\n", VertShaderFile
);
596 vertShader
= CompileShaderFile(GL_VERTEX_SHADER
, VertShaderFile
);
597 vertTime
= GetShaderCompileTime();
600 if (FragShaderFile
) {
601 printf("Read frag shader %s\n", FragShaderFile
);
602 fragShader
= CompileShaderFile(GL_FRAGMENT_SHADER
, FragShaderFile
);
603 fragTime
= GetShaderCompileTime();
606 Program
= LinkShaders(vertShader
, fragShader
);
607 linkTime
= GetShaderLinkTime();
609 printf("Time to compile vertex shader: %fs\n", vertTime
);
610 printf("Time to compile fragment shader: %fs\n", fragTime
);
611 printf("Time to link shaders: %fs\n", linkTime
);
613 assert(ValidateShaderProgram(Program
));
615 glUseProgram(Program
);
617 NumUniforms
= GetUniforms(Program
, Uniforms
);
618 if (config
.num_uniforms
) {
619 InitUniforms(&config
, Uniforms
);
622 RandomUniformValues();
624 SetUniformValues(Program
, Uniforms
);
625 PrintUniforms(Uniforms
);
627 NumAttribs
= GetAttribs(Program
, Attribs
);
628 PrintAttribs(Attribs
);
630 /* assert(glGetError() == 0); */
632 glClearColor(0.4f
, 0.4f
, 0.8f
, 0.0f
);
634 glEnable(GL_DEPTH_TEST
);
643 printf("Keyboard:\n");
644 printf(" a Animation toggle\n");
645 printf(" r Randomize uniform values\n");
646 printf(" o Change object\n");
647 printf(" arrows Rotate object\n");
648 printf(" ESC Exit\n");
656 printf(" shtest config.shtest\n");
657 printf(" Run w/ given config file.\n");
658 printf(" shtest --vs vertShader --fs fragShader\n");
659 printf(" Load/compile given shaders.\n");
664 ParseOptions(int argc
, char *argv
[])
673 for (i
= 1; i
< argc
; i
++) {
674 if (strcmp(argv
[i
], "--fs") == 0) {
675 FragShaderFile
= argv
[i
+1];
678 else if (strcmp(argv
[i
], "--vs") == 0) {
679 VertShaderFile
= argv
[i
+1];
683 /* assume the arg is a config file */
684 ConfigFile
= argv
[i
];
692 main(int argc
, char *argv
[])
694 glutInitWindowSize(400, 400);
695 glutInit(&argc
, argv
);
696 glutInitDisplayMode(GLUT_RGB
| GLUT_DOUBLE
| GLUT_DEPTH
);
697 win
= glutCreateWindow(argv
[0]);
699 glutReshapeFunc(Reshape
);
700 glutKeyboardFunc(Key
);
701 glutSpecialFunc(SpecialKey
);
702 glutDisplayFunc(Redisplay
);
703 ParseOptions(argc
, argv
);