#include #include #include #include "shader.h" #include "utils.h" static const char *vertex_shader_100 = "\ #version 100 \n\ attribute vec4 aPosition;\n\ void main(void) {\n\ gl_Position = aPosition;\n\ }\n\ "; static const char *vertex_shader_300 = "\ #version 300 es\n\ in vec4 aPosition;\n\ void main(void) {\n\ gl_Position = aPosition;\n\ }\n\ "; uint16_t get_gl_version() { GLint major = 0, minor = 0; #if defined(GL_MAJOR_VERSION) && defined(GL_MINOR_VERSION) glGetIntegerv(GL_MAJOR_VERSION, &major); glGetIntegerv(GL_MINOR_VERSION, &minor); #else char *version = (char *) glGetString(GL_VERSION); int res = sscanf(version, "OpenGL ES %d.%d", &major, &minor); if (res != 2) { // Maybe the OpenGL ES prefixed was missing res = sscanf(version, "%d.%d", &major, &minor); } if (res != 2) { major = 0; minor = 0; } #endif return (uint16_t)(major * 0x100 + minor); } static GLuint create_shader(const char *source, GLenum type) { // Create the shader object GLuint shader = glCreateShader(type); // Load the shader source glShaderSource(shader, 1, &source, 0); // Compile the shader glCompileShader(shader); // Check for errors GLint status = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { GLchar messages[1024]; glGetShaderInfoLog(shader, sizeof(messages), 0, &messages[0]); fprintf(stderr, "GLSL Shader Error: %s", messages); } return shader; } static GLuint create_program(const char *vsh, const char *fsh) { // Build shaders GLuint vertex_shader = create_shader(vsh, GL_VERTEX_SHADER); GLuint fragment_shader = create_shader(fsh, GL_FRAGMENT_SHADER); // Create program GLuint program = glCreateProgram(); fprintf(stderr, "Creating program...\n"); // Attach shaders glAttachShader(program, vertex_shader); glAttachShader(program, fragment_shader); fprintf(stderr, "Linking program...\n"); // Link program glLinkProgram(program); fprintf(stderr, "Checking for errors...\n"); // Check for errors GLint status; glGetProgramiv(program, GL_LINK_STATUS, &status); if (status == GL_FALSE) { GLint info_len = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_len); if (info_len > 1) { char* info_log = (char*)malloc(sizeof(char) * info_len); glGetProgramInfoLog(program, info_len, NULL, info_log); printf("Error linking program:\n%s\n", info_log); free(info_log); } glDeleteProgram(program); } // Delete shaders glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); return program; } bool init_shader_with_name(shader_t *shader, const char *name) { uint16_t gl_version = get_gl_version(); static char master_shader_code[0x801] = {0,}; static char shader_code[0x10001] = {0,}; static char final_shader_code[0x10801] = {0,}; static signed long filter_token_location = 0; if (!master_shader_code[0]) { size_t header_len = 0; if (gl_version >= 0x300) { char* header = "#version 300 es\n#define VERSION 0x300\n"; header_len = strlen(header); strcpy(master_shader_code, header); } else { char* header = "#version 100\n#define VERSION 0x100\n"; header_len = strlen(header); strcpy(master_shader_code, header); } FILE *master_shader_f = fopen(resource_path("Shaders/WasmMasterShader.fsh"), "r"); if (!master_shader_f) return false; fread(master_shader_code + header_len, 1, sizeof(master_shader_code) - 1, master_shader_f); fclose(master_shader_f); filter_token_location = strstr(master_shader_code, "{filter}") - master_shader_code; if (filter_token_location < 0) { master_shader_code[0] = 0; return false; } } char shader_path[1024]; sprintf(shader_path, "Shaders/%s.fsh", name); FILE *shader_f = fopen(resource_path(shader_path), "r"); if (!shader_f) return false; memset(shader_code, 0, sizeof(shader_code)); fread(shader_code, 1, sizeof(shader_code) - 1, shader_f); fclose(shader_f); memset(final_shader_code, 0, sizeof(final_shader_code)); memcpy(final_shader_code, master_shader_code, filter_token_location); strcpy(final_shader_code + filter_token_location, shader_code); strcat(final_shader_code + filter_token_location, master_shader_code + filter_token_location + sizeof("{filter}") - 1); fprintf(stderr, "Shader code:\n%s\n", final_shader_code); if (gl_version >= 0x300) { shader->program = create_program(vertex_shader_300, final_shader_code); } else { shader->program = create_program(vertex_shader_100, final_shader_code); } // Attributes shader->position_attribute = glGetAttribLocation(shader->program, "aPosition"); // Uniforms shader->input_resolution_uniform = glGetUniformLocation(shader->program, "input_resolution"); shader->resolution_uniform = glGetUniformLocation(shader->program, "output_resolution"); shader->origin_uniform = glGetUniformLocation(shader->program, "origin"); glGenTextures(1, &shader->texture); glBindTexture(GL_TEXTURE_2D, shader->texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(GL_TEXTURE_2D, 0); shader->texture_uniform = glGetUniformLocation(shader->program, "image"); glGenTextures(1, &shader->previous_texture); glBindTexture(GL_TEXTURE_2D, shader->previous_texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(GL_TEXTURE_2D, 0); shader->previous_texture_uniform = glGetUniformLocation(shader->program, "previous_image"); shader->mix_previous_uniform = glGetUniformLocation(shader->program, "mix_previous"); // Program glUseProgram(shader->program); GLuint vao; if (gl_version >= 0x300) { glGenVertexArrays(1, &vao); glBindVertexArray(vao); } GLuint vbo; glGenBuffers(1, &vbo); // Attributes static GLfloat const quad[16] = { -1.f, -1.f, 0, 1, -1.f, +1.f, 0, 1, +1.f, -1.f, 0, 1, +1.f, +1.f, 0, 1, }; glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW); glEnableVertexAttribArray(shader->position_attribute); glVertexAttribPointer(shader->position_attribute, 4, GL_FLOAT, GL_FALSE, 0, 0); return true; } void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous, unsigned source_width, unsigned source_height, unsigned x, unsigned y, unsigned w, unsigned h) { glUseProgram(shader->program); glUniform2f(shader->origin_uniform, x, y); glUniform2f(shader->input_resolution_uniform, source_width, source_height); glUniform2f(shader->resolution_uniform, w, h); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, shader->texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, source_width, source_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap); glUniform1i(shader->texture_uniform, 0); glUniform1i(shader->mix_previous_uniform, previous != NULL); if (previous) { glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, shader->previous_texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, source_width, source_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, previous); glUniform1i(shader->previous_texture_uniform, 1); } glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } void free_shader(shader_t *shader) { glDeleteProgram(shader->program); glDeleteTextures(1, &shader->texture); glDeleteTextures(1, &shader->previous_texture); }