aboutsummaryrefslogtreecommitdiff
path: root/src/libjin/Graphics/Shader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libjin/Graphics/Shader.cpp')
-rw-r--r--src/libjin/Graphics/Shader.cpp199
1 files changed, 199 insertions, 0 deletions
diff --git a/src/libjin/Graphics/Shader.cpp b/src/libjin/Graphics/Shader.cpp
new file mode 100644
index 0000000..a40f78d
--- /dev/null
+++ b/src/libjin/Graphics/Shader.cpp
@@ -0,0 +1,199 @@
+#include "../modules.h"
+#if JIN_MODULES_RENDER
+
+#include "../utils/macros.h"
+#include "Shader.h"
+namespace jin
+{
+namespace graphics
+{
+
+ #include "base.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_TEX_UNIT = 0;
+
+ /*static*/ JSLProgram* JSLProgram::currentJSLProgram = nullptr;
+
+ JSLProgram* JSLProgram::createJSLProgram(const char* program)
+ {
+ return new JSLProgram(program);
+ }
+
+ JSLProgram::JSLProgram(const char* program)
+ : currentTextureUnit(DEFAULT_TEX_UNIT)
+ {
+ char* fs = (char*)alloca(strlen(program) + strlen(base_shader));
+ formatShader(fs, program);
+ GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(shader, 1, (const GLchar**)&fs, NULL);
+ glCompileShader(shader);
+ pid = glCreateProgram();
+ glAttachShader(pid, shader);
+ glLinkProgram(pid);
+ }
+
+ JSLProgram::~JSLProgram()
+ {
+ if (currentJSLProgram == this)
+ unuse();
+ }
+
+ static inline GLint getMaxTextureUnits()
+ {
+ GLint maxTextureUnits = 0;
+ glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
+ return maxTextureUnits;
+ }
+
+ void JSLProgram::use()
+ {
+ glUseProgram(pid);
+ currentJSLProgram = this;
+ bindDefaultTex();
+ }
+
+ /*static*/ void JSLProgram::unuse()
+ {
+ glUseProgram(0);
+ currentJSLProgram = nullptr;
+ }
+
+ void JSLProgram::bindDefaultTex()
+ {
+ int loc = glGetUniformLocation(pid, default_tex);
+ glUniform1i(loc, DEFAULT_TEX_UNIT);
+ }
+
+ GLint JSLProgram::claimTextureUnit(const std::string& name)
+ {
+ std::map<std::string, GLint>::iterator unit = textureUnits.find(name);
+ if (unit != textureUnits.end())
+ return unit->second;
+ static GLint MAX_TEXTURE_UNITS = getMaxTextureUnits();
+ if (++currentTextureUnit >= MAX_TEXTURE_UNITS)
+ return 0;
+ textureUnits[name] = currentTextureUnit;
+ return currentTextureUnit;
+ }
+
+#define checkJSL() \
+ if (currentJSLProgram != this) \
+ return
+
+ void JSLProgram::sendFloat(const char* variable, float number)
+ {
+ checkJSL();
+ int loc = glGetUniformLocation(pid, variable);
+ glUniform1f(loc, number);
+ }
+
+ /**
+ * https://www.douban.com/note/627332677/
+ * struct TextureUnit
+ * {
+ * GLuint targetTexture1D;
+ * GLuint targetTexture2D;
+ * GLuint targetTexture3D;
+ * GLuint targetTextureCube;
+ * ...
+ * };
+ *
+ * TextureUnit textureUnits[GL_MAX_TEXTURE_IMAGE_UNITS]
+ * GLuint currentTextureUnit = 0;
+ */
+ void JSLProgram::sendTexture(const char* variable, const Texture* tex)
+ {
+ checkJSL();
+ GLint location = glGetUniformLocation(pid, variable);
+ if (location == -1)
+ return;
+ GLint unit = claimTextureUnit(variable);
+ if (unit == 0)
+ {
+ // TODO: 쳣󶨵
+ return;
+ }
+ glUniform1i(location, unit);
+ glActiveTexture(GL_TEXTURE0 + unit);
+ glBindTexture(GL_TEXTURE_2D, tex->getTexture());
+ glActiveTexture(GL_TEXTURE0);
+ }
+
+ void JSLProgram::sendCanvas(const char* variable, const Canvas* canvas)
+ {
+ checkJSL();
+ GLint location = glGetUniformLocation(pid, variable);
+ if (location == -1)
+ return;
+ GLint unit = claimTextureUnit(variable);
+ if (unit == 0)
+ {
+ // TODO: 쳣󶨵
+ return;
+ }
+ glUniform1i(location, unit);
+ glActiveTexture(GL_TEXTURE0 + unit);
+ glBindTexture(GL_TEXTURE_2D, canvas->getTexture());
+ glActiveTexture(GL_TEXTURE0);
+ }
+
+ void JSLProgram::sendVec2(const char* name, float x, float y)
+ {
+ checkJSL();
+ int loc = glGetUniformLocation(pid, name);
+ glUniform2f(loc, x, y);
+ }
+
+ void JSLProgram::sendVec3(const char* name, float x, float y, float z)
+ {
+ checkJSL();
+ int loc = glGetUniformLocation(pid, name);
+ glUniform3f(loc, x, y, z);
+ }
+
+ void JSLProgram::sendVec4(const char* name, float x, float y, float z, float w)
+ {
+ checkJSL();
+ int loc = glGetUniformLocation(pid, name);
+ glUniform4f(loc, x, y, z, w);
+ }
+
+ void JSLProgram::sendColor(const char* name, const color* col)
+ {
+ checkJSL();
+ int loc = glGetUniformLocation(pid, name);
+ glUniform4f(loc,
+ col->rgba.r / 255.f,
+ col->rgba.g / 255.f,
+ col->rgba.b / 255.f,
+ col->rgba.a / 255.f
+ );
+ }
+
+} // graphics
+} // jin
+
+#endif // JIN_MODULES_RENDER \ No newline at end of file