Browse Source

Classes for compiling and using shaders.

vectorfields
Vladimír Vondruš 16 years ago
parent
commit
3444f62638
  1. 114
      src/AbstractShaderProgram.cpp
  2. 190
      src/AbstractShaderProgram.h
  3. 2
      src/CMakeLists.txt
  4. 112
      src/Shader.cpp
  5. 191
      src/Shader.h

114
src/AbstractShaderProgram.cpp

@ -0,0 +1,114 @@
/*
Copyright © 2010 Vladimír Vondruš <mosra@centrum.cz>
This file is part of Magnum.
Magnum is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 3
only, as published by the Free Software Foundation.
Magnum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License version 3 for more details.
*/
#include "AbstractShaderProgram.h"
#include <iostream>
#include <fstream>
#define LINKER_MESSAGE_MAX_LENGTH 1024
using namespace std;
namespace Magnum {
AbstractShaderProgram::LogLevel AbstractShaderProgram::_logLevel = AbstractShaderProgram::Errors;
AbstractShaderProgram::AbstractShaderProgram(): state(Initialized) {
program = glCreateProgram();
}
AbstractShaderProgram::~AbstractShaderProgram() {
if(program) glDeleteProgram(program);
}
bool AbstractShaderProgram::use() {
if(state != Linked) return false;
glUseProgram(program);
return true;
}
bool AbstractShaderProgram::loadShader(Shader* shader) {
if(!shader) return false;
GLuint _shader = shader->compile();
if(!_shader) return false;
glAttachShader(program, _shader);
return true;
}
bool AbstractShaderProgram::bindAttribute(GLuint location, const string& name) {
/* Check whether given id already exists */
if(attributes.find(location) != attributes.end()) return false;
/* Check whether given name already exists */
for(map<GLuint, string>::const_iterator it = attributes.begin(); it != attributes.end(); ++it)
if(it->second == name) return false;
attributes.insert(pair<GLuint, string>(location, name));
return true;
}
void AbstractShaderProgram::link() {
/* Already compiled or failed, exit */
if(state != Initialized) return;
/* Set state to failed if anything goes wrong */
state = Failed;
/* Bind attributes to specified locations */
for(map<GLuint, string>::const_iterator it = attributes.begin(); it != attributes.end(); ++it)
glBindAttribLocation(program, it->first, it->second.c_str());
/* Link shader program */
glLinkProgram(program);
/* Check link status */
GLint status;
glGetProgramiv(program, GL_LINK_STATUS, &status);
/* Display errors or warnings */
if((status == GL_FALSE && _logLevel != None) || _logLevel == Warnings) {
/* Get message */
char message[LINKER_MESSAGE_MAX_LENGTH];
glGetProgramInfoLog(program, LINKER_MESSAGE_MAX_LENGTH, 0, message);
/* Show error log and delete shader */
if(status == GL_FALSE) {
cerr << "Shader program failed to link. Error message:" << endl
<< message << endl;
/* Or just warnings, if there are any */
} else if(message[0] != 0) {
cerr << "Shader program was linked with the following warnings:" << endl
<< message << endl;
}
}
if(status == GL_FALSE) {
glDeleteProgram(program);
program = 0;
state = Failed;
} else state = Linked;
}
GLint AbstractShaderProgram::uniformLocation(const std::string& name) {
if(state != Linked) return -1;
return glGetUniformLocation(program, name.c_str());
}
}

190
src/AbstractShaderProgram.h

@ -0,0 +1,190 @@
#ifndef Magnum_AbstractShaderProgram_h
#define Magnum_AbstractShaderProgram_h
/*
Copyright © 2010 Vladimír Vondruš <mosra@centrum.cz>
This file is part of Magnum.
Magnum is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 3
only, as published by the Free Software Foundation.
Magnum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License version 3 for more details.
*/
/** @file
* @brief Class Magnum::AbstractShaderProgram
*/
#include <string>
#include <map>
#include "Magnum.h"
#include "Shader.h"
namespace Magnum {
/**
@brief Base class for shaders
This class is designed to be used via subclassing. Subclasses define these
functions and properties:
- @b Constructor, which loads particular shaders, links the program, binds
attribute locations and gets uniform locations.
- <strong>Attribute location</strong> enum with indexes where the particular
attribute is bound, for example:
@code
enum Attribute {
VertexCoordinates = 1,
Color = 2,
TextureCoordinates = 3,
Normal = 4
};
@endcode
See also bindAttribute().
- <strong>Uniform binding functions</strong>, which set shader uniforms with
setUniform() and setUniformArray() functions. Example:
@code
void setProjectionMatrixUniform(const Matrix4& matrix);
@endcode
Basic workflow with AbstractShaderProgram subclasses is: instancing the class
(once at the beginning), then in every frame calling use(), binding uniforms
and assigning vertex buffers to given attribute locations.
*/
class AbstractShaderProgram {
DISABLE_COPY(AbstractShaderProgram)
public:
/** @brief Logging level */
enum LogLevel {
None, /**< @brief Don't display anything */
Errors, /**< @brief Display only errors */
Warnings /**< @brief Display only errors and warnings */
};
/**
* @brief Log level
*
* Log level for displaying compilation messages. Default is Errors.
*/
inline static LogLevel logLevel() { return _logLevel; }
/** @brief Set log level */
inline static void setLogLevel(LogLevel level) { _logLevel = level; }
/** @brief Default constructor */
AbstractShaderProgram();
/**
* @brief Destructor
*
* Deletes the shader program.
*/
~AbstractShaderProgram();
/**
* @brief Use shader
* @return False if the program wasn't successfully linked, true
* otherwise.
*/
bool use();
protected:
/**
* @brief Load shader
* @return False if the shader wasn't successfully compiled, true
* otherwise.
*
* Compiles the shader, if it is not already, and prepares it for
* linking.
* @note The shader should be deleted by caller after linking.
*/
bool loadShader(Shader* shader);
/**
* @brief Bind attribute to given location
* @param location Location
* @param name Attribute name
* @return False if the location or name is already bound, true
* otherwise.
*
* Binds attribute to the location which can be used later when binding
* vertex buffers.
* @note This function should be called between loadShader() calls
* and link().
*/
bool bindAttribute(GLuint location, const std::string& name);
/**
* @brief Link the shader
*
* Binds previously specified attributes to given indexes and links the
* shader program together.
*/
void link();
/**
* @brief Get uniform location
* @param name Uniform name
*
* @note This function should be called after link().
*/
GLint uniformLocation(const std::string& name);
/**
* @brief Set uniform value
* @param location Uniform location (see uniformLocation())
* @param value Value
*
* @attention This function doesn't check whether this shader is in use!
*/
inline void setUniform(GLint location, GLint value) {
glUniform1i(location, value);
}
/** @copydoc setUniform(GLint, GLint) */
void setUniform(GLint location, GLuint value) {
glUniform1ui(location, value);
}
/** @copydoc setUniform(GLint, GLint) */
void setUniform(GLint location, GLfloat value) {
glUniform1f(location, value);
}
/** @copydoc setUniform(GLint, GLint) */
void setUniform(GLint location, const Vector3& value) {
glUniform3fv(location, 1, value.data());
}
/** @copydoc setUniform(GLint, GLint) */
void setUniform(GLint location, const Vector4& value) {
glUniform4fv(location, 1, value.data());
}
/** @copydoc setUniform(GLint, GLint) */
void setUniform(GLint location, const Matrix4& value) {
glUniformMatrix4fv(location, 1, GL_FALSE, value.data());
}
private:
enum State {
Initialized,
Linked,
Failed
};
static LogLevel _logLevel;
GLuint program;
State state;
std::map<GLuint, std::string> attributes;
};
}
#endif

2
src/CMakeLists.txt

@ -7,8 +7,10 @@ add_subdirectory(Math)
set(Magnum_SRCS
AbstractObject.cpp
AbstractShaderProgram.cpp
Camera.cpp
Scene.cpp
Shader.cpp
)
add_library(Magnum SHARED ${Magnum_SRCS})

112
src/Shader.cpp

@ -0,0 +1,112 @@
/*
Copyright © 2010 Vladimír Vondruš <mosra@centrum.cz>
This file is part of Magnum.
Magnum is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 3
only, as published by the Free Software Foundation.
Magnum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License version 3 for more details.
*/
#include "Shader.h"
#include <iostream>
#include <fstream>
#define COMPILER_MESSAGE_MAX_LENGTH 1024
using namespace std;
namespace Magnum {
Shader::LogLevel Shader::_logLevel = Shader::Errors;
bool Shader::addFile(const std::string& filename) {
/* Open file */
ifstream file(filename.c_str());
if(!file.good()) {
file.close();
cerr << "Shader file " << filename << " cannot be opened." << endl;
return false;
}
/* Get size of shader and initialize buffer */
file.seekg(0, ios::end);
size_t size = file.tellg();
char* source = new char[size+1];
source[size] = '\0';
/* Read data, close */
file.seekg(0, ios::beg);
file.read(source, size);
file.close();
/* Add to sources and free the buffer */
addSource(source);
delete source;
return true;
}
GLuint Shader::compile() {
/* Already compiled, return */
if(_state != Initialized) return shader;
/* Array of sources */
const GLchar** _sources = new const GLchar*[sources.size()];
for(size_t i = 0; i != sources.size(); ++i)
_sources[i] = static_cast<const GLchar*>(sources[i].c_str());
/* Create shader and set its source */
shader = glCreateShader(_type);
glShaderSource(shader, 1, _sources, 0);
/* Compile shader */
glCompileShader(shader);
delete _sources;
/* Check compilation status */
GLint status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
/* Display errors or warnings */
if((status == GL_FALSE && _logLevel != None) || _logLevel == Warnings) {
/* Get message */
char message[COMPILER_MESSAGE_MAX_LENGTH];
glGetShaderInfoLog(shader, COMPILER_MESSAGE_MAX_LENGTH, 0, message);
if(status == GL_FALSE || message[0] != 0) switch(_type) {
case Vertex: cerr << "Vertex"; break;
case Geometry: cerr << "Geometry"; break;
case Fragment: cerr << "Fragment"; break;
}
/* Show error log and delete shader */
if(status == GL_FALSE) {
cerr << " shader failed to compile. Error message:" << endl
<< message << endl;
/* Or just warnings, if there are any */
} else if(message[0] != 0) {
cerr << " shader was compiled with the following warnings:" << endl
<< message << endl;
}
}
if(status == GL_FALSE) {
glDeleteShader(shader);
shader = 0;
_state = Failed;
} else _state = Compiled;
return shader;
}
}

191
src/Shader.h

@ -0,0 +1,191 @@
#ifndef Magnum_Shader_h
#define Magnum_Shader_h
/*
Copyright © 2010 Vladimír Vondruš <mosra@centrum.cz>
This file is part of Magnum.
Magnum is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 3
only, as published by the Free Software Foundation.
Magnum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License version 3 for more details.
*/
/** @file
* @brief Class Magnum::Shader
*/
#include "Magnum.h"
#include <vector>
#include <string>
namespace Magnum {
/**
* @brief Shader
*
* Allows loading and compiling the shader from file or directly from source
* string. Compiled shaders are then passed to AbstractShaderProgram subclasses
* for linking and usage.
*/
class Shader {
DISABLE_COPY(Shader)
public:
/** @brief Logging level */
enum LogLevel {
None, /**< @brief Don't display anything */
Errors, /**< @brief Display only errors */
Warnings /**< @brief Display only errors and warnings */
};
/** @brief Shader type */
enum Type {
Vertex = GL_VERTEX_SHADER, /**< @brief Vertex shader */
Geometry = GL_GEOMETRY_SHADER, /**< @brief Geometry shader */
Fragment = GL_FRAGMENT_SHADER /**< @brief Fragment shader */
};
/** @brief Shader state */
enum State {
Initialized, /**< @brief Shader sloaded */
Compiled, /**< @brief Shader is compiled */
Failed /**< @brief Compilation failed */
};
/**
* @brief Log level
*
* Log level for displaying compilation messages.
*/
inline static LogLevel logLevel() { return _logLevel; }
/** @brief Set log level */
inline static void setLogLevel(LogLevel level) { _logLevel = level; }
/**
* @brief Load shader from source
* @param type Shader type
* @param source Shader source
* @return Pointer to compiled shader
*
* Loads the shader from one source. Shorthand for
* @code
* Shader* s = new Shader(type);
* s->addData(data);
* @endcode
* Note that it is also possible to compile shader from more than one
* source.
*/
inline static Shader* fromData(Type type, const std::string& source) {
Shader* s = new Shader(type);
s->addSource(source);
return s;
}
/**
* @brief Load shader from file
* @param type Shader type
* @param filename Source filename
* @return Pointer to compiled shader or zero, if file cannot be opened.
*
* Loads the shader from from one file. Shorthand for
* @code
* Shader* s = new Shader(type);
* if(!s->addFile(filename)) delete s;
* @endcode
* Note that it is also possible to compile shader from more than one
* source.
*/
inline static Shader* fromFile(Type type, const char* filename) {
Shader* s = new Shader(type);
if(!s->addFile(filename)) {
delete s;
return 0;
}
return s;
}
/**
* @brief Constructor
*
* Creates empty shader. Sources can be added with addData() or addFile().
* @see fromData(), fromFile()
*/
inline Shader(Type type): _type(type), _state(Initialized), shader(0) {}
/**
* @brief Destructor
*
* If the shader is compiled, deletes it.
*/
inline ~Shader() { if(shader) glDeleteShader(shader); }
/**
* @brief Shader type
*
* Set in constructor.
*/
inline Type type() const { return _type; }
/**
* @brief Compilation state
*
* Changes after calling compile().
*/
inline State state() const { return _state; }
/**
* @brief Add shader source
* @param source String with shader source
*
* If the shader is not compiled already, adds given
* source to source list. Note that it is possible to compile source
* from more than one source.
* @see addFile()
*/
inline void addSource(const std::string& source) {
if(_state == Initialized) sources.push_back(source);
}
/**
* @brief Add source file
* @param filename Name of source file to read from
* @return False if reading the file fails, true otherwise.
*
* @see addSource()
*/
bool addFile(const std::string& filename);
/**
* @brief Compile shader
* @return Compiled shader or 0 if compilation failed.
*
* If the shader has any sources present and hasn't been compiled
* before, it tries to compile it. If compilation fails or no sources
* are present, returns 0. If the shader was compiled already, returns
* already existing shader.
* @see logLevel(), state()
*/
GLuint compile();
private:
static LogLevel _logLevel;
Type _type;
State _state;
std::vector<std::string> sources;
GLuint shader;
};
}
#endif
Loading…
Cancel
Save