From 153bb499a0ff5ada4a6225f956a3394b2e7c0da0 Mon Sep 17 00:00:00 2001 From: Joshua Moerman Date: Mon, 12 May 2014 15:29:38 +0200 Subject: [PATCH] Very clean render, with fbo pass, etc etc --- lib/app.cpp | 150 +++++++++++++++++--------- lib/app.hpp | 29 +++-- lib/fbo.hpp | 91 ++++++++++++++++ lib/renderbuffer.hpp | 57 ++++++++++ lib/texture.hpp | 62 +++++++++++ resources/{Fractal.fsh => Points.fsh} | 0 resources/{Fractal.vsh => Points.vsh} | 2 +- resources/Texture.fsh | 12 +++ resources/Texture.vsh | 11 ++ src/main.cpp | 7 +- 10 files changed, 356 insertions(+), 65 deletions(-) create mode 100644 lib/fbo.hpp create mode 100644 lib/renderbuffer.hpp create mode 100644 lib/texture.hpp rename resources/{Fractal.fsh => Points.fsh} (100%) rename resources/{Fractal.vsh => Points.vsh} (80%) create mode 100644 resources/Texture.fsh create mode 100644 resources/Texture.vsh diff --git a/lib/app.cpp b/lib/app.cpp index 4379e76..8f44be8 100644 --- a/lib/app.cpp +++ b/lib/app.cpp @@ -6,28 +6,33 @@ using namespace std; -static std::vector generate_random_points(size_t N){ - std::vector ret(N); - - const float d = 0.1; - for(auto& x : ret){ - x[0] = d * rand() / float(RAND_MAX); - x[1] = d * rand() / float(RAND_MAX); - x[2] = d * rand() / float(RAND_MAX); - } - - return ret; -} - -enum { - POS_ATTR, - TEX_ATTR +// implementation follow later +static moggle::shader_program create_shader_from_files(std::string vertex, std::string fragment); +template +static moggle::vbo create_vbo(std::vector const & data); +static moggle::vao create_vao(moggle::vbo const & vbo); +static moggle::vao create_vao(moggle::vbo const & vbo); +static GLuint create_gl_texture(GLsizei width, GLsizei height); +static std::vector generate_random_points(size_t N); +cl::Context create_shared_cl_context(GLContext & gl_context); + +std::vector quad = { + {{-1, -1}, {0, 0}}, + {{ 1, -1}, {1, 0}}, + {{-1, 1}, {0, 1}}, + {{ 1, 1}, {1, 1}}, }; +static const GLsizei FBO_SIZE = 1024; + App::App(GLContext& gl_context) : gl_vbo(create_vbo(generate_random_points(N))) , gl_vao(create_vao(gl_vbo)) -, gl_program(create_shader_from_files("Fractal.vsh", "Fractal.fsh")) +, gl_quad_vbo(create_vbo(quad)) +, gl_quad_vao(create_vao(gl_quad_vbo)) +, gl_points_program(create_shader_from_files("Points.vsh", "Points.fsh")) +, gl_texture_program(create_shader_from_files("Texture.vsh", "Texture.fsh")) +, gl_fbo(moggle::fbo::create_default(FBO_SIZE, FBO_SIZE)) , cl_context(create_shared_cl_context(gl_context)) , cl_device(cl_context.getInfo().front()) , cl_queue(cl_context, cl_device) @@ -40,35 +45,68 @@ App::App(GLContext& gl_context) moggle::gl::enable(GL_BLEND); moggle::gl::blend_function(GL_ONE, GL_ONE); moggle::gl::blend_equation(GL_FUNC_ADD); + + moggle::gl::active_texture(GL_TEXTURE0); + moggle::gl::texture_parameter_i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + moggle::gl::texture_parameter_i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } -void App::draw(){ +void App::draw(std::function bind, std::function flush){ auto dt = 1/60.0; time += dt; - if(wait > 0) wait -= dt; - - if(wait <= 0){ - k_update(cl_queue, N, p_s, p_r, p_b, p_dt, cl_buffer); - } + // OpenCL update + k_update(cl_queue, N, p_s, p_r, p_b, p_dt, cl_buffer); cl::checky(cl_queue.finish()); - moggle::gl::clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // OpenGL to fbo + gl_fbo.bind(); + moggle::gl::viewport(0, 0, FBO_SIZE, FBO_SIZE); - gl_program.use(); + gl_points_program.use(); gl_vao.bind(); - moggle::gl::draw_arrays(GL_POINTS, 0, N); + + // Draw fbo + bind(); + moggle::gl::clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + gl_texture_program.use(); + + gl_fbo.get_texture().bind(GL_TEXTURE_2D); + gl_texture_program.uniform("tex").set(0); + gl_texture_program.uniform("maxf").set(maxf); + + gl_quad_vao.bind(); + moggle::gl::draw_arrays(GL_TRIANGLE_STRIP, 0, 4); + + flush(); } void App::resize(size_t w, size_t h){ - wait = 5; - W = w; H = h; } -moggle::shader_program App::create_shader_from_files(string vertex, string fragment){ +cl::Program App::create_cl_program(string file){ + cl_int err = 0; + + // build the program + cl::Program cl_program(cl_context, slurp(file), true, &err); + cout << cl_program.getBuildInfo(cl_device) << std::endl; + + cl::checky(err); + cout << cl_program << endl; + + return cl_program; +} + +enum { + POS_ATTR, + TEX_ATTR +}; + +moggle::shader_program create_shader_from_files(string vertex, string fragment){ moggle::shader_program program; auto vs = moggle::shader::from_file(moggle::shader_type::vertex, vertex); @@ -78,20 +116,22 @@ moggle::shader_program App::create_shader_from_files(string vertex, string fragm program.attach(fs); program.bind_attribute(POS_ATTR, "position"); + program.bind_attribute(TEX_ATTR, "tex_coord"); program.link(); return program; } -moggle::vbo App::create_vbo(const std::vector& data){ - moggle::vbo vbo; +template +moggle::vbo create_vbo(const std::vector& data){ + moggle::vbo vbo; vbo.bind(GL_ARRAY_BUFFER); vbo.data(data); return vbo; } -moggle::vao App::create_vao(const moggle::vbo& vbo){ +moggle::vao create_vao(const moggle::vbo& vbo){ moggle::vao vao; vao.bind(); @@ -102,18 +142,41 @@ moggle::vao App::create_vao(const moggle::vbo& vbo){ return vao; } -GLuint App::create_gl_texture(GLsizei width, GLsizei height){ +moggle::vao create_vao(const moggle::vbo& vbo){ + moggle::vao vao; + vao.bind(); + + moggle::gl::enable_vertex_attribute_array(POS_ATTR); + moggle::gl::enable_vertex_attribute_array(TEX_ATTR); + + vao.attribute(POS_ATTR, vbo, &Vertex::pos); + vao.attribute(TEX_ATTR, vbo, &Vertex::tex); + + return vao; +} + +GLuint create_gl_texture(GLsizei width, GLsizei height){ GLuint tex = 0; - moggle::gl::active_texture(GL_TEXTURE0); moggle::gl::generate_textures(1, &tex); moggle::gl::bind_texture(GL_TEXTURE_2D, tex); moggle::gl::texture_image_2d(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - moggle::gl::texture_parameter_i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - moggle::gl::texture_parameter_i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); return tex; } -cl::Context App::create_shared_cl_context(GLContext& gl_context){ +static std::vector generate_random_points(size_t N){ + std::vector ret(N); + + const auto d = 2.0; + for(auto& x : ret){ + x[0] = d * rand() / float(RAND_MAX) - 0.5*d; + x[1] = d * rand() / float(RAND_MAX) - 0.5*d; + x[2] = d * rand() / float(RAND_MAX) - 0.5*d; + } + + return ret; +} + +cl::Context create_shared_cl_context(GLContext& gl_context){ CGLShareGroupObj sharegroup = CGLGetShareGroup(gl_context.ctx); cl_context_properties properties[] = { @@ -127,16 +190,3 @@ cl::Context App::create_shared_cl_context(GLContext& gl_context){ return cl_context; } - -cl::Program App::create_cl_program(string file){ - cl_int err = 0; - - // build the program - cl::Program cl_program(cl_context, slurp(file), true, &err); - cout << cl_program.getBuildInfo(cl_device) << std::endl; - - cl::checky(err); - cout << cl_program << endl; - - return cl_program; -} diff --git a/lib/app.hpp b/lib/app.hpp index 7e593a8..e24be02 100644 --- a/lib/app.hpp +++ b/lib/app.hpp @@ -1,5 +1,6 @@ #pragma once +#include "fbo.hpp" #include #include #include @@ -8,27 +9,38 @@ #include #include -using Vertex = moggle::hvector4; +// Used for the bare points +using GLPoint = moggle::hvector4; + +// Used for rendering a textured quad +struct Vertex { + moggle::vector2 pos; + moggle::vector2 tex; +}; struct App { size_t W = 128; size_t H = 128; - size_t N = 1 << 20; + size_t N = 1 << 18; float time = 0; - float wait = 5; float p_s = 10.962073; float p_r = 31.211250; float p_b = 1.537946; float p_dt = 0.013040; + float maxf = 500; App(GLContext & gl_context); - void draw(); + void draw(std::function bind, std::function flush); void resize(size_t w, size_t h); private: - moggle::vbo gl_vbo; + moggle::vbo gl_vbo; moggle::vao gl_vao; - moggle::shader_program gl_program; + moggle::vbo gl_quad_vbo; + moggle::vao gl_quad_vao; + moggle::shader_program gl_points_program; + moggle::shader_program gl_texture_program; + moggle::fbo gl_fbo; cl::Context cl_context; cl::Device cl_device; @@ -37,10 +49,5 @@ private: cl::Program cl_program; KernelOp k_update; - static moggle::shader_program create_shader_from_files(std::string vertex, std::string fragment); - static moggle::vbo create_vbo(std::vector const & data); - static moggle::vao create_vao(moggle::vbo const & vbo); - static GLuint create_gl_texture(GLsizei width, GLsizei height); - static cl::Context create_shared_cl_context(GLContext & gl_context); cl::Program create_cl_program(std::string file); }; diff --git a/lib/fbo.hpp b/lib/fbo.hpp new file mode 100644 index 0000000..4debf14 --- /dev/null +++ b/lib/fbo.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include +#include "renderbuffer.hpp" +#include "texture.hpp" + +#include + +/* + * One can attach either an external renderbuffer/texture, or move one into fbo + * In the latter case, the fbo will own the renderbuffer/texture + * + * Attachment can be one of: GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, or GL_STENCIL_ATTACHMENT + */ + +namespace moggle { + +struct fbo { + explicit fbo(bool create_now = false) { + if(create_now) create(); + } + + ~fbo() { destroy(); } + + fbo(fbo const &) = delete; + fbo & operator=(fbo const&) = delete; + + fbo(fbo && r) : id(r.id) { r.id = 0; } + fbo & operator=(fbo && r) { std::swap(id, r.id); return *this; } + + bool created() const { return id; } + explicit operator bool() const { return created(); } + + void create(){ if(!id) gl::generate_framebuffers(1, &id); } + void destroy(){ gl::delete_renderbuffers(1, &id); id = 0; } + + void bind() { + create(); + gl::bind_framebuffer(GL_FRAMEBUFFER, id); + } + + GLuint get_id() const { + return id; + } + + // Only well defined if the fbo owns the texture + texture & get_texture() { + return textures.front(); + } + + void attach(GLenum attachment, renderbuffer const & rb){ + bind(); + gl::framebuffer_renderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, rb.get_id()); + } + + void attach(GLenum attachment, renderbuffer && rb){ + renderbuffers.push_back(std::move(rb)); + attach(attachment, renderbuffers.back()); + } + + void attach(GLenum attachment, GLenum textarget, texture const & t){ + bind(); + gl::framebuffer_texture_2d(GL_FRAMEBUFFER, attachment, textarget, t.get_id(), 0); + } + + void attach(GLenum attachment, GLenum textarget, texture && t){ + textures.push_back(std::move(t)); + attach(attachment, textarget, textures.back()); + } + + void clear(GLenum bufferbits){ + bind(); + gl::clear(bufferbits); + } + + static fbo create_default(GLsizei width, GLsizei height){ + fbo f; + f.attach(GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture::create(width, height, GL_RGBA32F)); + f.attach(GL_DEPTH_ATTACHMENT, renderbuffer::create(width, height, 1, GL_DEPTH_COMPONENT16)); + f.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + return f; + } + +private: + std::vector renderbuffers; + std::vector textures; + + GLuint id = 0; +}; + +} diff --git a/lib/renderbuffer.hpp b/lib/renderbuffer.hpp new file mode 100644 index 0000000..857b098 --- /dev/null +++ b/lib/renderbuffer.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include + +namespace moggle { + +struct renderbuffer { + explicit renderbuffer(bool create_now = false) { + if(create_now) create(); + } + + ~renderbuffer() { destroy(); } + + renderbuffer(renderbuffer const &) = delete; + renderbuffer & operator=(renderbuffer const&) = delete; + + renderbuffer(renderbuffer && r) : id(r.id) { r.id = 0; } + renderbuffer & operator=(renderbuffer && r) { std::swap(id, r.id); return *this; } + + bool created() const { return id; } + explicit operator bool() const { return created(); } + + void create(){ if(!id) gl::generate_renderbuffers(1, &id); } + void destroy(){ gl::delete_renderbuffers(1, &id); id = 0; } + + void bind() { + create(); + gl::bind_renderbuffer(GL_RENDERBUFFER, id); + } + + GLuint get_id() const { + return id; + } + + void storage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height){ + bind(); + gl::renderbuffer_storage(target, internalformat, width, height); + } + + void storage_multisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height){ + bind(); + // TODO: wrap this call + glRenderbufferStorageMultisample(target, samples, internalformat, width, height); + } + + static renderbuffer create(GLsizei width, GLsizei height, GLsizei samples = 1, GLenum format = GL_RGBA4){ + renderbuffer r; + if(samples <= 1) r.storage(GL_RENDERBUFFER, format, width, height); + else r.storage_multisample(GL_RENDERBUFFER, samples, format, width, height); + return r; + } + +private: + GLuint id = 0; +}; + +} diff --git a/lib/texture.hpp b/lib/texture.hpp new file mode 100644 index 0000000..dc457f4 --- /dev/null +++ b/lib/texture.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include +#include + +/* + * NOTE: + * in glTexImage2D, border MUST be 0, so this parameter is left out in this interface. + */ + +namespace moggle { + +struct texture { + explicit texture(bool create_now = false) { + if(create_now) create(); + } + + ~texture() { destroy(); } + + texture(texture const &) = delete; + texture & operator=(texture const&) = delete; + + texture(texture && t) : id(t.id) { t.id = 0; } + texture & operator=(texture && t) { std::swap(id, t.id); return *this; } + + bool created() const { return id; } + explicit operator bool() const { return created(); } + + void create(){ if(!id) gl::generate_textures(1, &id); } + void destroy(){ gl::delete_textures(1, &id); id = 0; } + + void bind(GLenum target) { + create(); + gl::bind_texture(target, id); + } + + GLuint get_id() const { return id; } + + template + void image_2d(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLenum format, const T * data){ + bind(target); + auto type = gl_type_traits::gl_constant; + gl::texture_image_2d(target, level, internalformat, width, height, 0, format, type, data); + } + + void image_2d(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, std::nullptr_t){ + bind(target); + gl::texture_image_2d(target, level, internalformat, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + } + + //! creates empty texture + static texture create(GLsizei width, GLsizei height, GLint internalformat = GL_RGBA, GLenum target = GL_TEXTURE_2D){ + texture t; + t.image_2d(target, 0, internalformat, width, height, nullptr); + return t; + } + +private: + GLuint id = 0; +}; + +} diff --git a/resources/Fractal.fsh b/resources/Points.fsh similarity index 100% rename from resources/Fractal.fsh rename to resources/Points.fsh diff --git a/resources/Fractal.vsh b/resources/Points.vsh similarity index 80% rename from resources/Fractal.vsh rename to resources/Points.vsh index f3fcd54..e6ac4cb 100644 --- a/resources/Fractal.vsh +++ b/resources/Points.vsh @@ -6,5 +6,5 @@ out float mx; void main(){ gl_Position = vec4(1); gl_Position.xy = 0.04*position.xy; - mx = cos(0.1*position.z); + mx = cos(0.1337*position.z); } diff --git a/resources/Texture.fsh b/resources/Texture.fsh new file mode 100644 index 0000000..142d627 --- /dev/null +++ b/resources/Texture.fsh @@ -0,0 +1,12 @@ +#version 330 + +uniform sampler2D tex; +uniform float maxf; + +in vec2 tex_coord_; + +out vec4 fragColor; + +void main(){ + fragColor = sqrt(texture(tex, tex_coord_) / maxf); +} diff --git a/resources/Texture.vsh b/resources/Texture.vsh new file mode 100644 index 0000000..b7a3d9a --- /dev/null +++ b/resources/Texture.vsh @@ -0,0 +1,11 @@ +#version 330 + +in vec4 position; +in vec2 tex_coord; + +out vec2 tex_coord_; + +void main(){ + gl_Position = position; + tex_coord_ = tex_coord; +} diff --git a/src/main.cpp b/src/main.cpp index 3efc092..5ff27de 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,8 +14,8 @@ int main() { [&](ContextParameters ctxp){ a = make_unique(ctxp.context); }, - [&](ContextParameters){ - a->draw(); + [&](ContextParameters ctxp){ + a->draw(ctxp.bind_framebuffer, ctxp.flush_drawable); }, [&](ContextParameters, CGFloat w, CGFloat h){ a->resize(size_t(floor(w)), size_t(floor(h))); @@ -30,7 +30,8 @@ int main() { {"s", 10.962073, 9, 13, [&](double x){ a->p_s = x; }}, {"r", 31.211250, 30, 32, [&](double x){ a->p_r = x; }}, {"b", 1.537946, 0.0001, 3, [&](double x){ a->p_b = x; }}, - {"dt", 0.013040, 0.0001, 0.03, [&](double x){ a->p_dt = x; }} + {"dt", 0.013040, 0.0001, 0.03, [&](double x){ a->p_dt = x; }}, + {"max", 500, 0.1, 50000, [&](double x){ a->maxf = x; }}, }}); app.run();