From ffa662ec5acdaae36f0ffeaf0cee78a4200d897b Mon Sep 17 00:00:00 2001 From: Adrian Kummerlaender Date: Mon, 21 May 2018 13:04:41 +0200 Subject: Introduce basic shader, texture buffer abstraction --- src/compute_shader.h | 41 +++++++++ src/graphic_shader.h | 44 ++++++++++ src/main.cc | 229 ++++++++++++++++-------------------------------- src/shader/compute.glsl | 2 +- src/texture_buffer.h | 53 +++++++++++ src/util.h | 44 ++++++++++ 6 files changed, 258 insertions(+), 155 deletions(-) create mode 100644 src/compute_shader.h create mode 100644 src/graphic_shader.h create mode 100644 src/texture_buffer.h create mode 100644 src/util.h diff --git a/src/compute_shader.h b/src/compute_shader.h new file mode 100644 index 0000000..298a454 --- /dev/null +++ b/src/compute_shader.h @@ -0,0 +1,41 @@ +#include "util.h" + +class ComputeShader { +private: + const GLuint _id; + +public: + struct Guard { + const GLuint _id; + + Guard(GLuint id): _id(id) { + glUseProgram(_id); + } + ~Guard() { + glUseProgram(0); + } + }; + + Guard use() { + return Guard(_id); + } + + ComputeShader(const std::string& src): + _id(glCreateProgram()) { + glAttachShader(_id, util::compileShader(src, GL_COMPUTE_SHADER)); + glLinkProgram(_id); + }; + ~ComputeShader() { + glDeleteProgram(_id); + } + + GLuint setUniform(const std::string& name, float x, float y) { + GLuint id = util::getUniform(_id, name); + glUniform2f(id, x, y); + return id; + } + + void dispatch(std::size_t dimX) { + glDispatchCompute(dimX, 1, 1); + } +}; diff --git a/src/graphic_shader.h b/src/graphic_shader.h new file mode 100644 index 0000000..2dd8c41 --- /dev/null +++ b/src/graphic_shader.h @@ -0,0 +1,44 @@ +#include "util.h" + +class GraphicShader { +private: + const GLuint _id; + +public: + struct Guard { + const GLuint _id; + + Guard(GLuint id): _id(id) { + glUseProgram(_id); + } + ~Guard() { + glUseProgram(0); + } + }; + + Guard use() { + return Guard(_id); + } + + GraphicShader(const std::string& vertex, const std::string fragment): + _id(glCreateProgram()) { + glAttachShader(_id, util::compileShader(vertex, GL_VERTEX_SHADER)); + glAttachShader(_id, util::compileShader(fragment, GL_FRAGMENT_SHADER)); + glLinkProgram(_id); + }; + ~GraphicShader() { + glDeleteProgram(_id); + } + + GLuint setUniform(const std::string& name, int value) { + GLuint id = util::getUniform(_id, name); + glUniform1i(id, value); + return id; + } + + GLuint setUniform(const std::string& name, glm::mat4& M) { + GLuint id = util::getUniform(_id, name); + glUniformMatrix4fv(id, 1, GL_FALSE, &M[0][0]); + return id; + } +}; diff --git a/src/main.cc b/src/main.cc index 8a1d6de..c6945fb 100644 --- a/src/main.cc +++ b/src/main.cc @@ -8,6 +8,11 @@ #include #include #include +#include + +#include "graphic_shader.h" +#include "compute_shader.h" +#include "texture_buffer.h" #include "shader/vertex.glsl" #include "shader/fragment.glsl" @@ -15,92 +20,11 @@ const std::size_t particle_count = 100000; -GLint getUniform(GLuint program, const std::string& name) { - const GLint uniform = glGetUniformLocation(program, name.c_str()); - if ( uniform == -1 ) { - std::cerr << "Could not bind uniform " << name << std::endl; - } - return uniform; -} - -GLint compileShader(const std::string& source, GLenum type) { - GLint shader = glCreateShader(type); - - if ( !shader ) { - std::cerr << "Cannot create a shader of type " << type << std::endl; - exit(-1); - } - - const char* source_data = source.c_str(); - const int source_length = source.size(); - - glShaderSource(shader, 1, &source_data, &source_length); - glCompileShader(shader); - - GLint compiled; - glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); - if ( !compiled ) { - std::cerr << "Cannot compile shader" << std::endl; - GLint maxLength = 0; - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); - std::vector errorLog(maxLength); - glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]); - for( auto c : errorLog ) { - std::cerr << c; - } - std::cerr << std::endl; - } - - return shader; -} - -GLint setupShader() { - GLint shader = glCreateProgram(); - glAttachShader(shader, compileShader(VERTEX_SHADER_CODE, GL_VERTEX_SHADER)); - glAttachShader(shader, compileShader(FRAGMENT_SHADER_CODE, GL_FRAGMENT_SHADER)); - glLinkProgram(shader); - return shader; -} - -GLint setupComputeShader() { - GLint shader = glCreateProgram(); - glAttachShader(shader, compileShader(COMPUTE_SHADER_CODE, GL_COMPUTE_SHADER)); - glLinkProgram(shader); - return shader; -} - -GLint setupTextureShader() { - GLint shader = glCreateProgram(); - glAttachShader(shader, compileShader(R"( - #version 330 core - layout (location = 0) in vec2 screen_vertex; - layout (location = 1) in vec2 texture_vertex; - out vec2 TexCoords; - - void main() { - gl_Position = vec4(screen_vertex, 0.0, 1.0); - TexCoords = texture_vertex; - } - )", GL_VERTEX_SHADER)); - glAttachShader(shader, compileShader(R"( - #version 330 core - out vec4 FragColor; - in vec2 TexCoords; - uniform sampler2D screen_texture; - - void main() { - FragColor = texture(screen_texture, TexCoords); - } - )", GL_FRAGMENT_SHADER)); - glLinkProgram(shader); - return shader; -} - int window_width = 800; int window_height = 600; float world_width, world_height; glm::mat4 MVP; -GLuint FramebufferID; +std::unique_ptr textureBuffer; void updateMVP() { world_width = 20.f; @@ -123,10 +47,7 @@ void window_size_callback(GLFWwindow*, int width, int height) { window_width = width; window_height = height; - glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID); - - glViewport(0, 0, width, height); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, window_width, window_height, 0, GL_RGB, GL_UNSIGNED_BYTE, (void*)0); + textureBuffer->resize(width, height); updateMVP(); } @@ -178,51 +99,43 @@ int main() { glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE); - glGenFramebuffers(1, &FramebufferID); - glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID); - - GLuint TextureID; - glGenTextures(1, &TextureID); - glBindTexture(GL_TEXTURE_2D, TextureID); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, window_width, window_height, 0, GL_RGB, GL_UNSIGNED_BYTE, (void*)0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, TextureID, 0); + updateMVP(); - if ( glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE ) { - std::cerr << "Texture framebuffer error" << std::endl; - return -1; - } + textureBuffer = std::make_unique(window_width, window_height); - glClearColor(0.0f, 0.0f, 0.4f, 0.0f); + { + auto guard = textureBuffer->use(); - updateMVP(); + glClearColor(0.0f, 0.0f, 0.4f, 0.0f); + } - GLint ShaderID = setupShader(); - GLuint MatrixID = glGetUniformLocation(ShaderID, "MVP"); + GraphicShader sceneShader(VERTEX_SHADER_CODE, FRAGMENT_SHADER_CODE); + sceneShader.setUniform("MVP", MVP); - GLint ComputeShaderID = setupComputeShader(); - GLuint WorldID = glGetUniformLocation(ComputeShaderID, "world"); + ComputeShader computeShader(COMPUTE_SHADER_CODE); auto vertex_buffer_data = makeInitialParticles(particle_count); GLuint VertexArrayID; - glGenVertexArrays(1, &VertexArrayID); - glBindVertexArray(VertexArrayID); - glEnableVertexAttribArray(0); - GLuint VertexBufferID; - glGenBuffers(1, &VertexBufferID); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, VertexBufferID); - glBufferData( - GL_SHADER_STORAGE_BUFFER, - vertex_buffer_data.size() * sizeof(GLfloat), - vertex_buffer_data.data(), - GL_STATIC_DRAW - ); + { + auto guard = textureBuffer->use(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glGenVertexArrays(1, &VertexArrayID); + glBindVertexArray(VertexArrayID); + glEnableVertexAttribArray(0); + + glGenBuffers(1, &VertexBufferID); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, VertexBufferID); + + glBufferData( + GL_SHADER_STORAGE_BUFFER, + vertex_buffer_data.size() * sizeof(GLfloat), + vertex_buffer_data.data(), + GL_STATIC_DRAW + ); + } GLuint QuadVertexArrayID; glGenVertexArrays(1, &QuadVertexArrayID); @@ -230,7 +143,7 @@ int main() { GLuint QuadVertexBufferID; glGenBuffers(1, &QuadVertexBufferID); glBindBuffer(GL_ARRAY_BUFFER, QuadVertexBufferID); - std::vector quad_buffer_data{ + const std::vector quad_buffer_data{ -1.f, 1.f, 0.f, 1.f, -1.f, -1.f, 0.f, 0.f, 1.f, -1.f, 1.f, 0.f, @@ -246,56 +159,67 @@ int main() { GL_STATIC_DRAW ); glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (void*)0); glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (void*)(2*sizeof(GLfloat))); - GLuint TextureShaderID = setupTextureShader(); - GLuint ScreenTextureID = glGetUniformLocation(TextureShaderID, "screen_texture"); - glUniform1i(ScreenTextureID, 0); + GraphicShader displayShader(R"( + #version 330 core + layout (location = 0) in vec2 screen_vertex; + layout (location = 1) in vec2 texture_vertex; + out vec2 TexCoords; + + void main() { + gl_Position = vec4(screen_vertex, 0.0, 1.0); + TexCoords = texture_vertex; + } + )", R"( + #version 330 core + out vec4 FragColor; + in vec2 TexCoords; + uniform sampler2D screen_texture; + + void main() { + FragColor = texture(screen_texture, TexCoords); + } + )"); + displayShader.setUniform("screen_texture", 0); auto lastFrame = std::chrono::high_resolution_clock::now(); do { - glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID); - // update particles at most 50 times per second if ( std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - lastFrame).count() >= 20 ) { - glUseProgram(ComputeShaderID); + auto guard = computeShader.use(); - glUniform2f(WorldID, world_width, world_height); - glDispatchCompute(particle_count, 1, 1); - - glUseProgram(0); + computeShader.setUniform("world", world_width, world_height); + computeShader.dispatch(particle_count); lastFrame = std::chrono::high_resolution_clock::now(); } - glUseProgram(ShaderID); - - glClear(GL_COLOR_BUFFER_BIT); + { + auto texGuard = textureBuffer->use(); + auto sdrGuard = sceneShader.use(); - glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]); + glClear(GL_COLOR_BUFFER_BIT); - glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr); - glDrawArrays(GL_POINTS, 0, 3*particle_count); + sceneShader.setUniform("MVP", MVP); - glUseProgram(0); - - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - glUseProgram(TextureShaderID); + glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr); + glDrawArrays(GL_POINTS, 0, 3*particle_count); + } - glBindTexture(GL_TEXTURE_2D, TextureID); - glBindBuffer(GL_ARRAY_BUFFER, QuadVertexBufferID); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (void*)0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (void*)(2*sizeof(GLfloat))); + { + auto guard = displayShader.use(); - glClear(GL_COLOR_BUFFER_BIT); - glDrawArrays(GL_TRIANGLES, 0, 6); + glBindTexture(GL_TEXTURE_2D, textureBuffer->getTexture()); + glBindBuffer(GL_ARRAY_BUFFER, QuadVertexBufferID); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (void*)0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (void*)(2*sizeof(GLfloat))); - glUseProgram(0); + glClear(GL_COLOR_BUFFER_BIT); + glDrawArrays(GL_TRIANGLES, 0, 6); + } glfwSwapBuffers(window); glfwPollEvents(); @@ -306,9 +230,6 @@ int main() { glDeleteBuffers(1, &VertexBufferID); glDisableVertexAttribArray(0); glDeleteVertexArrays(1, &VertexArrayID); - glDeleteProgram(ShaderID); - glDeleteProgram(ComputeShaderID); - glDeleteFramebuffers(1, &FramebufferID); glfwTerminate(); diff --git a/src/shader/compute.glsl b/src/shader/compute.glsl index 8b1deab..58bf08b 100644 --- a/src/shader/compute.glsl +++ b/src/shader/compute.glsl @@ -22,7 +22,7 @@ void main() { vec2 v = vec2(data[idx+0], data[idx+1]); if ( data[idx+2] < 5. && insideWorld(v) ) { - data[idx+0] += 0.01 * cos(v.x*cos(v.y)); + data[idx+0] += 0.01 * cos(v.x*sin(v.y)); data[idx+1] += 0.01 * sin(v.x-v.y); data[idx+2] += 0.01; } else { diff --git a/src/texture_buffer.h b/src/texture_buffer.h new file mode 100644 index 0000000..851833c --- /dev/null +++ b/src/texture_buffer.h @@ -0,0 +1,53 @@ +class TextureBuffer { +private: + GLuint _id; + GLuint _texture; + +public: + struct Guard { + const GLuint _id; + + Guard(GLuint id): _id(id) { + glBindFramebuffer(GL_FRAMEBUFFER, _id); + } + ~Guard() { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + }; + + Guard use() { + return Guard(_id); + } + + TextureBuffer(std::size_t width, std::size_t height) { + glGenFramebuffers(1, &_id); + + auto guard = use(); + + glGenTextures(1, &_texture); + glBindTexture(GL_TEXTURE_2D, _texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, (void*)0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0); + + if ( glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE ) { + std::cerr << "Texture framebuffer error" << std::endl; + } + } + + ~TextureBuffer() { + glDeleteFramebuffers(1, &_id); + } + + void resize(std::size_t width, std::size_t height) { + auto guard = use(); + + glViewport(0, 0, width, height); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, (void*)0); + } + + GLuint getTexture() const { + return _texture; + } +}; diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..ce8d012 --- /dev/null +++ b/src/util.h @@ -0,0 +1,44 @@ +#pragma once + +namespace util { + +GLint getUniform(GLuint program, const std::string& name) { + const GLint uniform = glGetUniformLocation(program, name.c_str()); + if ( uniform == -1 ) { + std::cerr << "Could not bind uniform " << name << std::endl; + } + return uniform; +} + +GLint compileShader(const std::string& source, GLenum type) { + GLint shader = glCreateShader(type); + + if ( !shader ) { + std::cerr << "Cannot create a shader of type " << type << std::endl; + exit(-1); + } + + const char* source_data = source.c_str(); + const int source_length = source.size(); + + glShaderSource(shader, 1, &source_data, &source_length); + glCompileShader(shader); + + GLint compiled; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if ( !compiled ) { + std::cerr << "Cannot compile shader" << std::endl; + GLint maxLength = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); + std::vector errorLog(maxLength); + glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]); + for( auto c : errorLog ) { + std::cerr << c; + } + std::cerr << std::endl; + } + + return shader; +} + +} -- cgit v1.2.3