aboutsummaryrefslogtreecommitdiff
path: root/src/libjin/Graphics/Shader
diff options
context:
space:
mode:
Diffstat (limited to 'src/libjin/Graphics/Shader')
-rw-r--r--src/libjin/Graphics/Shader/Shader.cpp282
-rw-r--r--src/libjin/Graphics/Shader/Shader.h123
-rw-r--r--src/libjin/Graphics/Shader/base.shader.h88
-rw-r--r--src/libjin/Graphics/Shader/default.shader.h21
-rw-r--r--src/libjin/Graphics/Shader/font.shader.h21
-rw-r--r--src/libjin/Graphics/Shader/texture.shader.h21
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