Create a Game Engine: Part I Shader Manager

In the first chapters of this series I told you that we somehow want to create a basic Game Engine and keep our architecture as decoupled as we can. I know that this might sound a little crazy to begin working on a Game Engine or to an architecture knowing how to render just a triangle in NDC space and not touching several graphics techniques. But I think that in the end this will pay off and we can easily insert anything we want in our engine. So we are not creating a program to handle just a specific technique, we are creating an engine to handle that technique along with other techniques. At this moment, this might be a little boring because nothing spectacular will happen from a visual angle.

Until now, if you followed our tutorials up to this point, you saw that we tried to keep our architecture as decoupled as we could. I mean for God’s sake we created 10 tutorials just to render one triangle :)).

However there are a few problems you might notice:

  1. Up to now we can load just one shader and use just one program with our Shader_Loader class which was presented here What happens if we need another shader in our engine?
  2. Too many things are happening in the main.cpp file (Init GLUT, GLEW,  loading shaders, loading the triangle, render).
  3. Loading shaders,loading the model (triangle) and render the model is also in main.cpp
  4. GameModels class is too specific in creating just a triangle. We must be able to load other models too.

I have to tell you right from the beginning that this architecture will suffer modifications and refactoring in the future and what are we doing now is just a basic engine. Also at the end of this chapter I will write about the performance issues with this architecture. Let’s see how the whole architecture for this engine will look:

Game Engine Architecture. In this part we deal with ShaderManager

Game Engine Architecture. In this part we deal with ShaderManager

So, first things first let’s solve the Shader_Loader class to be able to load other shaders too. So what we are going to do is:

  • Keep these programs in a map structure (the map from STL) where the key is the shader name.
  • Have a GetProgram method to get a specific shader.
  • Make the map and the GetProgram method static to access them anywhere.
  • Replace all  param of type char* with std::string for flexibility.
  • Create the Shader Manager by renaming Shader_Loader to Shader_Manager. 
  • Create a folder in your project called Managers and move the Shader_Manager.cpp and Shader_Manager.h in this folder.
  • Rename the namespace Core for this class with Managers.
  • Delete programs in destructor.

Program structure should look like this:

Shader Manager

Project Structure

Your Shader_Manager.h should look like this:

//modify from
#pragma once
#include <fstream>
#include <iostream>
#include <map>
#include <vector>
#include "../Dependencies/glew/glew.h"
#include "../Dependencies/freeglut/freeglut.h"
namespace Managers

 class Shader_Manager



                           //modify char* to std::string
        void CreateProgram(const std::string& shaderName,
                           const std::string& VertexShaderFilename,
                           const std::string& FragmentShaderFilename);

       static const GLuint GetShader(const std::string&);

                            //modify char* to std::string
       std::string ReadShader(const std::string& filename);
                            //modify char* to std::string
       GLuint CreateShader(GLenum shaderType,
                           const std::string& source,
                           const std::string& shaderName);

       static std::map<std::string, GLuint> programs;

I think that it is obvious why we renamed our class from Shader_Loader to Shader_Manager. Beside loading and creating one shader program, we can load and create multiple shaders, provide the specific shader when is required and of course delete them. So this class act like a manager. Big engines (like Ogre for example) use this name convention. Of course there are other ways to implement this, feel free to comment below what you prefer if you done this before.

Now let’s see what changed in the cpp file( I pointed out in comments):

#include "Shader_Manager.h"

using namespace Managers;

//don't forget about this little static guy in cpp
std::map<std::string, GLuint> Shader_Manager::programs;


//destructor delete programs

   std::map<std::string, GLuint>::iterator i;
   for (i = programs.begin();i != programs.end(); ++i)
        GLuint pr = i->second;

std::string Shader_Manager::ReadShader(const std::string& filename)
 //same, nothing modified
 // use filename.c_str() to convert to const char*

GLuint Shader_Manager::CreateShader(GLenum shaderType,
                                    const std::string& source,
                                    const std::string& shaderName)

 //same, nothing modified
 //use c_str() to convert to const char* whre is required


void Shader_Manager::CreateProgram(const std::string& shaderName,
                                   const std::string& vertexShaderFilename,
                                   const std::string& fragmentShaderFilename)

 //same, nothing modified
 //use c_str() to convert to const char* where is required
 //last line of this function instead of return program will be:
    programs[shaderName] = program;
//also don't forget to check if the shaderName is already in the map
//you could use programs.insert; but it's your call

//the new method used to get the program
const GLuint Shader_Manager::GetShader(const std::string& shaderName)
    //make sure that you check if program exist first
    //before you return it


Of course beside GetShader other methods can be static too. But for the moment we only need the GetShader and create them in one place with a normal method. You can see that I didn’t check if the program exists or if the key is already in the map, to keep the tutorial simple. You should always do this checks otherwise you can end up with a black triangle because your program was deleted somewhere in the flow or you misspelled the shader name. Also you can implement a DeleteShader(name) method to delete a specific shader (homework).

Now going back in main.cpp we can use the manager like this:

//just after includes
Managers::Shader_Manager* shaderManager;

void Init()


  gameModels = new Models::GameModels();
  //load and compile shaders
  shaderManager = new Managers::Shader_Manager(); //thanks to Erik
                                                  // for pointing this out
   program = Managers::Shader_Manager::GetShader("colorShader");


//don't forget to delete the manager in main
int main(int argc, char **argv)


  delete gameModels;
  delete shaderManager;
  return 0;
As always, what can be more beautiful ?

As always, what can be more beautiful  than this boring triangle :))?

One problem is solve, now we can load other shaders too in our program. In the next tutorial we take care of this dirty main.cpp breaking him in other modules. 

// source code updated 3/9/2015

Source code: 1_Setting_OpenGL_multiple_shaders

Tagged under:
blog comments powered by Disqus