#32 - Shader Class C++

Date: 2018-10-27 12:00 - C++

Simple OpenGL Shader Wrapper class.

// Shader.h
#pragma once

#include "../OpenGL.h"

#include <list>
#include <map>
#include <string>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>

namespace YourNamespace {
	namespace Rendering {
		class Shader {
		public:
			static void unuse();
		private:
			GLuint _program;
			std::list<GLuint> _shaders;
			std::map<std::string, GLuint> _attributes;
			std::map<std::string, GLuint> _uniforms;
		public:
			Shader();
			bool compiled() const { return _program != 0; }
			void loadFromText(GLenum type, const std::string& source);
			void loadFromFile(GLenum type, const std::string& filename);
			virtual void createLinkProgram();
			bool setAttribLocation(const std::string& attribute, GLuint location);
			GLuint getAttibLocation(const std::string& attribute);
			GLuint getUniformLocation(const std::string& uniform);

			void uniform(unsigned int location, const glm::mat4& matrix) {
				glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(matrix));
			}

			void uniform(unsigned int location, const glm::vec2& value) {
				glUniform2f(location, value.x, value.y);
			}

			void uniform(unsigned int location, const glm::vec3& value) {
				glUniform3f(location, value.x, value.y, value.z);
			}

			void uniform(unsigned int location, const glm::vec4& value) {
				glUniform4f(location, value.x, value.y, value.z, value.w);
			}

			void uniform(unsigned int location, int value) {
				glUniform1i(location, value);
			}

			void uniform(unsigned int location, float value) {
				glUniform1f(location, value);
			}

			void uniform(unsigned int location, float v1, float v2) {
				glUniform2f(location, v1, v2);
			}

			void uniform(unsigned int location, float v1, float v2, float v3) {
				glUniform3f(location, v1, v2, v3);
			}

			void uniform(unsigned int location, float v1, float v2, float v3, float v4) {
				glUniform4f(location, v1, v2, v3, v4);
			}

			void uniform(const std::string& uniform, const glm::mat4& matrix) {
				glUniformMatrix4fv(getUniformLocation(uniform), 1, GL_FALSE, glm::value_ptr(matrix));
			}

			void uniform(const std::string& uniform, const glm::vec2& value) {
				glUniform2f(getUniformLocation(uniform), value.x, value.y);
			}

			void uniform(const std::string& uniform, const glm::vec3& value) {
				glUniform3f(getUniformLocation(uniform), value.x, value.y, value.z);
			}

			void uniform(const std::string& uniform, const glm::vec4& value) {
				glUniform4f(getUniformLocation(uniform), value.x, value.y, value.z, value.w);
			}

			void uniform(const std::string& uniform, int value) {
				glUniform1i(getUniformLocation(uniform), value);
			}

			void uniform(const std::string& uniform, float value) {
				glUniform1f(getUniformLocation(uniform), value);
			}

			void uniform(const std::string& uniform, float v1, float v2) {
				glUniform2f(getUniformLocation(uniform), v1, v2);
			}

			void uniform(const std::string& uniform, float v1, float v2, float v3) {
				glUniform3f(getUniformLocation(uniform), v1, v2, v3);
			}

			void uniform(const std::string& uniform, float v1, float v2, float v3, float v4) {
				glUniform4f(getUniformLocation(uniform), v1, v2, v3, v4);
			}

			void use() {
				glUseProgram(_program);
			}

			virtual ~Shader();
		};
	}
}
// Shader.cpp
#include "Shader.h"

#include <iostream>
#include <fstream>

#include "../Logging/Logger.h"

#define CHECK_IF_COMPILED if(_program != 0) throw "Shader already created.";

using namespace YourNamespace::Rendering;

void Shader::unuse() {
	glUseProgram(0);
}

Shader::Shader() : _program(0), _shaders() {
}

void Shader::loadFromText(GLenum type, const std::string& source) {
	CHECK_IF_COMPILED
		GLuint shader = glCreateShader(type);

	const char * ptmp = source.c_str();
	glShaderSource(shader, 1, &ptmp, NULL);

	GLint status;
	glCompileShader(shader);
	glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
	if (status == GL_FALSE) {
		GLint infoLogLength;
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
		GLchar *infoLog = new GLchar[infoLogLength];
		glGetShaderInfoLog(shader, infoLogLength, NULL, infoLog);
		std::string error = "Compile log: " + std::string(infoLog);
		delete[] infoLog;
		throw error;
	}
	_shaders.push_front(shader);
}

void Shader::loadFromFile(GLenum type, const std::string& filename) {
	CHECK_IF_COMPILED
		std::ifstream fp;
	fp.open(filename.c_str(), std::ios_base::in);
	if (fp) {
		std::string line, buffer;
		while (getline(fp, line)) {
			buffer.append(line);
			buffer.append("\r\n");
		}
		fp.close();
		loadFromText(type, buffer);
	}
	else
		throw ("Missing shader file: " + std::string(filename));
}

void Shader::createLinkProgram() {
	CHECK_IF_COMPILED
		_program = glCreateProgram();
	for (std::list<GLuint>::iterator shader = _shaders.begin(); shader != _shaders.end(); ++shader) {
		glAttachShader(_program, *shader);
	}

	std::map<std::string, GLuint>::iterator it = _attributes.begin();
	for (;it != _attributes.end(); it++) {
		const char * ptmp = it->first.c_str();
		glBindAttribLocation(_program, it->second, ptmp);
	}

	GLint status;
	glLinkProgram(_program);
	glGetProgramiv(_program, GL_LINK_STATUS, &status);
	if (status == GL_FALSE) {
		GLint infoLogLength;

		glGetProgramiv(_program, GL_INFO_LOG_LENGTH, &infoLogLength);
		GLchar *infoLog = new GLchar[infoLogLength];
		glGetProgramInfoLog(_program, infoLogLength, NULL, infoLog);
		std::string error = "Link log: " + std::string(infoLog);
		delete[] infoLog;
		throw error;
	}

	for (std::list<GLuint>::iterator shader = _shaders.begin(); shader != _shaders.end(); ++shader)
		glDeleteShader(*shader);
	_shaders.clear();
}

bool Shader::setAttribLocation(const std::string& attribute, GLuint location) {
	CHECK_IF_COMPILED
		_attributes[attribute] = location;
	return true;
}

GLuint Shader::getAttibLocation(const std::string& attribute) {
	GLuint location = -1;
	try {
		location = _attributes.at(attribute);
	}
	catch (const std::out_of_range& oor) {
		static_cast<void>(oor);
		location = glGetAttribLocation(_program, attribute.c_str());
		_attributes[attribute] = location;
		if (location == -1) {
			throw ("Attribute not found: " + std::string(attribute));
		}
	}
	return location;
}

GLuint Shader::getUniformLocation(const std::string& uniform) {
	std::map<std::string, GLuint>::iterator it = _uniforms.find(uniform);
	if (it != _uniforms.end())
		return (*it).second;

	GLuint location = glGetUniformLocation(_program, uniform.c_str());
	_uniforms[uniform] = location;
	if (location == -1) {
		throw ("Uniform not found: " + std::string(uniform));
	}
	return location;
}

Shader::~Shader() {
	glUseProgram(NULL);
	glDeleteProgram(_program);
}

Previous snippet | Next snippet