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:
23 * uniform 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
39 #include "shaderutil.h"
51 static char *FragShaderFile
= NULL
;
52 static char *VertShaderFile
= NULL
;
53 static char *ConfigFile
= NULL
;
55 /* program/shader objects */
56 static GLuint fragShader
;
57 static GLuint vertShader
;
58 static GLuint Program
;
61 #define MAX_UNIFORMS 100
62 static struct uniform_info Uniforms
[MAX_UNIFORMS
];
63 static GLuint NumUniforms
= 0;
66 #define MAX_ATTRIBS 100
67 static struct attrib_info Attribs
[MAX_ATTRIBS
];
68 static GLuint NumAttribs
= 0;
88 static GLboolean Anim
= GL_FALSE
;
89 static GLfloat TexRot
= 0.0;
90 static GLfloat xRot
= 0.0f
, yRot
= 0.0f
, zRot
= 0.0f
;
91 static shape Object
= SPHERE
;
95 RandomFloat(float min
, float max
)
97 int k
= rand() % 10000;
98 float x
= min
+ (max
- min
) * k
/ 10000.0;
103 /** Set new random values for uniforms */
105 RandomUniformValues(void)
108 for (i
= 0; i
< NumUniforms
; i
++) {
109 switch (Uniforms
[i
].type
) {
111 Uniforms
[i
].value
[0] = RandomFloat(0.0, 1.0);
116 case GL_SAMPLER_CUBE
:
117 case GL_SAMPLER_2D_RECT_ARB
:
118 /* don't change sampler values - random values are bad */
121 Uniforms
[i
].value
[0] = RandomFloat(-1.0, 2.0);
122 Uniforms
[i
].value
[1] = RandomFloat(-1.0, 2.0);
123 Uniforms
[i
].value
[2] = RandomFloat(-1.0, 2.0);
124 Uniforms
[i
].value
[3] = RandomFloat(-1.0, 2.0);
142 SquareVertex(GLfloat s
, GLfloat t
, GLfloat size
)
144 GLfloat x
= -size
+ s
* 2.0 * size
;
145 GLfloat y
= -size
+ t
* 2.0 * size
;
148 glMultiTexCoord2f(GL_TEXTURE0
, s
, t
);
149 glMultiTexCoord2f(GL_TEXTURE1
, s
, t
);
150 glMultiTexCoord2f(GL_TEXTURE2
, s
, t
);
151 glMultiTexCoord2f(GL_TEXTURE3
, s
, t
);
153 /* assign (s,t) to the generic attributes */
154 for (i
= 0; i
< NumAttribs
; i
++) {
155 if (Attribs
[i
].location
>= 0) {
156 glVertexAttrib2f(Attribs
[i
].location
, s
, t
);
165 * Draw a square, specifying normal and tangent vectors.
170 GLint tangentAttrib
= 1;
172 glVertexAttrib3f(tangentAttrib
, 1, 0, 0);
175 SquareVertex(0, 0, size
);
176 SquareVertex(1, 0, size
);
177 SquareVertex(1, 1, size
);
178 SquareVertex(0, 1, size
);
180 glTexCoord2f(0, 0); glVertex2f(-size
, -size
);
181 glTexCoord2f(1, 0); glVertex2f( size
, -size
);
182 glTexCoord2f(1, 1); glVertex2f( size
, size
);
183 glTexCoord2f(0, 1); glVertex2f(-size
, size
);
194 glRotatef(90, 0, 1, 0);
195 glTranslatef(0, 0, size
);
201 glRotatef(-90, 0, 1, 0);
202 glTranslatef(0, 0, size
);
208 glRotatef(90, 1, 0, 0);
209 glTranslatef(0, 0, size
);
215 glRotatef(-90, 1, 0, 0);
216 glTranslatef(0, 0, size
);
223 glTranslatef(0, 0, size
);
229 glRotatef(180, 0, 1, 0);
230 glTranslatef(0, 0, size
);
237 Sphere(GLfloat radius
, GLint slices
, GLint stacks
)
239 static GLUquadricObj
*q
= NULL
;
243 gluQuadricDrawStyle(q
, GLU_FILL
);
244 gluQuadricNormals(q
, GLU_SMOOTH
);
245 gluQuadricTexture(q
, GL_TRUE
);
248 gluSphere(q
, radius
, slices
, stacks
);
255 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
258 glRotatef(xRot
, 1.0f
, 0.0f
, 0.0f
);
259 glRotatef(yRot
, 0.0f
, 1.0f
, 0.0f
);
260 glRotatef(zRot
, 0.0f
, 0.0f
, 1.0f
);
262 glMatrixMode(GL_TEXTURE
);
264 glRotatef(TexRot
, 0.0f
, 1.0f
, 0.0f
);
265 glMatrixMode(GL_MODELVIEW
);
267 if (Object
== SPHERE
) {
270 else if (Object
== CUBE
) {
281 Reshape(int width
, int height
)
283 glViewport(0, 0, width
, height
);
284 glMatrixMode(GL_PROJECTION
);
286 glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 25.0);
287 glMatrixMode(GL_MODELVIEW
);
289 glTranslatef(0.0f
, 0.0f
, -15.0f
);
296 glDeleteShader(fragShader
);
297 glDeleteShader(vertShader
);
298 glDeleteProgram(Program
);
299 glutDestroyWindow(win
);
304 Key(unsigned char key
, int x
, int y
)
306 const GLfloat step
= 2.0;
325 Object
= (Object
+ 1) % NUM_SHAPES
;
328 RandomUniformValues();
329 SetUniformValues(Program
, Uniforms
);
330 PrintUniforms(Uniforms
);
342 SpecialKey(int key
, int x
, int y
)
344 const GLfloat step
= 2.0;
368 InitUniforms(const struct config_file
*conf
,
369 struct uniform_info uniforms
[])
373 for (i
= 0; i
< conf
->num_uniforms
; i
++) {
375 for (j
= 0; uniforms
[j
].name
; j
++) {
376 if (strcmp(uniforms
[j
].name
, conf
->uniforms
[i
].name
) == 0) {
377 uniforms
[j
].type
= conf
->uniforms
[i
].type
;
378 uniforms
[j
].value
[0] = conf
->uniforms
[i
].value
[0];
379 uniforms
[j
].value
[1] = conf
->uniforms
[i
].value
[1];
380 uniforms
[j
].value
[2] = conf
->uniforms
[i
].value
[2];
381 uniforms
[j
].value
[3] = conf
->uniforms
[i
].value
[3];
389 LoadTexture(GLint unit
, GLenum target
, const char *texFileName
)
391 GLint imgWidth
, imgHeight
;
393 GLubyte
*image
= NULL
;
395 GLenum filter
= GL_LINEAR
;
398 image
= LoadRGBImage(texFileName
, &imgWidth
, &imgHeight
, &imgFormat
);
400 printf("Couldn't read %s\n", texFileName
);
404 printf("Load Texture: unit %d, target 0x%x: %s %d x %d\n",
405 unit
, target
, texFileName
, imgWidth
, imgHeight
);
407 if (target
>= GL_TEXTURE_CUBE_MAP_POSITIVE_X
&&
408 target
<= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
) {
409 objTarget
= GL_TEXTURE_CUBE_MAP
;
415 glActiveTexture(GL_TEXTURE0
+ unit
);
416 glGenTextures(1, &tex
);
417 glBindTexture(objTarget
, tex
);
419 if (target
== GL_TEXTURE_3D
) {
421 gluBuild3DMipmaps(target
, 4, imgWidth
, imgHeight
, 1,
422 imgFormat
, GL_UNSIGNED_BYTE
, image
);
424 else if (target
== GL_TEXTURE_1D
) {
425 gluBuild1DMipmaps(target
, 4, imgWidth
,
426 imgFormat
, GL_UNSIGNED_BYTE
, image
);
429 gluBuild2DMipmaps(target
, 4, imgWidth
, imgHeight
,
430 imgFormat
, GL_UNSIGNED_BYTE
, image
);
435 glTexParameteri(objTarget
, GL_TEXTURE_WRAP_S
, GL_REPEAT
);
436 glTexParameteri(objTarget
, GL_TEXTURE_WRAP_T
, GL_REPEAT
);
437 glTexParameteri(objTarget
, GL_TEXTURE_MIN_FILTER
, filter
);
438 glTexParameteri(objTarget
, GL_TEXTURE_MAG_FILTER
, filter
);
443 TypeFromName(const char *n
)
445 static const struct {
449 { "GL_FLOAT", GL_FLOAT
},
450 { "GL_FLOAT_VEC2", GL_FLOAT_VEC2
},
451 { "GL_FLOAT_VEC3", GL_FLOAT_VEC3
},
452 { "GL_FLOAT_VEC4", GL_FLOAT_VEC4
},
453 { "GL_INT", GL_INT
},
454 { "GL_INT_VEC2", GL_INT_VEC2
},
455 { "GL_INT_VEC3", GL_INT_VEC3
},
456 { "GL_INT_VEC4", GL_INT_VEC4
},
457 { "GL_SAMPLER_1D", GL_SAMPLER_1D
},
458 { "GL_SAMPLER_2D", GL_SAMPLER_2D
},
459 { "GL_SAMPLER_3D", GL_SAMPLER_3D
},
460 { "GL_SAMPLER_CUBE", GL_SAMPLER_CUBE
},
461 { "GL_SAMPLER_2D_RECT", GL_SAMPLER_2D_RECT_ARB
},
466 for (i
= 0; types
[i
].name
; i
++) {
467 if (strcmp(types
[i
].name
, n
) == 0)
468 return types
[i
].type
;
477 * Read a config file.
480 ReadConfigFile(const char *filename
, struct config_file
*conf
)
485 f
= fopen(filename
, "r");
487 fprintf(stderr
, "Unable to open config file %s\n", filename
);
491 conf
->num_uniforms
= 0;
493 /* ugly but functional parser */
494 while (fgets(line
, sizeof(line
), f
) != NULL
) {
496 if (strncmp(line
, "vs ", 3) == 0) {
497 VertShaderFile
= strdup(line
+ 3);
498 VertShaderFile
[strlen(VertShaderFile
) - 1] = 0;
500 else if (strncmp(line
, "fs ", 3) == 0) {
501 FragShaderFile
= strdup(line
+ 3);
502 FragShaderFile
[strlen(FragShaderFile
) - 1] = 0;
504 else if (strncmp(line
, "texture ", 8) == 0) {
505 char target
[100], texFileName
[100];
507 k
= sscanf(line
+ 8, "%d %s %s", &unit
, target
, texFileName
);
508 assert(k
== 3 || k
== 8);
509 if (strcmp(target
, "CUBE") == 0) {
510 char texFileNames
[6][100];
511 k
= sscanf(line
+ 8, "%d %s %s %s %s %s %s %s",
519 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_POSITIVE_X
, texFileNames
[0]);
520 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_NEGATIVE_X
, texFileNames
[1]);
521 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_POSITIVE_Y
, texFileNames
[2]);
522 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
, texFileNames
[3]);
523 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_POSITIVE_Z
, texFileNames
[4]);
524 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
, texFileNames
[5]);
526 else if (!strcmp(target
, "2D")) {
527 LoadTexture(unit
, GL_TEXTURE_2D
, texFileName
);
529 else if (!strcmp(target
, "3D")) {
530 LoadTexture(unit
, GL_TEXTURE_3D
, texFileName
);
532 else if (!strcmp(target
, "RECT")) {
533 LoadTexture(unit
, GL_TEXTURE_RECTANGLE_ARB
, texFileName
);
536 printf("Bad texture target: %s\n", target
);
540 else if (strncmp(line
, "uniform ", 8) == 0) {
541 char name
[1000], typeName
[100];
543 float v1
= 0.0F
, v2
= 0.0F
, v3
= 0.0F
, v4
= 0.0F
;
546 k
= sscanf(line
+ 8, "%s %s %f %f %f %f", name
, typeName
,
549 type
= TypeFromName(typeName
);
551 if (strlen(name
) + 1 > sizeof(conf
->uniforms
[conf
->num_uniforms
].name
)) {
552 fprintf(stderr
, "string overflow\n");
555 strcpy(conf
->uniforms
[conf
->num_uniforms
].name
, name
);
556 conf
->uniforms
[conf
->num_uniforms
].value
[0] = v1
;
557 conf
->uniforms
[conf
->num_uniforms
].value
[1] = v2
;
558 conf
->uniforms
[conf
->num_uniforms
].value
[2] = v3
;
559 conf
->uniforms
[conf
->num_uniforms
].value
[3] = v4
;
560 conf
->uniforms
[conf
->num_uniforms
].type
= type
;
561 conf
->num_uniforms
++;
564 if (strlen(line
) > 1) {
565 fprintf(stderr
, "syntax error in: %s\n", line
);
579 GLdouble vertTime
, fragTime
, linkTime
;
580 struct config_file config
;
582 memset(&config
, 0, sizeof(config
));
585 ReadConfigFile(ConfigFile
, &config
);
587 if (!VertShaderFile
) {
588 fprintf(stderr
, "Error: no vertex shader\n");
592 if (!FragShaderFile
) {
593 fprintf(stderr
, "Error: no fragment shader\n");
597 if (!ShadersSupported())
600 vertShader
= CompileShaderFile(GL_VERTEX_SHADER
, VertShaderFile
);
601 vertTime
= GetShaderCompileTime();
602 fragShader
= CompileShaderFile(GL_FRAGMENT_SHADER
, FragShaderFile
);
603 fragTime
= GetShaderCompileTime();
605 Program
= LinkShaders(vertShader
, fragShader
);
606 linkTime
= GetShaderLinkTime();
608 printf("Read vert shader %s\n", VertShaderFile
);
609 printf("Read frag shader %s\n", FragShaderFile
);
611 printf("Time to compile vertex shader: %fs\n", vertTime
);
612 printf("Time to compile fragment shader: %fs\n", fragTime
);
613 printf("Time to link shaders: %fs\n", linkTime
);
615 assert(ValidateShaderProgram(Program
));
617 glUseProgram(Program
);
619 NumUniforms
= GetUniforms(Program
, Uniforms
);
620 if (config
.num_uniforms
) {
621 InitUniforms(&config
, Uniforms
);
624 RandomUniformValues();
626 SetUniformValues(Program
, Uniforms
);
627 PrintUniforms(Uniforms
);
629 NumAttribs
= GetAttribs(Program
, Attribs
);
630 PrintAttribs(Attribs
);
632 /* assert(glGetError() == 0); */
634 glClearColor(0.4f
, 0.4f
, 0.8f
, 0.0f
);
636 glEnable(GL_DEPTH_TEST
);
645 printf("Keyboard:\n");
646 printf(" a Animation toggle\n");
647 printf(" r Randomize uniform values\n");
648 printf(" o Change object\n");
649 printf(" arrows Rotate object\n");
650 printf(" ESC Exit\n");
658 printf(" shtest config.shtest\n");
659 printf(" Run w/ given config file.\n");
660 printf(" shtest --vs vertShader --fs fragShader\n");
661 printf(" Load/compile given shaders.\n");
666 ParseOptions(int argc
, char *argv
[])
675 for (i
= 1; i
< argc
; i
++) {
676 if (strcmp(argv
[i
], "--fs") == 0) {
677 FragShaderFile
= argv
[i
+1];
680 else if (strcmp(argv
[i
], "--vs") == 0) {
681 VertShaderFile
= argv
[i
+1];
685 /* assume the arg is a config file */
686 ConfigFile
= argv
[i
];
694 main(int argc
, char *argv
[])
696 glutInitWindowSize(400, 400);
697 glutInit(&argc
, argv
);
698 glutInitDisplayMode(GLUT_RGB
| GLUT_DOUBLE
| GLUT_DEPTH
);
699 win
= glutCreateWindow(argv
[0]);
701 glutReshapeFunc(Reshape
);
702 glutKeyboardFunc(Key
);
703 glutSpecialFunc(SpecialKey
);
704 glutDisplayFunc(Redisplay
);
705 ParseOptions(argc
, argv
);