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
40 #include "shaderutil.h"
52 static char *FragShaderFile
= NULL
;
53 static char *VertShaderFile
= NULL
;
54 static char *ConfigFile
= NULL
;
56 /* program/shader objects */
57 static GLuint fragShader
;
58 static GLuint vertShader
;
59 static GLuint Program
;
62 #define MAX_UNIFORMS 100
63 static struct uniform_info Uniforms
[MAX_UNIFORMS
];
64 static GLuint NumUniforms
= 0;
67 #define MAX_ATTRIBS 100
68 static struct attrib_info Attribs
[MAX_ATTRIBS
];
69 static GLuint NumAttribs
= 0;
89 static GLboolean Anim
= GL_FALSE
;
90 static GLfloat TexRot
= 0.0;
91 static GLfloat xRot
= 0.0f
, yRot
= 0.0f
, zRot
= 0.0f
;
92 static shape Object
= SPHERE
;
96 RandomFloat(float min
, float max
)
98 int k
= rand() % 10000;
99 float x
= min
+ (max
- min
) * k
/ 10000.0;
104 /** Set new random values for uniforms */
106 RandomUniformValues(void)
109 for (i
= 0; i
< NumUniforms
; i
++) {
110 switch (Uniforms
[i
].type
) {
112 Uniforms
[i
].value
[0] = RandomFloat(0.0, 1.0);
117 case GL_SAMPLER_CUBE
:
118 case GL_SAMPLER_2D_RECT_ARB
:
119 /* don't change sampler values - random values are bad */
122 Uniforms
[i
].value
[0] = RandomFloat(-1.0, 2.0);
123 Uniforms
[i
].value
[1] = RandomFloat(-1.0, 2.0);
124 Uniforms
[i
].value
[2] = RandomFloat(-1.0, 2.0);
125 Uniforms
[i
].value
[3] = RandomFloat(-1.0, 2.0);
143 SquareVertex(GLfloat s
, GLfloat t
, GLfloat size
)
145 GLfloat x
= -size
+ s
* 2.0 * size
;
146 GLfloat y
= -size
+ t
* 2.0 * size
;
149 glMultiTexCoord2f(GL_TEXTURE0
, s
, t
);
150 glMultiTexCoord2f(GL_TEXTURE1
, s
, t
);
151 glMultiTexCoord2f(GL_TEXTURE2
, s
, t
);
152 glMultiTexCoord2f(GL_TEXTURE3
, s
, t
);
154 /* assign (s,t) to the generic attributes */
155 for (i
= 0; i
< NumAttribs
; i
++) {
156 if (Attribs
[i
].location
>= 0) {
157 glVertexAttrib2f(Attribs
[i
].location
, s
, t
);
166 * Draw a square, specifying normal and tangent vectors.
171 GLint tangentAttrib
= 1;
173 glVertexAttrib3f(tangentAttrib
, 1, 0, 0);
176 SquareVertex(0, 0, size
);
177 SquareVertex(1, 0, size
);
178 SquareVertex(1, 1, size
);
179 SquareVertex(0, 1, size
);
181 glTexCoord2f(0, 0); glVertex2f(-size
, -size
);
182 glTexCoord2f(1, 0); glVertex2f( size
, -size
);
183 glTexCoord2f(1, 1); glVertex2f( size
, size
);
184 glTexCoord2f(0, 1); glVertex2f(-size
, size
);
195 glRotatef(90, 0, 1, 0);
196 glTranslatef(0, 0, size
);
202 glRotatef(-90, 0, 1, 0);
203 glTranslatef(0, 0, size
);
209 glRotatef(90, 1, 0, 0);
210 glTranslatef(0, 0, size
);
216 glRotatef(-90, 1, 0, 0);
217 glTranslatef(0, 0, size
);
224 glTranslatef(0, 0, size
);
230 glRotatef(180, 0, 1, 0);
231 glTranslatef(0, 0, size
);
238 Sphere(GLfloat radius
, GLint slices
, GLint stacks
)
240 static GLUquadricObj
*q
= NULL
;
244 gluQuadricDrawStyle(q
, GLU_FILL
);
245 gluQuadricNormals(q
, GLU_SMOOTH
);
246 gluQuadricTexture(q
, GL_TRUE
);
249 gluSphere(q
, radius
, slices
, stacks
);
256 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
259 glRotatef(xRot
, 1.0f
, 0.0f
, 0.0f
);
260 glRotatef(yRot
, 0.0f
, 1.0f
, 0.0f
);
261 glRotatef(zRot
, 0.0f
, 0.0f
, 1.0f
);
263 glMatrixMode(GL_TEXTURE
);
265 glRotatef(TexRot
, 0.0f
, 1.0f
, 0.0f
);
266 glMatrixMode(GL_MODELVIEW
);
268 if (Object
== SPHERE
) {
271 else if (Object
== CUBE
) {
282 Reshape(int width
, int height
)
284 glViewport(0, 0, width
, height
);
285 glMatrixMode(GL_PROJECTION
);
287 glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 25.0);
288 glMatrixMode(GL_MODELVIEW
);
290 glTranslatef(0.0f
, 0.0f
, -15.0f
);
297 glDeleteShader(fragShader
);
298 glDeleteShader(vertShader
);
299 glDeleteProgram(Program
);
300 glutDestroyWindow(win
);
305 Key(unsigned char key
, int x
, int y
)
307 const GLfloat step
= 2.0;
326 Object
= (Object
+ 1) % NUM_SHAPES
;
329 RandomUniformValues();
330 SetUniformValues(Program
, Uniforms
);
331 PrintUniforms(Uniforms
);
343 SpecialKey(int key
, int x
, int y
)
345 const GLfloat step
= 2.0;
369 InitUniforms(const struct config_file
*conf
,
370 struct uniform_info uniforms
[])
374 for (i
= 0; i
< conf
->num_uniforms
; i
++) {
376 for (j
= 0; uniforms
[j
].name
; j
++) {
377 if (strcmp(uniforms
[j
].name
, conf
->uniforms
[i
].name
) == 0) {
378 uniforms
[j
].type
= conf
->uniforms
[i
].type
;
379 uniforms
[j
].value
[0] = conf
->uniforms
[i
].value
[0];
380 uniforms
[j
].value
[1] = conf
->uniforms
[i
].value
[1];
381 uniforms
[j
].value
[2] = conf
->uniforms
[i
].value
[2];
382 uniforms
[j
].value
[3] = conf
->uniforms
[i
].value
[3];
390 LoadTexture(GLint unit
, GLenum target
, const char *texFileName
)
392 GLint imgWidth
, imgHeight
;
394 GLubyte
*image
= NULL
;
396 GLenum filter
= GL_LINEAR
;
399 image
= LoadRGBImage(texFileName
, &imgWidth
, &imgHeight
, &imgFormat
);
401 printf("Couldn't read %s\n", texFileName
);
405 printf("Load Texture: unit %d, target 0x%x: %s %d x %d\n",
406 unit
, target
, texFileName
, imgWidth
, imgHeight
);
408 if (target
>= GL_TEXTURE_CUBE_MAP_POSITIVE_X
&&
409 target
<= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
) {
410 objTarget
= GL_TEXTURE_CUBE_MAP
;
416 glActiveTexture(GL_TEXTURE0
+ unit
);
417 glGenTextures(1, &tex
);
418 glBindTexture(objTarget
, tex
);
420 if (target
== GL_TEXTURE_3D
) {
422 gluBuild3DMipmaps(target
, 4, imgWidth
, imgHeight
, 1,
423 imgFormat
, GL_UNSIGNED_BYTE
, image
);
425 else if (target
== GL_TEXTURE_1D
) {
426 gluBuild1DMipmaps(target
, 4, imgWidth
,
427 imgFormat
, GL_UNSIGNED_BYTE
, image
);
430 gluBuild2DMipmaps(target
, 4, imgWidth
, imgHeight
,
431 imgFormat
, GL_UNSIGNED_BYTE
, image
);
436 glTexParameteri(objTarget
, GL_TEXTURE_WRAP_S
, GL_REPEAT
);
437 glTexParameteri(objTarget
, GL_TEXTURE_WRAP_T
, GL_REPEAT
);
438 glTexParameteri(objTarget
, GL_TEXTURE_MIN_FILTER
, filter
);
439 glTexParameteri(objTarget
, GL_TEXTURE_MAG_FILTER
, filter
);
444 TypeFromName(const char *n
)
446 static const struct {
450 { "GL_FLOAT", GL_FLOAT
},
451 { "GL_FLOAT_VEC2", GL_FLOAT_VEC2
},
452 { "GL_FLOAT_VEC3", GL_FLOAT_VEC3
},
453 { "GL_FLOAT_VEC4", GL_FLOAT_VEC4
},
454 { "GL_INT", GL_INT
},
455 { "GL_INT_VEC2", GL_INT_VEC2
},
456 { "GL_INT_VEC3", GL_INT_VEC3
},
457 { "GL_INT_VEC4", GL_INT_VEC4
},
458 { "GL_SAMPLER_1D", GL_SAMPLER_1D
},
459 { "GL_SAMPLER_2D", GL_SAMPLER_2D
},
460 { "GL_SAMPLER_3D", GL_SAMPLER_3D
},
461 { "GL_SAMPLER_CUBE", GL_SAMPLER_CUBE
},
462 { "GL_SAMPLER_2D_RECT", GL_SAMPLER_2D_RECT_ARB
},
467 for (i
= 0; types
[i
].name
; i
++) {
468 if (strcmp(types
[i
].name
, n
) == 0)
469 return types
[i
].type
;
478 * Read a config file.
481 ReadConfigFile(const char *filename
, struct config_file
*conf
)
486 f
= fopen(filename
, "r");
488 fprintf(stderr
, "Unable to open config file %s\n", filename
);
492 conf
->num_uniforms
= 0;
494 /* ugly but functional parser */
496 fgets(line
, sizeof(line
), f
);
497 if (!feof(f
) && line
[0]) {
498 if (strncmp(line
, "vs ", 3) == 0) {
499 VertShaderFile
= strdup(line
+ 3);
500 VertShaderFile
[strlen(VertShaderFile
) - 1] = 0;
502 else if (strncmp(line
, "fs ", 3) == 0) {
503 FragShaderFile
= strdup(line
+ 3);
504 FragShaderFile
[strlen(FragShaderFile
) - 1] = 0;
506 else if (strncmp(line
, "texture ", 8) == 0) {
507 char target
[100], texFileName
[100];
509 k
= sscanf(line
+ 8, "%d %s %s", &unit
, target
, texFileName
);
510 assert(k
== 3 || k
== 8);
511 if (strcmp(target
, "CUBE") == 0) {
512 char texFileNames
[6][100];
513 k
= sscanf(line
+ 8, "%d %s %s %s %s %s %s %s",
521 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_POSITIVE_X
, texFileNames
[0]);
522 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_NEGATIVE_X
, texFileNames
[1]);
523 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_POSITIVE_Y
, texFileNames
[2]);
524 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
, texFileNames
[3]);
525 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_POSITIVE_Z
, texFileNames
[4]);
526 LoadTexture(unit
, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
, texFileNames
[5]);
528 else if (!strcmp(target
, "2D")) {
529 LoadTexture(unit
, GL_TEXTURE_2D
, texFileName
);
531 else if (!strcmp(target
, "3D")) {
532 LoadTexture(unit
, GL_TEXTURE_3D
, texFileName
);
534 else if (!strcmp(target
, "RECT")) {
535 LoadTexture(unit
, GL_TEXTURE_RECTANGLE_ARB
, texFileName
);
538 printf("Bad texture target: %s\n", target
);
542 else if (strncmp(line
, "uniform ", 8) == 0) {
543 char name
[1000], typeName
[100];
545 float v1
= 0.0F
, v2
= 0.0F
, v3
= 0.0F
, v4
= 0.0F
;
548 k
= sscanf(line
+ 8, "%s %s %f %f %f %f", name
, typeName
,
551 type
= TypeFromName(typeName
);
553 strcpy(conf
->uniforms
[conf
->num_uniforms
].name
, name
);
554 conf
->uniforms
[conf
->num_uniforms
].value
[0] = v1
;
555 conf
->uniforms
[conf
->num_uniforms
].value
[1] = v2
;
556 conf
->uniforms
[conf
->num_uniforms
].value
[2] = v3
;
557 conf
->uniforms
[conf
->num_uniforms
].value
[3] = v4
;
558 conf
->uniforms
[conf
->num_uniforms
].type
= type
;
559 conf
->num_uniforms
++;
562 if (strlen(line
) > 1) {
563 fprintf(stderr
, "syntax error in: %s\n", line
);
577 GLdouble vertTime
, fragTime
, linkTime
;
578 struct config_file config
;
580 memset(&config
, 0, sizeof(config
));
583 ReadConfigFile(ConfigFile
, &config
);
585 if (!VertShaderFile
) {
586 fprintf(stderr
, "Error: no vertex shader\n");
590 if (!FragShaderFile
) {
591 fprintf(stderr
, "Error: no fragment shader\n");
595 if (!ShadersSupported())
598 vertShader
= CompileShaderFile(GL_VERTEX_SHADER
, VertShaderFile
);
599 vertTime
= GetShaderCompileTime();
600 fragShader
= CompileShaderFile(GL_FRAGMENT_SHADER
, FragShaderFile
);
601 fragTime
= GetShaderCompileTime();
603 Program
= LinkShaders(vertShader
, fragShader
);
604 linkTime
= GetShaderLinkTime();
606 printf("Read vert shader %s\n", VertShaderFile
);
607 printf("Read frag shader %s\n", FragShaderFile
);
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
);