sunLoadingImage
whowedImag
decoration left 1
decoration left 2
transhome
transprojects
transgallery
transarticles
decoration rigth
English
Українська
Show/Hide search bar
black cat logo variable logo
[18 Jan 2013]

Compilation of GLSL shaders in OpenGL

Shaders are programs for GPUs that allow to reprogram different parts of graphical pipeline. Before introduction of shaders (programable pipeline), configuration of graphical pileline was done by changing states of rendering pipeline (fixed pipeline). In GLSL shader is compiled part of GLSL code. Single shader cannot be used in rendering pipeline, because shader represents only single shader stage. There're following shader stages: vertex shader, tesselation control shader, tesselation evaluation shader, geometry shader and fragement shader. To use shader in graphical pipeline you should combine different shaders into a shader program. Vertex and fragment shaders are obligatory for valid shader program. Other shader stages are optional. To create valid shader program you have to: load and compile shaders, attach shaders to shader program, link shader program, and also check for errors after compilation and linking. After that shader program is ready to be used as part of graphical pipeline. Rendering without active shader program will lead to error.
Following example shows how to create new shader program from vertex and fragment shaders:
Create new shader program:
GLuint loadShaderProgram(const QString & vertexShPath, const QString & fragmentShPath)
{
   GLuint shaderProgram(0), vertexShader(0), fragmentShader(0);
   
   // Compile vertex shader
   if(!compileShader(GL_VERTEX_SHADER, vertexShader, vertexShPath))
   {
      return -1;
   }

   // Compile fragment shader
   if(!compileShader(GL_FRAGMENT_SHADER, fragmentShader, fragmentShPath))
   {
      return -1;
   }

   // Create new shader program
   shaderProgram = glCreateProgram();
   // Attach shaders to shader program
   glAttachShader(shaderProgram, vertexShader);
   glAttachShader(shaderProgram, fragmentShader);
   // Link shader program
   glLinkProgram(shaderProgram);
   // Detach shader from shader program
   glDetachShader?(shaderProgram?, vertexShader);
   glDetachShader?(shaderProgram, fragmentShader?);
   // Remove shaders if you don't want to use them
   glDeleteShader(vertexShader);
   glDeleteShader(fragmentShader);

   // Check whether linking was successful
   GLint testVal;
   glGetProgramiv(shaderProgram, GL_LINK_STATUS, &testVal);
   if(testVal == GL_FALSE)
   {
      char infolog[1024];
      glGetProgramInfoLog(shaderProgram, 1024, NULL, infolog);
      // output infolog here if you want
      glDeleteProgram(shaderProgram);
      return -1;
   }

   return shaderProgram;
}
Compile shader:
bool compileShader(GLenum shaderType, GLuint & shader, const QString & path)
{
   QString source;

   // load sources of shader from file
   if(!loadSourceFromFile(path,source))
   {
      return false;
   }
   QByteArray byteArray = source.toUtf8();
   const char* vertCString = byteArray.constData();

   // create new shader with specified type
   shader = glCreateShader(shaderType);
   // set shader sources
   glShaderSource(shader, 1, &vertCString, nullptr);
   // compile shader
   glCompileShader(shader);
   // check whether compilation was successful
   if(!self->checkShaderStatus(shader, GL_COMPILE_STATUS))
   {
      glDeleteShader(shader);
      return false;
   }

   return true;
}
Load GLSL code sources from file:
bool loadSourceFromFile(const QString & path, QString &source)
{
   QFile fl(path);
   // Load contents of file
   if(!fl.open(QIODevice::ReadOnly))
   {
      return false;
   }
   QTextStream in(&fl);
   source = in.readAll();
   fl.close();

   return true;
}
Checks status of compilation:
bool checkShaderStatus(GLuint shader, GLenum status)
{
   GLint testVal = 0;
   // get compilation status from OpenGL
   glGetShaderiv(shader, status, &testVal);
   if(testVal == GL_FALSE)
   {
      char infolog[1024];
      // get description of the error
      glGetShaderInfoLog(shader, 1024, NULL, infolog);
      // you can output infolog here
      return false;
   }
   return true;
}
After linking of the shader program you can get infromation about all attributes and uniforms. This info may be usefull if you want to check whether the shader program has all required variables for your rendering needs, or to save locations of frequentrly used attributes and uniforms (like locations of matrices, etc). Following example shows how to do that:
// shader program should be active during following calls
glUseProgram(shaderProgram);

// get number of uniforms in shader program
GLint numUniforms = 0;
glGetProgramiv(shaderProgram, GL_ACTIVE_UNIFORMS, &numUniforms);
for(GLuint i=0; i<numUniforms; ++i)
{
   int name_len=-1, unifsize=-1;
   GLenum type = GL_ZERO;
   char name[100];
   // get name of uniform with index i
   glGetActiveUniform(shaderProgram, i, sizeof(name)-1,
                  &name_len, &unifsize, &type, name );
   name[name_len] = 0;
   // get location of uniform with name
   GLuint location = glGetUniformLocation(shaderProgram, name);
   
   // now you can use location and name of uniform
}

// get number of attributes from shader program (vertex shader)
GLint numAttributes=0;
glGetProgramiv(shaderProgram, GL_ACTIVE_ATTRIBUTES, &numAttributes);
for(GLuint i=0; i<numAttributes; ++i)
{
   int name_len=-1, unifsize=-1;
   GLenum type = GL_ZERO;
   char name[100];
   // get name of attribute with index i
   glGetActiveAttrib(shaderProgram, i, sizeof(name)-1,
                  &name_len, &unifsize, &type, name);
   name[name_len] = 0;
   // get location of attribute with name
   GLuint location = glGetAttribLocation(shaderProgram, name);
   
   // now you can use location and name of attribute
}
If there's no attribute or uniform in the shader program with requested name, then glUniformLocation() will return -1. Following function outputs error message if it fails to find location of variable with specified name. Use it only during initialization, not in redering calls.
GLint getUniformLocation(const QString & name)
{
   // get location of uniform with name
   //and output warning if there is no such uniform

   GLint loc = glGetUniformLocation(shaderProgram, name.toLatin1());
   if(loc == -1)
   {
      Log::instance().log("Failed to get location of uniform : " + name);
   }
   return loc;

   // similar funcion for attributes
}
Following code allows to check whether the shader program may be correctly executed with current state of OpenGL context. It's usefull only for development, not for release. It outputs information about critical problems or performance issues of the the shader program. Output messages of this function depends on implementation of the driver.
glUseProgram(shaderProgram);
glValidateProgram(shaderProgram);
glGetProgramiv(shaderProgram, GL_VALIDATE_STATUS, &testVal);
if(testVal == GL_FALSE)
{
   char infolog[1024];
   glGetProgramInfoLog(shaderProgram, 1024, NULL, infolog);
   // output infolog here
   glDeleteProgram(shaderProgram);
   return;
}
Before linking of the shader program you can set indices which should be used by attributes of vertex shader and results of fragment shader. Also before linking you can configure settings of Transform Feedback with glTransformFeedbackVaryings() function. Don't forget to link the shader program after that.
// attribute with name attributeName should have location 3
glBindAttribLocation(shaderProgram, 3, attributeName);
// output value of fragment shader with name outColor should have location 1
glBindFragDataLocation(shaderProgram, 1, outColor);
// тепер необхідно знову відлінкувати шейдерну програму
glLinkProgram(shaderProgram);
Delete the shader program if it's no longer required.
glDeleteProgram(shaderProgram);


Sun and Black Cat- Igor Dykhta (igor dykhta email) © 2007-2014