basic shaders
parent
68fd994119
commit
c4097f1c9b
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* <Short Description>
|
||||||
|
* Copyright (C) 2023 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLT_WITH_GRAPHICS_SHADER_H
|
||||||
|
#define BLT_WITH_GRAPHICS_SHADER_H
|
||||||
|
|
||||||
|
#include <blt/gfx/gl_includes.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string>
|
||||||
|
#include <blt/math/math.h>
|
||||||
|
|
||||||
|
namespace blt::gfx
|
||||||
|
{
|
||||||
|
class shader_base {
|
||||||
|
protected:
|
||||||
|
struct IntDefaultedToMinusOne {
|
||||||
|
GLint i = -1;
|
||||||
|
};
|
||||||
|
std::unordered_map<std::string, IntDefaultedToMinusOne> uniformVars;
|
||||||
|
GLuint programID = 0;
|
||||||
|
|
||||||
|
inline GLint getUniformLocation(const std::string &name) {
|
||||||
|
if (uniformVars[name].i != -1)
|
||||||
|
return uniformVars[name].i;
|
||||||
|
int loc = glGetUniformLocation(programID, name.c_str());
|
||||||
|
uniformVars[name].i = loc;
|
||||||
|
return loc;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
inline void bind() const {
|
||||||
|
glUseProgram(programID);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setBool(const std::string &name, bool value) {
|
||||||
|
glUniform1i(getUniformLocation(name), (int) value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setInt(const std::string &name, int value) {
|
||||||
|
glUniform1i(getUniformLocation(name), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setFloat(const std::string &name, float value) {
|
||||||
|
glUniform1f(getUniformLocation(name), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setMatrix(const std::string &name, blt::mat4x4 &matrix) {
|
||||||
|
glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, matrix.ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setVec3(const std::string &name, const blt::vec3 &vec) {
|
||||||
|
glUniform3f(getUniformLocation(name), vec.x(), vec.y(), vec.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setVec4(const std::string &name, const blt::vec4 &vec) {
|
||||||
|
glUniform4f(getUniformLocation(name), vec.x(), vec.y(), vec.z(), vec.w());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setVec2(const std::string &name, float x, float y) {
|
||||||
|
glUniform2f(getUniformLocation(name), x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setVec3(const std::string &name, float x, float y, float z) {
|
||||||
|
glUniform3f(getUniformLocation(name), x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setVec4(const std::string &name, float x, float y, float z, float w) {
|
||||||
|
glUniform4f(getUniformLocation(name), x, y, z, w);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a basic computer shader class, contains the functions and resources required to use compute shaders!
|
||||||
|
*/
|
||||||
|
class compute_shader : public shader_base {
|
||||||
|
private:
|
||||||
|
GLuint shaderID = 0;
|
||||||
|
public:
|
||||||
|
explicit compute_shader(const std::string& shader_source, bool loadAsString = true);
|
||||||
|
|
||||||
|
inline void execute(int x, int y, int z) const {
|
||||||
|
bind();
|
||||||
|
glDispatchCompute(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
~compute_shader();
|
||||||
|
};
|
||||||
|
|
||||||
|
class shader : public shader_base {
|
||||||
|
private:
|
||||||
|
GLuint vertexShaderID = 0;
|
||||||
|
GLuint fragmentShaderID = 0;
|
||||||
|
// while these will remain unused. (Webgl2 apparently doesn't support them despite being based on GL4.3? that's a TODO!)
|
||||||
|
GLuint geometryShaderID = 0;
|
||||||
|
// this would be very useful however it is highly unlikely webgl will support it
|
||||||
|
// im leaving some of this stuff in here because I might expand the native application to use some of it.
|
||||||
|
// im trying to keep the web and native versions the same though
|
||||||
|
GLuint tessellationShaderID = 0;
|
||||||
|
|
||||||
|
static unsigned int createShader(const std::string& source, int type);
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates a shader
|
||||||
|
* @param vertex vertex shader source or file
|
||||||
|
* @param fragment fragment shader source or file
|
||||||
|
* @param geometry geometry shader source or file (optional)
|
||||||
|
* @param load_as_string load the shader as a string (true) or use the string to load the shader as a file (false)
|
||||||
|
*/
|
||||||
|
shader(const std::string &vertex, const std::string &fragment, const std::string &geometry = "", bool load_as_string = true);
|
||||||
|
|
||||||
|
shader(shader&& move) noexcept;
|
||||||
|
|
||||||
|
// used to set the location of VAOs to the in variables in opengl shaders.
|
||||||
|
void bindAttribute(int attribute, const std::string &name) const;
|
||||||
|
|
||||||
|
// used to set location of shared UBOs like the perspective and view matrix
|
||||||
|
void setUniformBlockLocation(const std::string &name, int location) const;
|
||||||
|
|
||||||
|
~shader();
|
||||||
|
|
||||||
|
static void updateProjectionMatrix(const blt::mat4x4& projectionMatrix);
|
||||||
|
static void updateOrthographicMatrix(const blt::mat4x4& orthoMatrix);
|
||||||
|
static void updateViewMatrix(const blt::mat4x4& viewMatrix);
|
||||||
|
// returns the perspective view matrix which is calculated per frame. (This is for optimization)
|
||||||
|
static const blt::mat4x4& getPVM();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //BLT_WITH_GRAPHICS_SHADER_H
|
|
@ -0,0 +1,190 @@
|
||||||
|
/*
|
||||||
|
* <Short Description>
|
||||||
|
* Copyright (C) 2023 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <blt/gfx/shader.h>
|
||||||
|
#include <blt/std/loader.h>
|
||||||
|
#include <blt/std/string.h>
|
||||||
|
#include <blt/std/memory.h>
|
||||||
|
|
||||||
|
namespace blt::gfx
|
||||||
|
{
|
||||||
|
static std::string removeEmptyFirstLines(const std::string& string){
|
||||||
|
auto lines = blt::string::split(string, "\n");
|
||||||
|
std::string new_source_string;
|
||||||
|
for (const auto& line : lines) {
|
||||||
|
if (!line.empty() && !blt::string::contains(line, "\"")) {
|
||||||
|
new_source_string += line;
|
||||||
|
new_source_string += "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new_source_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int shader::createShader(const std::string& source, int type) {
|
||||||
|
const char* shader_code = source.c_str();
|
||||||
|
// creates a Shader
|
||||||
|
unsigned int shaderID = glCreateShader(type);
|
||||||
|
// loads the shader code for later complication and uploading into the graphics card
|
||||||
|
// TODO: defines can be added here by sending them as additional strings. No need to edit the source string
|
||||||
|
glShaderSource(shaderID, 1, &shader_code, nullptr);
|
||||||
|
// Compile it
|
||||||
|
glCompileShader(shaderID);
|
||||||
|
|
||||||
|
// make sure there are no errors in the compilation. If there is then print out information pertaining to the error.
|
||||||
|
// the actual log is highly dependent on the platform this is being run from, so we cannot make any assumptions about the issue.
|
||||||
|
// the TODO: maybe find a way of lexing the output to give suggestions about fixing the error? default error messages can be unhelpful at times.
|
||||||
|
GLint success;
|
||||||
|
glGetShaderiv(shaderID, GL_COMPILE_STATUS, &success);
|
||||||
|
if (!success) {
|
||||||
|
int log_length = 0;
|
||||||
|
glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &log_length);
|
||||||
|
|
||||||
|
// scoped buffers will delete their memory when they go out of scope. A benefit of using BLT
|
||||||
|
blt::scoped_buffer<GLchar> infoLog{static_cast<unsigned long>(log_length + 1)};
|
||||||
|
|
||||||
|
glGetShaderInfoLog(shaderID, log_length + 1, nullptr, infoLog.data());
|
||||||
|
auto shader_type_str = (type == GL_VERTEX_SHADER ? "Vertex Shader" : type == GL_FRAGMENT_SHADER ? "Fragment Shader" : "Other Shader");
|
||||||
|
BLT_ERROR("--- --- --- --- --- --- --- --- ---");
|
||||||
|
BLT_ERROR("Unable to compile shader of type %s\nShader source:", shader_type_str);
|
||||||
|
BLT_ERROR(source);
|
||||||
|
BLT_ERROR("I have an log of %d length", log_length);
|
||||||
|
BLT_ERROR(infoLog.data());
|
||||||
|
BLT_ERROR("--- --- --- --- --- --- --- --- ---");
|
||||||
|
}
|
||||||
|
return shaderID;
|
||||||
|
}
|
||||||
|
|
||||||
|
shader::shader(const std::string& vertex, const std::string& fragment, const std::string& geometry, bool load_as_string) {
|
||||||
|
// load shader sources
|
||||||
|
bool load_geometry = !geometry.empty();
|
||||||
|
std::string vertex_source = vertex;
|
||||||
|
std::string fragment_source = fragment;
|
||||||
|
std::string geometry_source = geometry;
|
||||||
|
if (!load_as_string){
|
||||||
|
// BLT provides a recursive file loader for glsl shaders. It's pretty much just a recursive function looking for include statements.
|
||||||
|
vertex_source = blt::fs::loadShaderFile(vertex);
|
||||||
|
fragment_source = blt::fs::loadShaderFile(fragment);
|
||||||
|
if (load_geometry)
|
||||||
|
geometry_source = blt::fs::loadShaderFile(geometry);
|
||||||
|
} else {
|
||||||
|
vertex_source = removeEmptyFirstLines(vertex_source);
|
||||||
|
fragment_source = removeEmptyFirstLines(fragment_source);
|
||||||
|
geometry_source = removeEmptyFirstLines(geometry_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the shaders
|
||||||
|
vertexShaderID = createShader(vertex_source, GL_VERTEX_SHADER);
|
||||||
|
fragmentShaderID = createShader(fragment_source, GL_FRAGMENT_SHADER);
|
||||||
|
if (load_geometry)
|
||||||
|
geometryShaderID = createShader(geometry_source, GL_GEOMETRY_SHADER);
|
||||||
|
|
||||||
|
// bind them to a program
|
||||||
|
programID = glCreateProgram();
|
||||||
|
// attach the loaded shaders to the Shader program
|
||||||
|
glAttachShader(programID, vertexShaderID);
|
||||||
|
glAttachShader(programID, fragmentShaderID);
|
||||||
|
if (load_geometry)
|
||||||
|
glAttachShader(programID, geometryShaderID);
|
||||||
|
// link and make sure that our program is valid.
|
||||||
|
glLinkProgram(programID);
|
||||||
|
|
||||||
|
GLint success;
|
||||||
|
glGetProgramiv(programID, GL_LINK_STATUS, &success);
|
||||||
|
if (!success) {
|
||||||
|
int log_length = 0;
|
||||||
|
glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &log_length);
|
||||||
|
|
||||||
|
// scoped buffers will delete their memory when they go out of scope.
|
||||||
|
blt::scoped_buffer<GLchar> infoLog{static_cast<unsigned long>(log_length + 1)};
|
||||||
|
|
||||||
|
glGetProgramInfoLog(programID, log_length + 1, nullptr, infoLog.data());
|
||||||
|
BLT_ERROR("--- --- --- --- --- --- --- --- ---");
|
||||||
|
BLT_ERROR("Unable to link program of ID: %d", programID);
|
||||||
|
BLT_ERROR(vertex_source);
|
||||||
|
BLT_ERROR(fragment_source);
|
||||||
|
BLT_ERROR(geometry_source);
|
||||||
|
BLT_ERROR("I have an log of %d length", log_length);
|
||||||
|
BLT_ERROR(infoLog.data());
|
||||||
|
BLT_ERROR("--- --- --- --- --- --- --- --- ---");
|
||||||
|
}
|
||||||
|
|
||||||
|
glValidateProgram(programID);
|
||||||
|
bind();
|
||||||
|
setUniformBlockLocation("StandardMatrices", 0);
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void shader::bindAttribute(int attribute, const std::string &name) const {
|
||||||
|
bind();
|
||||||
|
glBindAttribLocation(programID, attribute, name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void shader::setUniformBlockLocation(const std::string &name, int location) const {
|
||||||
|
bind();
|
||||||
|
glUniformBlockBinding(programID, glGetUniformBlockIndex(programID, name.c_str()), location);
|
||||||
|
}
|
||||||
|
|
||||||
|
shader::~shader() {
|
||||||
|
glUseProgram(0);
|
||||||
|
// shader was moved
|
||||||
|
if (programID <= 0)
|
||||||
|
return;
|
||||||
|
// remove all the shaders from the program
|
||||||
|
glDetachShader(programID, vertexShaderID);
|
||||||
|
if (geometryShaderID)
|
||||||
|
glDetachShader(programID, geometryShaderID);
|
||||||
|
if (tessellationShaderID)
|
||||||
|
glDetachShader(programID, tessellationShaderID);
|
||||||
|
glDetachShader(programID, fragmentShaderID);
|
||||||
|
|
||||||
|
// delete the shaders
|
||||||
|
glDeleteShader(vertexShaderID);
|
||||||
|
if (geometryShaderID)
|
||||||
|
glDeleteShader(geometryShaderID);
|
||||||
|
if (tessellationShaderID)
|
||||||
|
glDeleteShader(tessellationShaderID);
|
||||||
|
glDeleteShader(fragmentShaderID);
|
||||||
|
|
||||||
|
// delete the Shader program
|
||||||
|
glDeleteProgram(programID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void shader::updateProjectionMatrix(const blt::mat4x4& projectionMatrix) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void shader::updateViewMatrix(const blt::mat4x4& viewMatrix) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void shader::updateOrthographicMatrix(const blt::mat4x4& orthoMatrix) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
shader::shader(shader&& move) noexcept {
|
||||||
|
// the move constructor doesn't need to construct a new shader but it does need to ensure all old variables are moved over
|
||||||
|
programID = move.programID;
|
||||||
|
vertexShaderID = move.vertexShaderID;
|
||||||
|
fragmentShaderID = move.fragmentShaderID;
|
||||||
|
geometryShaderID = move.geometryShaderID;
|
||||||
|
tessellationShaderID = move.tessellationShaderID;
|
||||||
|
for (const auto& pair : move.uniformVars)
|
||||||
|
uniformVars.insert(pair);
|
||||||
|
// by setting the program ID to -1 we tell the shader it has been moved.
|
||||||
|
move.programID = -1;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue