aboutsummaryrefslogtreecommitdiff
path: root/src/libjin/graphics/shaders/shader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libjin/graphics/shaders/shader.cpp')
-rw-r--r--src/libjin/graphics/shaders/shader.cpp321
1 files changed, 321 insertions, 0 deletions
diff --git a/src/libjin/graphics/shaders/shader.cpp b/src/libjin/graphics/shaders/shader.cpp
new file mode 100644
index 0000000..c7f6f11
--- /dev/null
+++ b/src/libjin/graphics/shaders/shader.cpp
@@ -0,0 +1,321 @@
+#include "../../core/configuration.h"
+#if defined(jin_graphics) && (jin_graphics & jin_graphics_shader)
+
+#include <iostream>
+
+#include "../../time/timer.h"
+#include "../../filesystem/buffer.h"
+#include "../../utils/log.h"
+#include "../../utils/macros.h"
+
+#include "../gl.h"
+#include "../window.h"
+
+#include "jsl_compiler.h"
+#include "shader.h"
+
+using namespace std;
+using namespace JinEngine::Filesystem;
+using namespace JinEngine::Time;
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Shaders
+ {
+
+ //
+ // default_texture
+ // base_shader
+ // SHADER_FORMAT_SIZE
+ // formatShader
+ //
+ #include "built-in/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 MAIN_TEXTURE_UNIT = 0;
+
+ static GLint textureUnit = 0;
+
+ GLint Shader::mAttributeIndex = 0;
+
+ Shader::Shader(const string& program)
+ {
+ if (!compile(program))
+ {
+ jin_log_error("Compile jsl shader failed.");
+ throw Exception("Compile jsl shader failed");
+ }
+ }
+
+ Shader::~Shader()
+ {
+ if (gl.getShader() == this)
+ gl.unuseShader();
+ // Delete shader program.
+ glDeleteShader(mPID);
+ }
+
+ Shader& Shader::begin()
+ {
+
+ textureUnit = MAIN_TEXTURE_UNIT;
+
+ // Send uniforms.
+ sendInt(SHADER_MAIN_TEXTURE, MAIN_TEXTURE_UNIT);
+ sendVec2(SHADER_TIME, Time::getSecond(), Time::getDeltaTime());
+ Canvas* rt = gl.getCanvas();
+ if (rt == OpenGL::SCREEN)
+ {
+ sendVec2(SHADER_RENDERTARGET_SIZE, Window::get()->getW(), Window::get()->getH());
+ }
+ else if(rt != nullptr)
+ {
+ sendVec2(SHADER_RENDERTARGET_SIZE, rt->getWidth(), rt->getHeight());
+ }
+
+ gl.activeTextureUnit(MAIN_TEXTURE_UNIT);
+
+ return *this;
+ }
+
+ void Shader::end()
+ {
+ // Reset attribute index.
+ for (; mAttributeIndex > 0; --mAttributeIndex)
+ glDisableVertexAttribArray(mAttributeIndex);
+ }
+
+ bool Shader::compile(const string& program)
+ {
+ string vertex_shader, fragment_shader;
+ // Compile JSL shader source into GLSL shader source.
+ JSLCompiler* compiler = JSLCompiler::get();
+ if (!compiler->compile(program, &vertex_shader, &fragment_shader))
+ {
+ return false;
+ }
+
+ #define glsl(SHADER_MODE, SHADER, SRC) \
+ do{ \
+ const GLchar* src = SRC.c_str(); \
+ glShaderSource(SHADER, 1, &src, NULL); \
+ glCompileShader(SHADER); \
+ GLint success; \
+ glGetShaderiv(SHADER, GL_COMPILE_STATUS, &success); \
+ if (success == GL_FALSE) \
+ return false; \
+ }while(0)
+
+ // Compile vertex shader.
+ GLuint vid = glCreateShader(GL_VERTEX_SHADER);
+ glsl(GL_VERTEX_SHADER, vid, vertex_shader);
+ // Compile fragment shader.
+ GLuint fid = glCreateShader(GL_FRAGMENT_SHADER);
+ glsl(GL_FRAGMENT_SHADER, fid, fragment_shader);
+
+ #undef glsl
+ // Create OpenGL shader program.
+ mPID = glCreateProgram();
+ glAttachShader(mPID, vid);
+ glAttachShader(mPID, fid);
+ glLinkProgram(mPID);
+ GLint success;
+ glGetProgramiv(mPID, GL_LINK_STATUS, &success);
+ if (success == GL_FALSE)
+ return false;
+ }
+
+ static inline GLint getMaxTextureUnits()
+ {
+ GLint maxTextureUnits = 0;
+ glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
+ return maxTextureUnits;
+ }
+
+ GLint Shader::claimTextureUnit(/*const std::string& name*/)
+ {
+ return textureUnit++;
+ }
+
+ GLint Shader::getUniformLocation(const char* uniform)
+ {
+ map<std::string, GLint>::iterator it = mUniformsLocation.find(uniform);
+ if (it != mUniformsLocation.end())
+ return it->second;
+ GLint loc = glGetUniformLocation(mPID, uniform);
+ mUniformsLocation.insert(pair<std::string, GLint>(uniform, loc));
+ return loc;
+ }
+
+ #define check_jsl() \
+ if (gl.getShader() != this) \
+ return *this
+
+ Shader & Shader::sendInt(const char* name, int value)
+ {
+ check_jsl();
+ int loc = getUniformLocation(name);
+ glUniform1i(loc, value);
+ return *this;
+ }
+
+ Shader& Shader::sendFloat(const char* variable, float number)
+ {
+ check_jsl();
+ int loc = getUniformLocation(variable);
+ glUniform1f(loc, number);
+ return *this;
+ }
+
+ //
+ // 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;
+ //
+ Shader& Shader::sendTexture(const char* variable, const Texture* tex)
+ {
+ check_jsl();
+ GLint location = getUniformLocation(variable);
+ if (location == -1)
+ return *this;
+ GLint unit = claimTextureUnit(/*variable*/);
+ if (unit == 0)
+ {
+ // TODO: 쳣󶨵
+ return *this;
+ }
+ gl.activeTextureUnit(unit);
+ glUniform1i(location, unit);
+ gl.bindTexture2D(tex->getGLTexture());
+ gl.activeTextureUnit(MAIN_TEXTURE_UNIT);
+ return *this;
+ }
+
+ Shader& Shader::sendCanvas(const char* variable, const Canvas* canvas)
+ {
+ check_jsl();
+ GLint location = getUniformLocation(variable);
+ if (location == -1)
+ return *this;
+ GLint unit = claimTextureUnit(/*variable*/);
+ if (unit == 0)
+ {
+ // TODO: 쳣󶨵
+ return *this;
+ }
+ glUniform1i(location, unit);
+ glActiveTexture(GL_TEXTURE0 + unit);
+ gl.bindTexture2D(canvas->getGLTexture());
+
+ glActiveTexture(GL_TEXTURE0);
+ return *this;
+ }
+
+ Shader& Shader::sendVec2(const char* name, float x, float y)
+ {
+ check_jsl();
+ int loc = getUniformLocation(name);
+ glUniform2f(loc, x, y);
+ return *this;
+ }
+
+ Shader& Shader::sendVec3(const char* name, float x, float y, float z)
+ {
+ check_jsl();
+ int loc = getUniformLocation(name);
+ glUniform3f(loc, x, y, z);
+ return *this;
+ }
+
+ Shader& Shader::sendVec4(const char* name, float x, float y, float z, float w)
+ {
+ check_jsl();
+ int loc = getUniformLocation(name);
+ glUniform4f(loc, x, y, z, w);
+ return *this;
+ }
+
+ Shader& Shader::sendColor(const char* name, const Color* col)
+ {
+ check_jsl();
+ int loc = getUniformLocation(name);
+ glUniform4f(loc,
+ col->r / 255.f,
+ col->g / 255.f,
+ col->b / 255.f,
+ col->a / 255.f
+ );
+ return *this;
+ }
+
+ Shader& Shader::sendMatrix4(const char* name, const Math::Matrix* mat4)
+ {
+ int loc = getUniformLocation(name);
+ glUniformMatrix4fv(loc, 1, GL_FALSE, mat4->getElements());
+ return *this;
+ }
+
+ Shader& Shader::uploadVertices(int n, GLenum type, GLsizei stride, const GLvoid * pointers, GLboolean normalized)
+ {
+ uploadAttribute(SHADER_VERTEX_COORDS, n, type, stride, pointers, normalized);
+ return *this;
+ }
+
+ Shader& Shader::uploadUV(int n, GLenum type, GLsizei stride, const GLvoid * pointers, GLboolean normalized)
+ {
+ uploadAttribute(SHADER_TEXTURE_COORDS, n, type, stride, pointers, normalized);
+ return *this;
+ }
+
+ Shader& Shader::uploadColor(int n, GLenum type, GLsizei stride, const GLvoid * pointers, GLboolean normalized)
+ {
+ uploadAttribute(SHADER_VERTEX_COLOR, n, type, stride, pointers, normalized);
+ return *this;
+ }
+
+ Shader& Shader::uploadAttribute(const String& name, int n, GLenum type, GLsizei stride, const GLvoid * pointers, GLboolean normalized)
+ {
+ GLint loc = glGetAttribLocation(mPID, name);
+ glEnableVertexAttribArray(mAttributeIndex++);
+ glVertexAttribPointer(loc, n, type, normalized, stride, pointers);
+ return *this;
+ }
+
+ } // namespace Shaders
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // (jin_graphics) && (jin_graphics & jin_graphics_shader) \ No newline at end of file