Compile and Link OpenGL Shader Language - GLSL

This is a header-only mini-library to compile simple OpenGL shaders and link them into shader programs. You can also find a working example of its use HERE.

Why?

Let’s read some GLSL files, compile them, then Link them into a program the old fashioned way. Code taken from learnopengl.com.

// I'll spare you the wall of text. Just go to the learnopengl.com link above and you'll see how arduous it is.

That’s more than I want to write and it only covers two shaders. Let’s use this library!

GLProgram simple{
    {
        GLShader{GL_VERTEX_SHADER, "path/to/vert.glsl"},
        GLShader{GL_FRAGMENT_SHADER, "path/to/frag.glsl"}
    }
};

Whoa! That was easy! Now for the technical stuff.

Prerequisites

  • GLEW
  • C++11

Use

// Construct a linked OpenGL Shader Program with an implicitly converted std::vector< GLShader >
GLProgram simple{
    {
        GLShader{GL_VERTEX_SHADER, "path/to/vert.glsl"},
        GLShader{GL_FRAGMENT_SHADER, "path/to/frag.glsl"}
    }
};

// Instruct OpenGL server to use the compiled program by calling glUseProgram()
// First checks whether it contains a valid shader program
simple.use();

// Return the Program as GLuint for use in OpenGL functions
// First checks whether it contains a valid shader program
GLint someAttributeInShader = glGetAttribLocation(simple.program(), "someAttributeInShader");

Declarations

class GLShader{
public:
    GLuint type;
    std::string fileName = "";
    GLuint compiled;

    GLShader();
    GLShader(const GLuint& t, const std::string& fn);

    // Rule of 5's
    GLShader(const GLShader& rhs);
    GLShader& operator=(const GLShader& rhs);
    GLShader(const GLShader&& o);
    GLShader& operator=(const GLShader&& o);
    ~GLShader();
};

class GLProgram{
public:
    GLuint prg;

    GLProgram();
    GLProgram(const std::vector< GLShader >& sh);

    // Rule of 5's
    GLProgram(const GLProgram& rhs);
    GLProgram& operator=(const GLProgram& rhs);
    GLProgram(const GLProgram&& o);
    GLProgram& operator=(const GLProgram&& o);
    ~GLProgram();

    GLuint program();
    void use();
};

Instantiations

// Arguments are GLenum shaderType and an std::string filename. Filename extension does not matter.
// Shaders are compiled upon instantiation
GLShader myVertexShader{GL_VERTEX_SHADER, "path/to/vert.glsl"};
GLShader myFragmentShader{GL_FRAGMENT_SHADER, "path/to/frag.glsl"};

std::vector< GLShader > myShaders = {myVertexShader, myFragmentShader};

// Create program with an std::vector< GLShader >
// You can supply an std::vector of any size
// The program is linked upon instantiation
GLProgram myFirstProgram{myShaders};

// Create program with implicitly converted std::vector< GLShader >
// You can supply a list of any size
GLProgram mySecondProgram{
    {
        GLShader{GL_VERTEX_SHADER, "path/to/vert.glsl"},
        GLShader{GL_FRAGMENT_SHADER, "path/to/frag.glsl"}
    }
};

Management

// Easy program management with map
#include <map>
#include <string>

GLProgram simple{
    {
        GLShader{GL_VERTEX_SHADER, "path/to/vert.glsl"},
        GLShader{GL_FRAGMENT_SHADER, "path/to/frag.glsl"}
    }
};
GLProgram complex{
    {
        GLShader{GL_VERTEX_SHADER, "path/to/vert.glsl"},
        GLShader{GL_FRAGMENT_SHADER, "path/to/frag.glsl"},
        GLShader{GL_GEOMETRY_SHADER, "path/to/geometry.glsl"}
    }
};

std::map< std::string, GLProgram > myPrograms = {
    {"simple", simple},
    {"complex", complex}
}

// Showing another way to instantiate
std::map< std::string, GLProgram > myOtherPrograms = {
    {"simple",
        GLProgram{
            {
                GLShader{GL_VERTEX_SHADER, "path/to/vertex.glsl"},
                GLShader{GL_FRAGMENT_SHADER, "path/to/fragment.glsl"},
            }
        }
    }
};

myPrograms["simple"].use();
GLint someAttributeInShader = glGetAttribLocation(myPrograms["simple"].program(), "someAttributeInShader");

myPrograms["complex"].use();
GLint someOtherAttributeInShader = glGetAttribLocation(myPrograms["complex"].program(), "someOtherAttributeInShader");</string></map>

Copying and Moving

#include <map>
#include <string>

GLShader myVertexShader;

GLShader tempVertex{GL_VERTEX_SHADER, "path/to/vert.glsl"};

// Copy Assignment
myVertexShader = tempVertex;

// Move Assignment
myVertexShader = GLShader{GL_VERTEX_SHADER, "path/to/vert.glsl"};

GLProgram simple{
    {
        GLShader{GL_VERTEX_SHADER, "path/to/vert.glsl"},
        GLShader{GL_FRAGMENT_SHADER, "path/to/frag.glsl"}
    }
};
GLProgram complex{
    {
        GLShader{GL_VERTEX_SHADER, "path/to/vert.glsl"},
        GLShader{GL_FRAGMENT_SHADER, "path/to/frag.glsl"},
        GLShader{GL_GEOMETRY_SHADER, "path/to/geometry.glsl"}
    }
};

std::map< std::string, GLProgram > myPrograms = {
    {"simple", simple},
    {"complex", complex}
}

// Showing another way to instantiate
std::map< std::string, GLProgram > myOtherPrograms = {
    {"simple",
        GLProgram{
            {
                GLShader{GL_VERTEX_SHADER, "path/to/vertex.glsl"},
                GLShader{GL_FRAGMENT_SHADER, "path/to/fragment.glsl"},
            }
        }
    },
    {"complex",
        GLProgram{
            {
                GLShader{GL_VERTEX_SHADER, "path/to/vertex.glsl"},
                GLShader{GL_FRAGMENT_SHADER, "path/to/fragment.glsl"},
                GLShader{GL_GEOMETRY_SHADER, "path/to/geometry.glsl"}
            }
        }
    }
};

// Copy Assignment
myPrograms["simple"] = myOtherPrograms["simple"];

// Move Assignment
myProgram["complex"] = GLProgram{
    {
        GLShader{GL_VERTEX_SHADER, "path/to/vertex.glsl"},
        GLShader{GL_FRAGMENT_SHADER, "path/to/fragment.glsl"},
        GLShader{GL_GEOMETRY_SHADER, "path/to/geometry.glsl"}
    }
};</string></map>