/** * Utilities for OpenGL shading language * * Brian Paul * 9 April 2008 */ #include #include #include #include #include #include "glut_wrap.h" #include "shaderutil.h" /** time to compile previous shader */ static GLdouble CompileTime = 0.0; /** time to linke previous program */ static GLdouble LinkTime = 0.0; PFNGLCREATESHADERPROC CreateShader = NULL; PFNGLDELETESHADERPROC DeleteShader = NULL; PFNGLSHADERSOURCEPROC ShaderSource = NULL; PFNGLGETSHADERIVPROC GetShaderiv = NULL; PFNGLGETSHADERINFOLOGPROC GetShaderInfoLog = NULL; PFNGLCREATEPROGRAMPROC CreateProgram = NULL; PFNGLDELETEPROGRAMPROC DeleteProgram = NULL; PFNGLATTACHSHADERPROC AttachShader = NULL; PFNGLLINKPROGRAMPROC LinkProgram = NULL; PFNGLUSEPROGRAMPROC UseProgram = NULL; PFNGLGETPROGRAMIVPROC GetProgramiv = NULL; PFNGLGETPROGRAMINFOLOGPROC GetProgramInfoLog = NULL; PFNGLVALIDATEPROGRAMPROC ValidateProgram = NULL; PFNGLUNIFORM1IPROC Uniform1i = NULL; PFNGLUNIFORM1FVPROC Uniform1fv = NULL; PFNGLUNIFORM2FVPROC Uniform2fv = NULL; PFNGLUNIFORM3FVPROC Uniform3fv = NULL; PFNGLUNIFORM4FVPROC Uniform4fv = NULL; PFNGLUNIFORMMATRIX4FVPROC UniformMatrix4fv = NULL; PFNGLGETACTIVEATTRIBPROC GetActiveAttrib = NULL; PFNGLGETATTRIBLOCATIONPROC GetAttribLocation = NULL; GLboolean ShadersSupported(void) { if (GLAD_GL_VERSION_2_0) { CreateShader = glCreateShader; DeleteShader = glDeleteShader; ShaderSource = glShaderSource; GetShaderiv = glGetShaderiv; GetShaderInfoLog = glGetShaderInfoLog; CreateProgram = glCreateProgram; DeleteProgram = glDeleteProgram; AttachShader = glAttachShader; LinkProgram = glLinkProgram; UseProgram = glUseProgram; GetProgramiv = glGetProgramiv; GetProgramInfoLog = glGetProgramInfoLog; ValidateProgram = glValidateProgram; Uniform1i = glUniform1i; Uniform1fv = glUniform1fv; Uniform2fv = glUniform2fv; Uniform3fv = glUniform3fv; Uniform4fv = glUniform4fv; UniformMatrix4fv = glUniformMatrix4fv; GetActiveAttrib = glGetActiveAttrib; GetAttribLocation = glGetAttribLocation; return GL_TRUE; } fprintf(stderr, "Sorry, GLSL not supported with this OpenGL.\n"); return GL_FALSE; } GLuint CompileShaderText(GLenum shaderType, const char *text) { GLuint shader; GLint stat; GLdouble t0, t1; shader = CreateShader(shaderType); ShaderSource(shader, 1, (const GLchar **) &text, NULL); t0 = glutGet(GLUT_ELAPSED_TIME) * 0.001; glCompileShader(shader); t1 = glutGet(GLUT_ELAPSED_TIME) * 0.001; CompileTime = t1 - t0; GetShaderiv(shader, GL_COMPILE_STATUS, &stat); if (!stat) { GLchar log[1000]; GLsizei len; GetShaderInfoLog(shader, 1000, &len, log); fprintf(stderr, "Error: problem compiling shader: %s\n", log); exit(1); } else { /*printf("Shader compiled OK\n");*/ } return shader; } /** * Read a shader from a file. */ GLuint CompileShaderFile(GLenum shaderType, const char *filename) { const int max = 100*1000; int n; char *buffer = (char*) malloc(max); GLuint shader; FILE *f; f = fopen(filename, "r"); if (!f) { fprintf(stderr, "Unable to open shader file %s\n", filename); free(buffer); return 0; } n = fread(buffer, 1, max, f); /*printf("read %d bytes from shader file %s\n", n, filename);*/ if (n > 0) { buffer[n] = 0; shader = CompileShaderText(shaderType, buffer); } else { fclose(f); free(buffer); return 0; } fclose(f); free(buffer); return shader; } GLuint LinkShaders(GLuint vertShader, GLuint fragShader) { return LinkShaders3(vertShader, 0, fragShader); } GLuint LinkShaders3(GLuint vertShader, GLuint geomShader, GLuint fragShader) { GLuint program = CreateProgram(); GLdouble t0, t1; assert(vertShader || fragShader); if (vertShader) AttachShader(program, vertShader); if (geomShader) AttachShader(program, geomShader); if (fragShader) AttachShader(program, fragShader); t0 = glutGet(GLUT_ELAPSED_TIME) * 0.001; LinkProgram(program); t1 = glutGet(GLUT_ELAPSED_TIME) * 0.001; LinkTime = t1 - t0; /* check link */ { GLint stat; GetProgramiv(program, GL_LINK_STATUS, &stat); if (!stat) { GLchar log[1000]; GLsizei len; GetProgramInfoLog(program, 1000, &len, log); fprintf(stderr, "Shader link error:\n%s\n", log); return 0; } } return program; } GLuint LinkShaders3WithGeometryInfo(GLuint vertShader, GLuint geomShader, GLuint fragShader, GLint verticesOut, GLenum inputType, GLenum outputType) { GLuint program = CreateProgram(); GLdouble t0, t1; assert(vertShader || fragShader); if (vertShader) AttachShader(program, vertShader); if (geomShader) { AttachShader(program, geomShader); glProgramParameteri(program, GL_GEOMETRY_VERTICES_OUT, verticesOut); glProgramParameteri(program, GL_GEOMETRY_INPUT_TYPE, inputType); glProgramParameteri(program, GL_GEOMETRY_OUTPUT_TYPE, outputType); } if (fragShader) AttachShader(program, fragShader); t0 = glutGet(GLUT_ELAPSED_TIME) * 0.001; LinkProgram(program); t1 = glutGet(GLUT_ELAPSED_TIME) * 0.001; LinkTime = t1 - t0; /* check link */ { GLint stat; GetProgramiv(program, GL_LINK_STATUS, &stat); if (!stat) { GLchar log[1000]; GLsizei len; GetProgramInfoLog(program, 1000, &len, log); fprintf(stderr, "Shader link error:\n%s\n", log); return 0; } } return program; } GLboolean ValidateShaderProgram(GLuint program) { GLint stat; ValidateProgram(program); GetProgramiv(program, GL_VALIDATE_STATUS, &stat); if (!stat) { GLchar log[1000]; GLsizei len; GetProgramInfoLog(program, 1000, &len, log); fprintf(stderr, "Program validation error:\n%s\n", log); fflush(stderr); return 0; } return (GLboolean) stat; } GLdouble GetShaderCompileTime(void) { return CompileTime; } GLdouble GetShaderLinkTime(void) { return LinkTime; } void SetUniformValues(GLuint program, struct uniform_info uniforms[]) { GLuint i; for (i = 0; uniforms[i].name; i++) { uniforms[i].location = glGetUniformLocation(program, uniforms[i].name); switch (uniforms[i].type) { case GL_INT: case GL_SAMPLER_1D: case GL_SAMPLER_2D: case GL_SAMPLER_3D: case GL_SAMPLER_CUBE: case GL_SAMPLER_2D_RECT_ARB: case GL_SAMPLER_1D_SHADOW: case GL_SAMPLER_2D_SHADOW: case GL_SAMPLER_1D_ARRAY: case GL_SAMPLER_2D_ARRAY: case GL_SAMPLER_1D_ARRAY_SHADOW: case GL_SAMPLER_2D_ARRAY_SHADOW: assert(uniforms[i].value[0] >= 0.0F); Uniform1i(uniforms[i].location, (GLint) uniforms[i].value[0]); break; case GL_FLOAT: Uniform1fv(uniforms[i].location, 1, uniforms[i].value); break; case GL_FLOAT_VEC2: Uniform2fv(uniforms[i].location, 1, uniforms[i].value); break; case GL_FLOAT_VEC3: Uniform3fv(uniforms[i].location, 1, uniforms[i].value); break; case GL_FLOAT_VEC4: Uniform4fv(uniforms[i].location, 1, uniforms[i].value); break; case GL_FLOAT_MAT4: UniformMatrix4fv(uniforms[i].location, 1, GL_FALSE, uniforms[i].value); break; default: if (strncmp(uniforms[i].name, "gl_", 3) == 0) { /* built-in uniform: ignore */ } else { fprintf(stderr, "Unexpected uniform data type in SetUniformValues\n"); abort(); } } } } /** Get list of uniforms used in the program */ GLuint GetUniforms(GLuint program, struct uniform_info uniforms[]) { GLint n, max, i; GetProgramiv(program, GL_ACTIVE_UNIFORMS, &n); GetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max); for (i = 0; i < n; i++) { GLint size, len; GLenum type; char name[100]; glGetActiveUniform(program, i, 100, &len, &size, &type, name); uniforms[i].name = strdup(name); uniforms[i].size = size; uniforms[i].type = type; uniforms[i].location = glGetUniformLocation(program, name); } uniforms[i].name = NULL; /* end of list */ return n; } void PrintUniforms(const struct uniform_info uniforms[]) { GLint i; printf("Uniforms:\n"); for (i = 0; uniforms[i].name; i++) { printf(" %d: %s size=%d type=0x%x loc=%d value=%g, %g, %g, %g\n", i, uniforms[i].name, uniforms[i].size, uniforms[i].type, uniforms[i].location, uniforms[i].value[0], uniforms[i].value[1], uniforms[i].value[2], uniforms[i].value[3]); } } /** Get list of attribs used in the program */ GLuint GetAttribs(GLuint program, struct attrib_info attribs[]) { GLint n, max, i; GetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &n); GetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max); for (i = 0; i < n; i++) { GLint size, len; GLenum type; char name[100]; GetActiveAttrib(program, i, 100, &len, &size, &type, name); attribs[i].name = strdup(name); attribs[i].size = size; attribs[i].type = type; attribs[i].location = GetAttribLocation(program, name); } attribs[i].name = NULL; /* end of list */ return n; } void PrintAttribs(const struct attrib_info attribs[]) { GLint i; printf("Attribs:\n"); for (i = 0; attribs[i].name; i++) { printf(" %d: %s size=%d type=0x%x loc=%d\n", i, attribs[i].name, attribs[i].size, attribs[i].type, attribs[i].location); } }