#include #include #include #include #include #include #include #include using namespace std::literals::string_literals; #define WINDOW_WIDTH 640 #define WINDOW_HEIGHT 480 std::string file_to_string(char const *path) { std::string fileContent; std::string line; std::ifstream fileStream(path); if(!fileStream.is_open()) throw "Error opening \""s + path + "\""; while(std::getline(fileStream, line)) { fileContent += line + "\n"; } fileStream.close(); return fileContent; } GLuint load_shader(std::string const& shaderContent, GLenum type) { GLuint shader = glCreateShader(type); if(shader == 0) return 0; char const* shaderContentPtr = shaderContent.c_str(); glShaderSource(shader, 1, &shaderContentPtr, nullptr); glCompileShader(shader); GLint result = GL_FALSE; glGetShaderiv(shader, GL_COMPILE_STATUS, &result); if(result == GL_FALSE) { GLint infoLogLength = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); if(infoLogLength == 0) return 0; std::unique_ptr logBuffer(new char[infoLogLength]); glGetShaderInfoLog(shader, infoLogLength, nullptr, logBuffer.get()); std::cerr << "Error compiling the given shader: " << logBuffer.get() << std::endl; return 0; } return shader; } GLuint load_program(char const* fs_path) { static std::string const vs_content = R"END(#version 330 core #define VERTICES 0 layout(location = VERTICES) in vec2 vpos; out vec2 fragCoord; uniform vec2 iResolution; void main() { vec2 tmp = (vpos + 1.0) / 2.0; fragCoord = vec2(tmp.x * iResolution.x, tmp.y * iResolution.y); gl_Position = vec4(vpos, 0.0, 1.0); } )END"; static std::string const fs_header = R"END(#version 330 core in vec2 fragCoord; out vec4 fragColor; uniform float iGlobalTime; uniform vec2 iResolution; uniform int iFrame; )END"; static std::string const fs_footer = R"END( void main() { mainImage(fragColor, fragCoord); } )END"; std::string fs_content; try { fs_content = file_to_string(fs_path); } catch(std::string& err) { std::cerr << err << "\n"; return 0; } std::string fragment_shader_content = fs_header + fs_content + fs_footer; std::cout << fragment_shader_content << std::endl; GLuint vertexShader = load_shader(vs_content, GL_VERTEX_SHADER); GLuint fragmentShader = load_shader(fragment_shader_content, GL_FRAGMENT_SHADER); if(!vertexShader || !fragmentShader) { glDeleteShader(vertexShader); glDeleteShader(fragmentShader); return 0; } GLuint program = glCreateProgram(); if(!program) { glDeleteShader(vertexShader); glDeleteShader(fragmentShader); return 0; } glAttachShader(program, vertexShader); glAttachShader(program, fragmentShader); glBindFragDataLocation(program, 0, "fragColor"); glLinkProgram(program); GLint result = GL_FALSE; glGetProgramiv(program, GL_LINK_STATUS, &result); if(result == GL_FALSE) { glDeleteShader(vertexShader); glDeleteShader(fragmentShader); GLint infoLogLength = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength); if(infoLogLength == 0) return 0; std::unique_ptr logBuffer(new char[infoLogLength]); glGetProgramInfoLog(program, infoLogLength, nullptr, logBuffer.get()); std::cerr << "Error linking the given program: " << logBuffer.get() << std::endl; glDeleteProgram(program); return 0; } glDetachShader(program, vertexShader); glDetachShader(program, fragmentShader); glDeleteShader(vertexShader); glDeleteShader(fragmentShader); return program; } int main(int argc, char *argv[]) { SDL_Window *window; SDL_GLContext context; GLenum glew_err; GLuint program; GLuint vao; GLuint vbo; GLint iGlobalTime, iResolution, iFrame; int curframe = 0; bool quit; static const GLfloat vertices[] = { -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f }; if(argc != 2) { std::cerr << "Usage: " << argv[0] << " fragment_shader.glsl\n"; return EXIT_FAILURE; } if(SDL_Init(SDL_INIT_VIDEO) != 0) { std::cerr << "Error initializing SDL (" << SDL_GetError() << ")\n"; goto error_a; } SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG | SDL_GL_CONTEXT_DEBUG_FLAG); window = SDL_CreateWindow(u8"Shader loader", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL); if(window == nullptr) { std::cerr << "Error creating window (" << SDL_GetError() << ")\n"; goto error_b; } context = SDL_GL_CreateContext(window); if(context == NULL) { std::cerr << "Error initializing OpenGL context (" << SDL_GetError() << ")\n"; goto error_c; } glewExperimental = GL_TRUE; glew_err = glewInit(); if(glew_err != GLEW_OK) { std::cerr << "Error initializing GLEW (" << glewGetErrorString(glew_err) << ")\n"; goto error_d; } if(!GLEW_ARB_debug_output) { std::cerr << "Error: ARB_debug_output isn't supported\n"; goto error_d; } glDebugMessageCallbackARB( [](GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, GLchar const* message, GLvoid const* userParam) -> void { std::function source_to_str ([](GLenum source) -> std::string { if(source == GL_DEBUG_SOURCE_API_ARB) return "OpenGL"s; else if(source == GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB) return "Window System"s; else if(source == GL_DEBUG_SOURCE_SHADER_COMPILER_ARB) return "Shader Compiler"s; else if(source == GL_DEBUG_SOURCE_THIRD_PARTY_ARB) return "Third Party"s; else if(source == GL_DEBUG_SOURCE_APPLICATION_ARB) return "Application"s; else if(source == GL_DEBUG_SOURCE_OTHER_ARB) return "Other"s; else return "?"s; }); std::function type_to_str ([](GLenum type) -> std::string { if(type == GL_DEBUG_TYPE_ERROR_ARB) return "Error"s; else if(type == GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB) return "Deprecated Behavior"s; else if(type == GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB) return "Undefined Behavior"s; else if(type == GL_DEBUG_TYPE_PORTABILITY_ARB) return "Portability"s; else if(type == GL_DEBUG_TYPE_PERFORMANCE_ARB) return "Performance"s; else if(type == GL_DEBUG_TYPE_OTHER_ARB) return "Other"s; else return "?"s; }); std::function severity_to_str ([](GLenum severity) -> std::string { if(severity == GL_DEBUG_SEVERITY_LOW_ARB) return "Low"s; else if(severity == GL_DEBUG_SEVERITY_MEDIUM_ARB) return "Medium"s; else if(severity == GL_DEBUG_SEVERITY_HIGH_ARB) return "High"s; else return "?"s; }); std::cout << "ARB_debug_output: " << "source = " << source_to_str(source) << ", " << "type = " << type_to_str(type) << ", " << "severity = " << severity_to_str(severity) << ", " << "id = " << id << ", " << "message = " << message << std::endl; }, nullptr); program = load_program(argv[1]); if(program == 0) { goto error_d; } glGenVertexArrays(1, &vao); glBindVertexArray(vao); glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof vertices, vertices, GL_STATIC_DRAW); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); quit = false; while(!quit) { SDL_Event event; while(SDL_PollEvent(&event)) { if(event.type == SDL_QUIT) quit = true; else if(event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE) quit = true; else if(event.type == SDL_KEYUP && event.key.keysym.sym == SDLK_ESCAPE) quit = true; } glClear(GL_COLOR_BUFFER_BIT); glUseProgram(program); iGlobalTime = glGetUniformLocation(program, "iGlobalTime"); glUniform1f(iGlobalTime, (float) SDL_GetTicks() / 1000.0); iResolution = glGetUniformLocation(program, "iResolution"); glUniform2f(iResolution, (GLfloat) WINDOW_WIDTH, (GLfloat) WINDOW_HEIGHT); iFrame = glGetUniformLocation(program, "iFrame"); glUniform1i(iFrame, curframe++); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, vbo); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*) 0); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDisableVertexAttribArray(0); SDL_GL_SwapWindow(window); } glDeleteBuffers(1, &vbo); glDeleteVertexArrays(1, &vao); SDL_GL_DeleteContext(context); SDL_DestroyWindow(window); SDL_Quit(); return EXIT_SUCCESS; error_d: SDL_GL_DeleteContext(context); error_c: SDL_DestroyWindow(window); error_b: SDL_Quit(); error_a: return EXIT_FAILURE; }