Sunday, February 6, 2011

A C++ class for GLSLShader

I had written a pure C++ class called GLSLShader for handling the common GLSLShader compilation and linking chores. Here are the details:

The Class Interface
 

#pragma once
#include <GL/glew.h>
#include <map>
#include <string>

using namespace std;

class GLSLShader
{
public:
 GLSLShader(void);
 ~GLSLShader(void); 
 void LoadFromString(GLenum whichShader, const string& source);
 void LoadFromFile(GLenum whichShader, const string& filename);
 void CreateAndLinkProgram();
 void Use();
 void UnUse();
 void AddAttribute(const string& attribute);
 void AddUniform(const string& uniform);
 GLuint GetProgram() const;
 //An indexer that returns the location of the attribute/uniform
 GLuint operator[](const string& attribute);
 GLuint operator()(const string& uniform);

private:
 enum ShaderType {VERTEX_SHADER, FRAGMENT_SHADER, GEOMETRY_SHADER};
 GLuint _program;
 int _totalShaders;
 GLuint _shaders[3];//0-> vertexshader, 1-> fragmentshader, 2-> geometryshader
 map<string,gluint> _attributeList;
 map<string,gluint> _uniformLocationList;
};

The attributes and uniform locations are stored in a map which are populated once so that we dont get the locations of the attributes and uniforms everytime. Then using an indexer (using () or [] operator), the locations of the attribute and uniform are accessed so that client application can modify them as needed.

Typical usage
Create a reference first and then call the LoadFromFile function to load a GLSLShader from a text file or call LoadFromString to load a GLSL shader from a string. The first parameter is the shader type which may be (GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER). The second parameter is either the file name (for LoadFromFile function) or a string containing the shader contents (for LoadFromString function).
 
GLSLShader shader;
shader.LoadFromFile(GL_VERTEX_SHADER,"Shader.vp");
shader.LoadFromFile(GL_FRAGMENT_SHADER,"Shader.fp");

Next the call to compile and link is issued.
 
shader.CreateAndLinkProgram();

Once compiled we may use the shader and then add the uniforms and attributes as needed. For one time uniforms, we may set their values here.
 

shader.Use(); 
   shader.AddAttribute("vVertex");
   shader.AddAttribute("vNormal");
   shader.AddAttribute("vTexCoord");
   shader.AddUniform("MVP");
shader.UnUse();

When we want to get the location of any attribute for the vertex buffers, we may use the indexers as follows,
 
glEnableVertexAttribArray(shader["vVertex"]);
glVertexAttribPointer(shader["vVertex"],  4, GL_FLOAT, GL_FALSE, 0, 0);

Note that shader["vVertex"] returns the attribute location of "vVertex" attribute from the shader. Likewise, for handling of uniforms, we may do something like this in the render function,
 
shader.Use();
   glUniformMatrix4fv(shader("MVP"), 1, GL_FALSE,  glm::value_ptr(MVP)); 
   glDrawElements(GL_TRIANGLES, ...);
shader.UnUse();

The class also handles the deletion of program and shaders on exit making this a very compact class for handling of GLSLShaders. Here are the source files.

4 comments:

  1. nice, but can you share sources?
    ^)

    ReplyDelete
  2. Hi Korak,
    Well the sources are shared in my OpenSource project opencloth. Check the GLSL cloth code in this project.
    http://code.google.com/p/opencloth

    ReplyDelete
  3. Some performance notice:
    * pass std::strings by reference
    * use binary search in sorted std::vector rather then std::map (or even better to use hash_map)

    ReplyDelete
  4. Thanks Korak,
    I have noted your corrections.

    ReplyDelete