diff options
Diffstat (limited to 'src/libjin/Graphics/Shader')
-rw-r--r-- | src/libjin/Graphics/Shader/Shader.cpp | 282 | ||||
-rw-r--r-- | src/libjin/Graphics/Shader/Shader.h | 123 | ||||
-rw-r--r-- | src/libjin/Graphics/Shader/base.shader.h | 88 | ||||
-rw-r--r-- | src/libjin/Graphics/Shader/default.shader.h | 21 | ||||
-rw-r--r-- | src/libjin/Graphics/Shader/font.shader.h | 21 | ||||
-rw-r--r-- | src/libjin/Graphics/Shader/texture.shader.h | 21 |
6 files changed, 556 insertions, 0 deletions
diff --git a/src/libjin/Graphics/Shader/Shader.cpp b/src/libjin/Graphics/Shader/Shader.cpp new file mode 100644 index 0000000..163ba92 --- /dev/null +++ b/src/libjin/Graphics/Shader/Shader.cpp @@ -0,0 +1,282 @@ +#include <regex> +#include "../../jin_configuration.h" +#if LIBJIN_MODULES_RENDER + +#include <iostream> + +#include "../../Filesystem/Buffer.h" +#include "../../utils/macros.h" +#include "Shader.h" +namespace jin +{ + namespace graphics + { + + using namespace jin::filesystem; + using namespace std; + + /** + * default_texture + * base_shader + * SHADER_FORMAT_SIZE + * formatShader + */ + #include "default.shader.h" + + /** + * https://stackoverflow.com/questions/27941496/use-sampler-without-passing-through-value + * The default value of a sampler variable is 0. From the GLSL 3.30 spec, + * section "4.3.5 Uniforms": + * + * The link time initial value is either the value of the variable's + * initializer, if present, or 0 if no initializer is present.Sampler + * types cannot have initializers. + * + * Since a value of 0 means that it's sampling from texture unit 0, it will + * work without ever setting the value as long as you bind your textures to + * unit 0. This is well defined behavior. + * + * Since texture unit 0 is also the default until you call glActiveTexture() + * with a value other than GL_TEXTURE0, it's very common to always use unit + * 0 as long as shaders do not need more than one texture.Which means that + * often times, setting the sampler uniforms is redundant for simple + * applications. + * + * I would still prefer to always set the values.If nothing else, it makes + * it clear to anybody reading your code that you really mean to sample from + * texture unit 0, and did not just forget to set the value. + */ + const int DEFAULT_TEXTURE_UNIT = 0; + + /*static*/ Shader* Shader::CurrentShader = nullptr; + + Shader* Shader::createShader(const string& program) + { + Shader* shader = nullptr; + try + { + shader = new Shader(program); + } + catch(...) + { + return nullptr; + } + return shader; + } + + Shader::Shader(const string& program) + : mCurrentTextureUnit(DEFAULT_TEXTURE_UNIT) + { + if (!compile(program)) + throw 0; + } + + Shader::~Shader() + { + if (CurrentShader == this) + unuse(); + } + + bool Shader::compile(const string& program) + { + /* parse shader source, need some optimizations */ + int loc_VERTEX_SHADER = program.find("#VERTEX_SHADER"); + int loc_END_VERTEX_SHADER = program.find("#END_VERTEX_SHADER"); + int loc_FRAGMENT_SHADER = program.find("#FRAGMENT_SHADER"); + int loc_END_FRAGMENT_SHADER = program.find("#END_FRAGMENT_SHADER"); + if (loc_VERTEX_SHADER == string::npos + || loc_END_VERTEX_SHADER == string::npos + || loc_FRAGMENT_SHADER == string::npos + || loc_END_FRAGMENT_SHADER == string::npos + ) + return false; + /* load vertex and fragment shader source into buffers */ + int start = loc_VERTEX_SHADER + strlen("#VERTEX_SHADER"); + string vertex_shader = program.substr(start, loc_END_VERTEX_SHADER - start); + Buffer vbuffer = Buffer(vertex_shader.length() + BASE_VERTEX_SHADER_SIZE); + formatVertexShader((char*)vbuffer.data, vertex_shader.c_str()); + start = loc_FRAGMENT_SHADER + strlen("#FRAGMENT_SHADER"); + string fragment_shader = program.substr(start, loc_END_FRAGMENT_SHADER - start); + Buffer fbuffer = Buffer(fragment_shader.length() + BASE_FRAGMENT_SHADER_SIZE); + formatFragmentShader((char*)fbuffer.data, fragment_shader.c_str()); + /* compile */ + GLint success; + GLuint vshader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vshader, 1, (const GLchar**)&vbuffer.data, NULL); + glCompileShader(vshader); + glGetShaderiv(vshader, GL_COMPILE_STATUS, &success); + if (success == GL_FALSE) + return false; + GLuint fshader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fshader, 1, (const GLchar**)&fbuffer.data, NULL); + glCompileShader(fshader); + glGetShaderiv(fshader, GL_COMPILE_STATUS, &success); + if (success == GL_FALSE) + return false; + mPID = glCreateProgram(); + glAttachShader(mPID, vshader); + glAttachShader(mPID, fshader); + glLinkProgram(mPID); + glGetProgramiv(mPID, GL_LINK_STATUS, &success); + if (success == GL_FALSE) + throw false; + } + + static inline GLint getMaxTextureUnits() + { + GLint maxTextureUnits = 0; + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits); + return maxTextureUnits; + } + + void Shader::use() + { + glUseProgram(mPID); + CurrentShader = this; + sendInt(SHADER_MAIN_TEXTURE, DEFAULT_TEXTURE_UNIT); + } + + /*static*/ void Shader::unuse() + { + glUseProgram(0); + CurrentShader = nullptr; + } + + GLint Shader::claimTextureUnit(const std::string& name) + { + std::map<std::string, GLint>::iterator unit = mTextureUnits.find(name); + if (unit != mTextureUnits.end()) + return unit->second; + static GLint MAX_TEXTURE_UNITS = getMaxTextureUnits(); + if (++mCurrentTextureUnit >= MAX_TEXTURE_UNITS) + return 0; + mTextureUnits[name] = mCurrentTextureUnit; + return mCurrentTextureUnit; + } + + #define checkJSL() \ + if (CurrentShader != this) \ + return + + void Shader::sendInt(const char* name, int value) + { + checkJSL(); + int loc = glGetUniformLocation(mPID, name); + glUniform1i(loc, value); + } + + void Shader::sendFloat(const char* variable, float number) + { + checkJSL(); + int loc = glGetUniformLocation(mPID, variable); + glUniform1f(loc, number); + } + + /** + * https://www.douban.com/note/627332677/ + * struct TextureUnit + * { + * GLuint targetTexture1D; + * GLuint targetTexture2D; + * GLuint targetTexture3D; + * GLuint targetTextureCube; + * ... + * }; + * + * TextureUnit mTextureUnits[GL_MAX_TEXTURE_IMAGE_UNITS] + * GLuint mCurrentTextureUnit = 0; + */ + void Shader::sendTexture(const char* variable, const Texture* tex) + { + checkJSL(); + GLint location = glGetUniformLocation(mPID, variable); + if (location == -1) + return; + GLint unit = claimTextureUnit(variable); + if (unit == 0) + { + // TODO: 쳣 + return; + } + gl.activeTexUnit(unit); + glUniform1i(location, unit); + gl.bindTexture(tex->getTexture()); + gl.activeTexUnit(0); + } + + void Shader::sendCanvas(const char* variable, const Canvas* canvas) + { + checkJSL(); + GLint location = glGetUniformLocation(mPID, variable); + if (location == -1) + return; + GLint unit = claimTextureUnit(variable); + if (unit == 0) + { + // TODO: 쳣 + return; + } + glUniform1i(location, unit); + glActiveTexture(GL_TEXTURE0 + unit); + gl.bindTexture(canvas->getTexture()); + + glActiveTexture(GL_TEXTURE0); + } + + void Shader::sendVec2(const char* name, float x, float y) + { + checkJSL(); + int loc = glGetUniformLocation(mPID, name); + glUniform2f(loc, x, y); + } + + void Shader::sendVec3(const char* name, float x, float y, float z) + { + checkJSL(); + int loc = glGetUniformLocation(mPID, name); + glUniform3f(loc, x, y, z); + } + + void Shader::sendVec4(const char* name, float x, float y, float z, float w) + { + checkJSL(); + int loc = glGetUniformLocation(mPID, name); + glUniform4f(loc, x, y, z, w); + } + + void Shader::sendColor(const char* name, const Color* col) + { + checkJSL(); + int loc = glGetUniformLocation(mPID, name); + glUniform4f(loc, + col->r / 255.f, + col->g / 255.f, + col->b / 255.f, + col->a / 255.f + ); + } + + void Shader::sendMatrix4(const char* name, const math::Matrix* mat4) + { + int loc = glGetUniformLocation(mPID, name); + glUniformMatrix4fv(loc, 1, GL_FALSE, mat4->getElements()); + } + + void Shader::bindVertexPointer(int n, GLenum type, GLsizei stride, const GLvoid * pointers) + { + GLint loc = glGetAttribLocation(mPID, SHADER_VERTEX_COORDS); + glEnableVertexAttribArray(0); + glVertexAttribPointer(loc, n, type, GL_FALSE, stride, pointers); + } + + void Shader::bindUVPointer(int n, GLenum type, GLsizei stride, const GLvoid * pointers) + { + GLint loc = glGetAttribLocation(mPID, SHADER_TEXTURE_COORDS); + glEnableVertexAttribArray(1); + glVertexAttribPointer(loc, n, type, GL_FALSE, stride, pointers); + } + + } // namespace graphics +} // namespace jin + +#endif // LIBJIN_MODULES_RENDER
\ No newline at end of file diff --git a/src/libjin/Graphics/Shader/Shader.h b/src/libjin/Graphics/Shader/Shader.h new file mode 100644 index 0000000..b9f83e7 --- /dev/null +++ b/src/libjin/Graphics/Shader/Shader.h @@ -0,0 +1,123 @@ +#ifndef __LIBJIN_SHADER_H +#define __LIBJIN_SHADER_H +#include "../../jin_configuration.h" +#if LIBJIN_MODULES_RENDER + +#include <string> +#include <map> +#include "../../3rdparty/GLee/GLee.h" +#include "../color.h" +#include "../texture.h" +#include "../canvas.h" +#include "base.shader.h" + +namespace jin +{ + namespace graphics + { + /// + /// @details Built in shader program. + /// + class Shader + { + public: + /// + /// @brief Create shader program from source code. + /// + /// @param source The shader source code. + /// + static Shader* createShader(const std::string& source); + + /// + /// @brief Get current shader. + /// + static inline Shader* getCurrentShader() { return CurrentShader; } + + /// + /// @brief Unuse current shader + /// + static void unuse(); + + /// + /// + /// + virtual ~Shader(); + + /// + /// + /// + void use(); + + /// + /// + /// + void sendFloat(const char* name, float number); + + /// + /// + /// + void sendTexture(const char* name, const Texture* image); + + /// + /// + /// + void sendInt(const char* name, int value); + + /// + /// + /// + void sendVec2(const char* name, float x, float y); + + /// + /// + /// + void sendVec3(const char* name, float x, float y, float z); + + /// + /// + /// + void sendVec4(const char* name, float x, float y, float z, float w); + + /// + /// + /// + void sendCanvas(const char* name, const Canvas* canvas); + + /// + /// + /// + void sendColor(const char* name, const Color* col); + + /// + /// + /// + void sendMatrix4(const char* name, const math::Matrix* mat4); + + /// + /// + /// + void bindVertexPointer(int n, GLenum type, GLsizei stride, const GLvoid * pointers); + + /// + /// + /// + void bindUVPointer(int n, GLenum type, GLsizei stride, const GLvoid * pointers); + + protected: + static Shader* CurrentShader; + + GLint claimTextureUnit(const std::string& name); + Shader(const std::string& program); + bool compile(const std::string& program); + + GLuint mPID; + GLint mCurrentTextureUnit; + std::map<std::string, GLint> mTextureUnits; + + }; + + } // namespace graphics +} // namespace jin + +#endif // LIBJIN_MODULES_RENDER +#endif // __LIBJIN_SHADER_H
\ No newline at end of file diff --git a/src/libjin/Graphics/Shader/base.shader.h b/src/libjin/Graphics/Shader/base.shader.h new file mode 100644 index 0000000..45b63cd --- /dev/null +++ b/src/libjin/Graphics/Shader/base.shader.h @@ -0,0 +1,88 @@ +#ifndef __LIBJIN_BASE_SHADER_H +#define __LIBJIN_BASE_SHADER_H + +static const char* base_shared = R"( +#define Number float +#define Texture sampler2D +#define Canvas sampler2D +#define Color vec4 +#define Vec2 vec2 +#define Vec3 vec3 +#define Vec4 vec4 + +#define texel texture2D + +struct Vertex +{ + vec2 xy; + vec2 uv; +}; + +)"; + +static const int BASE_SHARED_SIZE = strlen(base_shared); + +static const char* base_vertex = R"( +#version 130 core + +%s + +uniform mat4 jin_ProjectionMatrix; +uniform mat4 jin_ModelMatrix; + +in vec2 jin_VertexCoords; +in vec2 jin_TextureCoords; + +out vec4 jin_Color; +out vec2 jin_XY; +out vec2 jin_UV; + +%s + +void main() +{ + vec4 v = jin_ModelMatrix * vec4(jin_VertexCoords, 0, 1.0); + Vertex _v = vert(Vertex(v.xy, jin_TextureCoords)); + gl_Position = jin_ProjectionMatrix * vec4(_v.xy, 0, 1.0f); + jin_Color = gl_Color; + jin_XY = _v.xy; + jin_UV = _v.uv; +} +)"; + +static const int BASE_VERTEX_SHADER_SIZE = strlen(base_vertex) + BASE_SHARED_SIZE; + +#define formatVertexShader(buf, program) sprintf(buf,base_vertex, base_shared, program) + +static const char* base_fragment = R"( +#version 130 core + +%s + +uniform Texture jin_MainTexture; + +in vec4 jin_Color; +in vec2 jin_XY; +in vec2 jin_UV; + +out vec4 jin_OutColor; + +%s + +void main() +{ + jin_OutColor = frag(jin_Color, jin_MainTexture, Vertex(jin_XY, jin_UV)); +} +)"; + +static const int BASE_FRAGMENT_SHADER_SIZE = strlen(base_fragment) + BASE_SHARED_SIZE; + +#define formatFragmentShader(buf, program) sprintf(buf, base_fragment, base_shared, program) + +static const char* SHADER_PROJECTION_MATRIX = "jin_ProjectionMatrix"; +static const char* SHADER_MODEL_MATRIX = "jin_ModelMatrix"; +static const char* SHADER_MAIN_TEXTURE = "jin_MainTexture"; +static const char* SHADER_VERTEX_COORDS = "jin_VertexCoords"; +static const char* SHADER_TEXTURE_COORDS = "jin_TextureCoords"; + +#endif
\ No newline at end of file diff --git a/src/libjin/Graphics/Shader/default.shader.h b/src/libjin/Graphics/Shader/default.shader.h new file mode 100644 index 0000000..f0175d7 --- /dev/null +++ b/src/libjin/Graphics/Shader/default.shader.h @@ -0,0 +1,21 @@ +// Ĭshader +static const char* default_shader = R"( + +#VERTEX_SHADER + +Vertex vert(Vertex v) +{ + return v; +} + +#END_VERTEX_SHADER + +#FRAGMENT_SHADER + +Color frag(Color col, Texture tex, Vertex v) +{ + return col; +} + +#END_FRAGMENT_SHADER +)";
\ No newline at end of file diff --git a/src/libjin/Graphics/Shader/font.shader.h b/src/libjin/Graphics/Shader/font.shader.h new file mode 100644 index 0000000..e04c225 --- /dev/null +++ b/src/libjin/Graphics/Shader/font.shader.h @@ -0,0 +1,21 @@ +// shader +static const char* font_shader = R"( + +#VERTEX_SHADER + +Vertex vert(Vertex v) +{ + return v; +} + +#END_VERTEX_SHADER + +#FRAGMENT_SHADER + +Color frag(Color col, Texture tex, Vertex v) +{ + return Color(col.rgb, texel(tex, v.uv).a); +} + +#END_FRAGMENT_SHADER +)";
\ No newline at end of file diff --git a/src/libjin/Graphics/Shader/texture.shader.h b/src/libjin/Graphics/Shader/texture.shader.h new file mode 100644 index 0000000..d1fc86f --- /dev/null +++ b/src/libjin/Graphics/Shader/texture.shader.h @@ -0,0 +1,21 @@ +// ͼshader +static const char* texture_shader = R"( + +#VERTEX_SHADER + +Vertex vert(Vertex v) +{ + return v; +} + +#END_VERTEX_SHADER + +#FRAGMENT_SHADER + +Color frag(Color col, Texture tex, Vertex v) +{ + return col * texel(tex, v.uv); +} + +#END_FRAGMENT_SHADER +)";
\ No newline at end of file |