From faa74e2c942102a9b1aa215a913fddf422714d7e Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 22 Oct 2021 03:35:27 +0200 Subject: Add shader --- README.md | 1 + include/mgl/gl.h | 15 ++++ include/mgl/gl_macro.h | 15 +++- include/mgl/graphics/shader.h | 30 +++++++ src/gl.c | 15 ++++ src/graphics/shader.c | 195 ++++++++++++++++++++++++++++++++++++++++++ src/graphics/vertex_buffer.c | 4 +- tests/circle_mask.glsl | 17 ++++ tests/main.c | 19 ++++ 9 files changed, 307 insertions(+), 4 deletions(-) create mode 100644 include/mgl/graphics/shader.h create mode 100644 src/graphics/shader.c create mode 100644 tests/circle_mask.glsl diff --git a/README.md b/README.md index 02015e8..05b12e7 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,4 @@ Handle window close (window destroyed event, disconnected from server and socket Bind texture and cache the bound texture to reduce calls to opengl. Use gl triangle instead of quad. Fix crash on exit. +Support using multiple textures in shaders by using glActiveTexture for each one and set the uniform sampler2D value for each as as the index. \ No newline at end of file diff --git a/include/mgl/gl.h b/include/mgl/gl.h index 8a09e3e..a13406e 100644 --- a/include/mgl/gl.h +++ b/include/mgl/gl.h @@ -52,6 +52,21 @@ typedef struct { void (*glVertexPointer)(int size, unsigned int type, int stride, const void *ptr); void (*glColorPointer)(int size, unsigned int type, int stride, const void *ptr); void (*glTexCoordPointer)(int size, unsigned int type, int stride, const void *ptr); + void (*glCompileShader)(unsigned int shader); + unsigned int (*glCreateProgram)(void); + unsigned int (*glCreateShader)(unsigned int type); + void (*glDeleteProgram)(unsigned int program); + void (*glDeleteShader)(unsigned int shader); + void (*glGetShaderiv)(unsigned int shader, unsigned int pname, int *params); + void (*glGetShaderInfoLog)(unsigned int shader, int bufSize, int *length, char *infoLog); + void (*glGetProgramiv)(unsigned int program, unsigned int pname, int *params); + void (*glGetProgramInfoLog)(unsigned int program, int bufSize, int *length, char *infoLog); + void (*glLinkProgram)(unsigned int program); + void (*glShaderSource)(unsigned int shader, int count, const char *const*string, const int *length); + void (*glUseProgram)(unsigned int program); + void (*glAttachShader)(unsigned int program, unsigned int shader); + int (*glGetUniformLocation)(unsigned int program, const char *name); + void (*glUniform2fv)(int location, int count, const float *value); /* Optional*/ void (*glXSwapIntervalEXT)(Display * dpy, GLXDrawable drawable, int interval); diff --git a/include/mgl/gl_macro.h b/include/mgl/gl_macro.h index a3be960..c47c8fb 100644 --- a/include/mgl/gl_macro.h +++ b/include/mgl/gl_macro.h @@ -1,7 +1,6 @@ #ifndef MGL_GL_MACRO_H #define MGL_GL_MACRO_H -/* Copied from GL/glx.h */ #define GLX_USE_GL 1 #define GLX_BUFFER_SIZE 2 #define GLX_LEVEL 3 @@ -20,7 +19,6 @@ #define GLX_ACCUM_BLUE_SIZE 16 #define GLX_ACCUM_ALPHA_SIZE 17 -/* Copied from GL/gl.h */ #define GL_COLOR_BUFFER_BIT 0x00004000 #define GL_BLEND 0x0BE2 #define GL_SRC_ALPHA 0x0302 @@ -73,4 +71,17 @@ #define GL_COLOR_ARRAY 0x8076 #define GL_TEXTURE_COORD_ARRAY 0x8078 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_GEOMETRY_SHADER 0x8DD9 + +#define GL_INFO_LOG_LENGTH 0x8B84 + +#define GL_LINK_STATUS 0x8B82 +#define GL_COMPILE_STATUS 0x8B81 + +#define GL_FALSE 0 +#define GL_TRUE 1 + + #endif /* MGL_GL_MACRO_H */ diff --git a/include/mgl/graphics/shader.h b/include/mgl/graphics/shader.h new file mode 100644 index 0000000..fb00066 --- /dev/null +++ b/include/mgl/graphics/shader.h @@ -0,0 +1,30 @@ +#ifndef MGL_SHADER_H +#define MGL_SHADER_H + +#include "../system/vec.h" + +typedef struct mgl_texture mgl_texture; + +typedef struct { + unsigned int id; +} mgl_shader_program; + +typedef enum { + MGL_SHADER_VERTEX, + MGL_SHADER_FRAGMENT, + MGL_SHADER_GEOMETRY +} mgl_shader_type; + +int mgl_shader_program_init(mgl_shader_program *self); +void mgl_shader_program_deinit(mgl_shader_program *self); + +int mgl_shader_program_add_shader_from_file(mgl_shader_program *self, const char *filepath, mgl_shader_type shader_type); +int mgl_shader_program_add_shader_from_memory(mgl_shader_program *self, unsigned char *shader_data, int shader_size, mgl_shader_type shader_type); +int mgl_shader_program_finalize(mgl_shader_program *self); + +int mgl_shader_program_set_uniform_vec2f(mgl_shader_program *self, const char *uniform_name, mgl_vec2f value); + +/* If |shader_program| is NULL then no shader is used */ +void mgl_shader_program_use(mgl_shader_program *shader_program); + +#endif /* MGL_SHADER_H */ diff --git a/src/gl.c b/src/gl.c index 615fb55..4227d9e 100644 --- a/src/gl.c +++ b/src/gl.c @@ -67,6 +67,21 @@ int mgl_gl_load(mgl_gl *self) { { &self->glVertexPointer, "glVertexPointer" }, { &self->glColorPointer, "glColorPointer" }, { &self->glTexCoordPointer, "glTexCoordPointer" }, + { &self->glCompileShader, "glCompileShader" }, + { &self->glCreateProgram, "glCreateProgram" }, + { &self->glCreateShader, "glCreateShader" }, + { &self->glDeleteProgram, "glDeleteProgram" }, + { &self->glDeleteShader, "glDeleteShader" }, + { &self->glGetShaderiv, "glGetShaderiv" }, + { &self->glGetShaderInfoLog, "glGetShaderInfoLog" }, + { &self->glGetProgramiv, "glGetProgramiv" }, + { &self->glGetProgramInfoLog, "glGetProgramInfoLog" }, + { &self->glLinkProgram, "glLinkProgram" }, + { &self->glShaderSource, "glShaderSource" }, + { &self->glUseProgram, "glUseProgram" }, + { &self->glAttachShader, "glAttachShader" }, + { &self->glGetUniformLocation, "glGetUniformLocation" }, + { &self->glUniform2fv, "glUniform2fv" }, { NULL, NULL } }; diff --git a/src/graphics/shader.c b/src/graphics/shader.c new file mode 100644 index 0000000..3d8a204 --- /dev/null +++ b/src/graphics/shader.c @@ -0,0 +1,195 @@ +#include "../../include/mgl/graphics/shader.h" +#include "../../include/mgl/graphics/texture.h" +#include "../../include/mgl/system/fileutils.h" +#include "../../include/mgl/mgl.h" +#include +#include + +typedef struct { + unsigned int id; + mgl_shader_type shader_type; +} mgl_shader; + +static unsigned int mgl_shader_type_to_gl_shader_type(mgl_shader_type shader_type) { + switch(shader_type) { + case MGL_SHADER_VERTEX: return GL_VERTEX_SHADER; + case MGL_SHADER_FRAGMENT: return GL_FRAGMENT_SHADER; + case MGL_SHADER_GEOMETRY: return GL_GEOMETRY_SHADER; + } + return 0; +} + +static void print_compile_log(mgl_context *context, unsigned int shader_id, const char *prefix) { + int log_length = 0; + context->gl.glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &log_length); + if(log_length > 0) { + char *log_str = malloc(log_length + 1); + if(!log_str) { + fprintf(stderr, "Error: failed to allocate memory for shader compile log\n"); + return; + } + + log_str[0] = '\0'; + log_str[log_length] = '\0'; + context->gl.glGetShaderInfoLog(shader_id, log_length, NULL, log_str); + + fprintf(stderr, "%s: %s\n", prefix, log_str); + free(log_str); + } +} + +static void mgl_shader_unload(mgl_shader *self); + +static int mgl_shader_load_from_memory(mgl_shader *self, unsigned char *shader_data, int shader_size, mgl_shader_type shader_type) { + self->id = 0; + self->shader_type = shader_type; + + mgl_context *context = mgl_get_context(); + self->id = context->gl.glCreateShader(mgl_shader_type_to_gl_shader_type(shader_type)); + if(self->id == 0) { + fprintf(stderr, "Error: failed to load shader, error: glCreateShader failed\n"); + return -1; + } + + context->gl.glShaderSource(self->id, 1, (const char* const*)&shader_data, &shader_size); + context->gl.glCompileShader(self->id); + + int compiled_successfully = GL_FALSE; + context->gl.glGetShaderiv(self->id, GL_COMPILE_STATUS, &compiled_successfully); + if(compiled_successfully == GL_FALSE) { + print_compile_log(context, self->id, "Error"); + mgl_shader_unload(self); + return -1; + } + + print_compile_log(context, self->id, "Warning"); + return 0; +} + +static int mgl_shader_load_from_file(mgl_shader *self, const char *filepath, mgl_shader_type shader_type) { + self->id = 0; + self->shader_type = shader_type; + + mgl_filedata filedata; + if(mgl_load_file(filepath, &filedata) != 0) { + fprintf(stderr, "Error: failed to load shader %s, error: mgl_load_file failed\n", filepath); + return -1; + } + + if(filedata.size > INT32_MAX) { + fprintf(stderr, "Error: failed to load shader %s, error: shader size is too large\n", filepath); + return -1; + } + + int res = mgl_shader_load_from_memory(self, filedata.data, filedata.size, shader_type); + mgl_filedata_free(&filedata); + return res; +} + +void mgl_shader_unload(mgl_shader *self) { + mgl_context *context = mgl_get_context(); + if(self->id) { + context->gl.glDeleteShader(self->id); + self->id = 0; + } +} + +int mgl_shader_program_init(mgl_shader_program *self) { + self->id = 0; + + mgl_context *context = mgl_get_context(); + self->id = context->gl.glCreateProgram(); + if(self->id == 0) { + fprintf(stderr, "Error: failed to create shader program: error glCreateProgram failed\n"); + return -1; + } + + return 0; +} + +void mgl_shader_program_deinit(mgl_shader_program *self){ + mgl_context *context = mgl_get_context(); + if(self->id) { + context->gl.glDeleteProgram(self->id); + self->id = 0; + } +} + +/* TODO: Check for attach shader error */ + +int mgl_shader_program_add_shader_from_file(mgl_shader_program *self, const char *filepath, mgl_shader_type shader_type){ + mgl_shader shader; + if(mgl_shader_load_from_file(&shader, filepath, shader_type) != 0) + return -1; + + mgl_get_context()->gl.glAttachShader(self->id, shader.id); + mgl_shader_unload(&shader); /* TODO: Verify if deleting the shader here is always ok now that we have attached it */ + return 0; +} + +int mgl_shader_program_add_shader_from_memory(mgl_shader_program *self, unsigned char *shader_data, int shader_size, mgl_shader_type shader_type){ + mgl_shader shader; + if(mgl_shader_load_from_memory(&shader, shader_data, shader_size, shader_type) != 0) + return -1; + + mgl_get_context()->gl.glAttachShader(self->id, shader.id); + mgl_shader_unload(&shader); /* TODO: Verify if deleting the shader here is always ok now that we have attached it */ + return 0; +} + +static void print_link_log(mgl_context *context, unsigned int shader_id, const char *prefix) { + int log_length = 0; + context->gl.glGetProgramiv(shader_id, GL_INFO_LOG_LENGTH, &log_length); + if(log_length > 0) { + char *log_str = malloc(log_length + 1); + if(!log_str) { + fprintf(stderr, "Error: failed to allocate memory for shader link log\n"); + return; + } + + log_str[0] = '\0'; + log_str[log_length] = '\0'; + context->gl.glGetProgramInfoLog(shader_id, log_length, NULL, log_str); + + fprintf(stderr, "%s: %s\n", prefix, log_str); + free(log_str); + } +} + +int mgl_shader_program_finalize(mgl_shader_program *self) { + mgl_context *context = mgl_get_context(); + + context->gl.glLinkProgram(self->id); + int is_linked = GL_TRUE; + context->gl.glGetProgramiv(self->id, GL_LINK_STATUS, &is_linked); + if(is_linked == GL_FALSE) { + print_link_log(context, self->id, "Error"); + return -1; + } + + print_link_log(context, self->id, "Warning"); + return 0; +} + +/* TODO: Optimize glUseProgram */ +/* TODO: Optimize glGetUniformLocation */ + +int mgl_shader_program_set_uniform_vec2f(mgl_shader_program *self, const char *uniform_name, mgl_vec2f value) { + mgl_context *context = mgl_get_context(); + int uniform_location = context->gl.glGetUniformLocation(self->id, uniform_name); + if(uniform_location == -1) { + fprintf(stderr, "Error: no uniform by the name %s was found in the shader\n", uniform_name); + return -1; + } + + context->gl.glUseProgram(self->id); + context->gl.glUniform2fv(uniform_location, 1, &value.x); + context->gl.glUseProgram(0); + return 0; +} + +/* TODO: Optimize glUseProgram */ +void mgl_shader_program_use(mgl_shader_program *shader_program) { + mgl_context *context = mgl_get_context(); + context->gl.glUseProgram(shader_program ? shader_program->id : 0); +} diff --git a/src/graphics/vertex_buffer.c b/src/graphics/vertex_buffer.c index 415f574..df2c139 100644 --- a/src/graphics/vertex_buffer.c +++ b/src/graphics/vertex_buffer.c @@ -14,7 +14,7 @@ static unsigned int mgl_vertex_buffer_usage_to_gl_usage(mgl_vertex_buffer_usage case MGL_USAGE_DYNAMIC: return GL_DYNAMIC_DRAW; case MGL_USAGE_STATIC: return GL_STATIC_DRAW; } - return GL_STREAM_DRAW; + return 0; } static unsigned int mgl_primitive_type_to_gl_mode(mgl_primitive_type primitive_type) { @@ -29,7 +29,7 @@ static unsigned int mgl_primitive_type_to_gl_mode(mgl_primitive_type primitive_t case MGL_PRIMITIVE_QUAD_STRIP: return GL_QUAD_STRIP; case MGL_PRIMITIVE_POLYGON: return GL_POLYGON; } - return MGL_PRIMITIVE_QUADS; + return 0; } int mgl_vertex_buffer_init(mgl_vertex_buffer *self, mgl_primitive_type primitive_type, mgl_vertex_buffer_usage usage) { diff --git a/tests/circle_mask.glsl b/tests/circle_mask.glsl new file mode 100644 index 0000000..0564cf3 --- /dev/null +++ b/tests/circle_mask.glsl @@ -0,0 +1,17 @@ +uniform sampler2D texture; +uniform vec2 resolution; + +vec4 circle(vec2 uv, vec2 pos, float rad, vec4 color) { + float d = smoothstep(0.0, 2.0, length(pos - uv) - rad); + float t = clamp(d, 0.0, 1.0); + return vec4(color.rgb, color.a * (1.0 - t)); +} + +void main() { + vec2 uv = gl_TexCoord[0].xy * resolution; + vec2 center = resolution * 0.5; + float radius = 0.5 * resolution.y - 1.0; + + vec4 texture_color = texture2D(texture, gl_TexCoord[0].xy); + gl_FragColor = circle(uv, center, radius, texture_color); +} \ No newline at end of file diff --git a/tests/main.c b/tests/main.c index 8631c35..0e6e199 100644 --- a/tests/main.c +++ b/tests/main.c @@ -8,11 +8,13 @@ #include #include #include +#include typedef struct { mgl_texture *texture; mgl_font *font; mgl_vertex_buffer *vertex_buffer; + mgl_shader_program *shader_program; } Userdata; static void draw(mgl_window *window, void *userdata) { @@ -26,10 +28,15 @@ static void draw(mgl_window *window, void *userdata) { }; mgl_rectangle_draw(context, &rect); + /*mgl_shader_program_set_uniform_texture2d(u->shader_program, "texture", u->texture);*/ + mgl_shader_program_set_uniform_vec2f(u->shader_program, "resolution", (mgl_vec2f){ window->size.x, window->size.y }); + mgl_sprite sprite; mgl_sprite_init(&sprite, u->texture, 100.0f - 10.0f, 0.0f); mgl_sprite_set_color(&sprite, (mgl_color){255, 255, 255, 128}); + mgl_shader_program_use(u->shader_program); mgl_sprite_draw(context, &sprite); + mgl_shader_program_use(NULL); mgl_text text; mgl_text_init(&text, u->font, "hello world!\nGood bye world!", 0.0f, 0.0f); @@ -47,11 +54,13 @@ int main(int argc, char **argv) { mgl_texture texture; mgl_font font; mgl_vertex_buffer vertex_buffer; + mgl_shader_program shader_program; Userdata userdata; userdata.texture = &texture; userdata.font = &font; userdata.vertex_buffer = &vertex_buffer; + userdata.shader_program = &shader_program; mgl_window window; if(mgl_window_create(&window, "mgl", 1280, 720) != 0) @@ -63,6 +72,15 @@ int main(int argc, char **argv) { if(mgl_font_load_from_file(&font, "/usr/share/fonts/noto/NotoSans-Regular.ttf", 32) != 0) return 1; + if(mgl_shader_program_init(&shader_program) != 0) + return 1; + + if(mgl_shader_program_add_shader_from_file(&shader_program, "tests/circle_mask.glsl", MGL_SHADER_FRAGMENT) != 0) + return 1; + + if(mgl_shader_program_finalize(&shader_program) != 0) + return 1; + if(mgl_vertex_buffer_init(&vertex_buffer, MGL_PRIMITIVE_QUADS, MGL_USAGE_STATIC) != 0) return 1; @@ -106,6 +124,7 @@ int main(int argc, char **argv) { } mgl_vertex_buffer_deinit(&vertex_buffer); + mgl_shader_program_deinit(&shader_program); mgl_font_unload(&font); mgl_texture_unload(&texture); mgl_window_deinit(&window); -- cgit v1.2.3