diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ddef65834fa2f84777f6dcd44f0c20277dc4fd0..7c49904490d91dec41a2673a0ad8e18e481ccdeb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ project(scigl_render) cmake_minimum_required(VERSION 2.8.3) +set (CMAKE_CXX_STANDARD 11) # using conan to manage dependencies if(CONAN_EXPORTED) # when packaging @@ -26,6 +27,7 @@ add_library(scigl_render src/gl_context.cpp src/buffer/frame_buffer.cpp src/buffer/frame_buffer_reader.cpp + src/buffer/texture2d.cpp src/render/depth_simulator.cpp src/render/onscreen_render.cpp src/render/rasterizer.cpp diff --git a/include/scigl_render/buffer/frame_buffer.hpp b/include/scigl_render/buffer/frame_buffer.hpp index 203a0a4520258fb6b41c395191843a6083554406..5caf8b0eb8b58aebd9bab4d904c3108b5448bcaa 100644 --- a/include/scigl_render/buffer/frame_buffer.hpp +++ b/include/scigl_render/buffer/frame_buffer.hpp @@ -1,5 +1,7 @@ #pragma once #include <GL/gl3w.h> +#include <memory> +#include <scigl_render/buffer/texture2d.hpp> namespace scigl_render { @@ -15,11 +17,9 @@ public: /*! Creates the framebuffer with a rednerbuffer attachment to render to. \throws a runtime_error on failure - \param width of the texture - \param height of the texture - \param internal_format color components & size of each + \param texture the texture to render to */ - FrameBuffer(int width, int height, GLenum internal_format); + FrameBuffer(std::shared_ptr<Texture2D> texture); FrameBuffer(const FrameBuffer &) = delete; ~FrameBuffer(); @@ -41,19 +41,17 @@ public: */ void clear(float color = 0, float depth = 1, int stencil = 0) const; - /** returns the texture used for rendering */ - GLuint get_texture() const; + /** returns the render target texture */ + std::shared_ptr<Texture2D> get_texture() const; /** gets the maximum rbo size */ static int get_max_size(); private: - // Parameterization - int width, height; // framebuffer object to render the texture into GLuint fbo; // texture for renderng colors, can be sampled from - GLuint color_tex; + std::shared_ptr<Texture2D> color_tex; // renderbuffer object for the depth testing, does not allow sampling GLuint depth_stencil_rbo; }; diff --git a/include/scigl_render/buffer/frame_buffer_reader.hpp b/include/scigl_render/buffer/frame_buffer_reader.hpp index b13ade42e085d41968bda87fb0018e948dd8cb9f..88777dcf11294fca4fc00d77a1c0ff0b1f5724e3 100644 --- a/include/scigl_render/buffer/frame_buffer_reader.hpp +++ b/include/scigl_render/buffer/frame_buffer_reader.hpp @@ -18,20 +18,17 @@ class FramebufferReader public: /*! Renders the scene off-screen and calculates the depth values. - \param width number of image columns - \param height number of image rows + \param buffer read from this framebuffer \param pixel_size the size of each pixel: number_channels * sizeof(type) */ - FramebufferReader(int width, int height, size_t pixel_size); + FramebufferReader(std::shared_ptr<FrameBuffer> buffer, + size_t pixel_size); /*! Starts reading from the FBO to the backbuffer PBO. This operation is asynchronous. - \param format specifies the channels, for example GL_RGB - \param type primitive to store, for example GL_UNSIGNED_BYTE - \param buffer transfer data from this FBO to the PBO */ - void start_read(GLenum format, GLenum type, const FrameBuffer& buffer) const; + void start_read() const; /*! Reads synchronously from the frontbuffer PBO. Operate on this data while @@ -49,8 +46,7 @@ public: void swap_buffers(); private: - int width; - int height; + std::shared_ptr<FrameBuffer> framebuffer; // Two pbos one to read to the other one to map form, alternating std::array<GLuint, 2> pbos; // transfer from fbo to pbo via glReadPixels diff --git a/include/scigl_render/buffer/texture2d.hpp b/include/scigl_render/buffer/texture2d.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f0abbf72c1f132e28cb17aa93c63cfa7602f864d --- /dev/null +++ b/include/scigl_render/buffer/texture2d.hpp @@ -0,0 +1,39 @@ +#pragma once +#include <GL/gl3w.h> + +namespace scigl_render +{ +/** RAII style creation of a 2D texture */ +class Texture2D +{ +public: + Texture2D(GLsizei width, GLsizei height, + GLenum format, GLenum internal_format, GLenum type); + Texture2D(const Texture2D &) = delete; + ~Texture2D(); + + /** activates texture_n for use in shaders */ + static void activate(GLenum texture_n); + + /** binds this texture to GL_TEXTURE_2D */ + void bind() const; + + /** unbinds this texture */ + void unbind() const; + + /** upload an image to the texture. */ + void store_image(const GLvoid *image) const; + + GLuint get_raw() const; + GLsizei get_width() const; + GLsizei get_height() const; + GLenum get_format() const; + GLenum get_internal_format() const; + GLenum get_type() const; + +private: + const GLsizei width, height; + const GLenum format, internal_format, type; + GLuint texture; +}; +} // namespace scigl_render \ No newline at end of file diff --git a/include/scigl_render/example/depth_offscreen_render.hpp b/include/scigl_render/example/depth_offscreen_render.hpp index 3dc3da51fd7151c8ab26b56615058ed19cde33c5..13959a295a01363f2b7838a8a084428b21184f5b 100644 --- a/include/scigl_render/example/depth_offscreen_render.hpp +++ b/include/scigl_render/example/depth_offscreen_render.hpp @@ -22,17 +22,14 @@ class DepthOffscreenRender public: /*! Configures the rendering environment and loads the models. - \param context the environment to configure - \param shader how to render the scene - \param tex_format format parameter of glTexImage2D - \param tex_type type parameter of glTexImage2D - \param tex_internal_format internal_format parameter of glTexImage2D + \param context the environment to render to (swap Buffers) + \param texture parametrized for offscreen rendering \param pixel_size the size of one pixel. Must match the internal format: number_of_channels * sizeof(type) */ - DepthOffscreenRender( - std::shared_ptr<GLContext> context, GLenum tex_format, GLenum tex_type, - GLint tex_internal_format, size_t pixel_size); + DepthOffscreenRender(std::shared_ptr<GLContext> context, + std::shared_ptr<Texture2D>, + size_t pixel_size); /*! Continues to render the next frame. @@ -41,26 +38,20 @@ public: const CartesianPose &model_pose, const CartesianPose &camera_pose); - /*! - Resizes the buffers to the screen size of the GLContext. - */ - void resize_buffers(); - private: // parameters for rendering - GLenum texture_format; - GLenum texture_type; - GLenum texture_internal_format; - size_t pixel_size; std::shared_ptr<GLContext> gl_context; - // Render targets - FrameBuffer framebuffer; - FramebufferReader fbo_reader; + // render to fbo + std::shared_ptr<Texture2D> fbo_texture; + std::shared_ptr<FrameBuffer> framebuffer; + std::unique_ptr<FramebufferReader> fbo_reader; + // display fullscreen quad from other texture (to showcase the mapping) TextureFullscreenRender texture_render; - // buffer for reading from the offscreen render - std::vector<unsigned char> image_buffer; + std::shared_ptr<Texture2D> quad_texture; // display different views Rasterizer rasterizer; + // CPU memory buffer for reading from the offscreen render + std::vector<uint8_t> image_buffer; /*! Display the data from the framebuffer diff --git a/include/scigl_render/render/texture_fullscreen_render.hpp b/include/scigl_render/render/texture_fullscreen_render.hpp index 8d0599bd97146f2adf340a09ecdfbd23301c1239..bfe4deb7b95fa4b864b6c7bd32d5c0bc60ba3caf 100644 --- a/include/scigl_render/render/texture_fullscreen_render.hpp +++ b/include/scigl_render/render/texture_fullscreen_render.hpp @@ -1,4 +1,6 @@ #pragma once +#include <memory> +#include <scigl_render/buffer/texture2d.hpp> #include <scigl_render/shader/shader.hpp> namespace scigl_render @@ -14,24 +16,15 @@ public: Create the renderer with a texture storage with the given format. \param internal_format the internal format used in glTexStorage2D */ - TextureFullscreenRender(int width, int height, - GLint internal_format = GL_RGB8); + TextureFullscreenRender(); - /*! - Draws the pixels into the currently selected framebuffer. This function - changes glDepthFunc(GL_LEQUAL) so the quad can be seen (z = 1.0). - \param pixels the pointer to the lower left pixel of the image - \param format format of a pixel for example GL_RGB - \param type the type of the storage for example GL_UNSIGNED_BYTE + /** + * Draws the contents of the texture to a fullscreen quad. + * Changes glDepthFunc(GL_LEQUAL) so the quad can be seen (z = 1.0). */ - void draw(const GLvoid *pixels, GLenum format, GLenum type); + void draw(std::shared_ptr<Texture2D> texture) const; private: - // store the texture here for rendering - GLuint texture; - // properties of the image - GLsizei width; - GLsizei height; // Dummy VAO since the geometry shader creates the vertices GLuint vao; Shader shader; diff --git a/src/buffer/frame_buffer.cpp b/src/buffer/frame_buffer.cpp index aff3fff9f3244b39ab7d4f0e0a3fd55227497eb5..15f70f6987d900d1608ea921b76240cdaf3689aa 100644 --- a/src/buffer/frame_buffer.cpp +++ b/src/buffer/frame_buffer.cpp @@ -3,21 +3,21 @@ namespace scigl_render { -FrameBuffer::FrameBuffer(int width, int height, GLenum internal_format) +FrameBuffer::FrameBuffer(std::shared_ptr<Texture2D> texture) + : color_tex(texture) { - this->width = width; - this->height = height; // Create framebuffer with renderbuffer attachements glGenFramebuffers(1, &fbo); activate(); - glGenTextures(1, &color_tex); - glBindTexture(GL_TEXTURE_2D, color_tex); - glTexStorage2D(GL_TEXTURE_2D, 1, internal_format, width, height); + color_tex->bind(); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, color_tex, 0); + GL_TEXTURE_2D, color_tex->get_raw(), 0); + color_tex->unbind(); glGenRenderbuffers(1, &depth_stencil_rbo); glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil_rbo); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); + glRenderbufferStorage( + GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, + color_tex->get_width(), color_tex->get_height()); glBindRenderbuffer(GL_RENDERBUFFER, 0); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depth_stencil_rbo); @@ -33,7 +33,6 @@ FrameBuffer::~FrameBuffer() { glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteRenderbuffers(1, &depth_stencil_rbo); - glDeleteTextures(1, &color_tex); } void FrameBuffer::activate() const @@ -56,7 +55,7 @@ void FrameBuffer::clear(float color, float depth, int stencil) const deactivate(); } -GLuint FrameBuffer::get_texture() const +std::shared_ptr<Texture2D> FrameBuffer::get_texture() const { return color_tex; } diff --git a/src/buffer/frame_buffer_reader.cpp b/src/buffer/frame_buffer_reader.cpp index 9221e49ce65d8631ea32ab12c34ea3fcbbdd27aa..9029ad53361c4cbf0eebfac180017c7b127b1525 100644 --- a/src/buffer/frame_buffer_reader.cpp +++ b/src/buffer/frame_buffer_reader.cpp @@ -5,30 +5,38 @@ namespace scigl_render { -FramebufferReader::FramebufferReader(int width, int height, size_t pixel_size) - : width(width), height(height) +FramebufferReader::FramebufferReader(std::shared_ptr<FrameBuffer> buffer, + size_t pixel_size) + : framebuffer(buffer) { glGenBuffers(pbos.size(), pbos.data()); + size_t buffer_size = framebuffer->get_texture()->get_width() * + framebuffer->get_texture()->get_height() * + pixel_size; for (size_t i = 0; i < pbos.size(); i++) { glBindBuffer(GL_PIXEL_PACK_BUFFER, pbos[i]); // constantly reading new contents - glBufferData(GL_PIXEL_PACK_BUFFER, width * height * pixel_size, 0, + glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, 0, GL_DYNAMIC_READ); } glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); check_gl_error("framebuffer-reader created pbos"); } -void FramebufferReader::start_read(GLenum format, GLenum type, - const FrameBuffer &buffer) const +void FramebufferReader::start_read() const { - buffer.activate(); + framebuffer->activate(); glReadBuffer(GL_COLOR_ATTACHMENT0); glBindBuffer(GL_PIXEL_PACK_BUFFER, pbos[backbuffer_index]); - glReadPixels(0, 0, width, height, format, type, nullptr); + glReadPixels(0, 0, + framebuffer->get_texture()->get_width(), + framebuffer->get_texture()->get_height(), + framebuffer->get_texture()->get_format(), + framebuffer->get_texture()->get_type(), + nullptr); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - buffer.deactivate(); + framebuffer->deactivate(); } void *FramebufferReader::do_read() const diff --git a/src/buffer/texture2d.cpp b/src/buffer/texture2d.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a8205224edf0917d5f5a5c6e4f57c9c2c572cef7 --- /dev/null +++ b/src/buffer/texture2d.cpp @@ -0,0 +1,69 @@ +#include <scigl_render/buffer/texture2d.hpp> + +namespace scigl_render +{ +Texture2D::Texture2D(GLsizei width, GLsizei height, + GLenum format, GLenum internal_format, GLenum type) + : width(width), height(height), + format(format), internal_format(internal_format), + type(type) +{ + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexStorage2D(GL_TEXTURE_2D, 1, internal_format, width, height); + glBindTexture(GL_TEXTURE_2D, 0); +} + +Texture2D::~Texture2D() +{ + glDeleteTextures(1, &texture); +} + +GLuint Texture2D::get_raw() const +{ + return texture; +} + +GLsizei Texture2D::get_width() const +{ + return width; +} +GLsizei Texture2D::get_height() const +{ + return height; +} +GLenum Texture2D::get_format() const +{ + return format; +} +GLenum Texture2D::get_internal_format() const +{ + return internal_format; +} +GLenum Texture2D::get_type() const +{ + return type; +} + +void Texture2D::activate(GLenum texture_n) +{ + glActiveTexture(texture_n); +} + +void Texture2D::bind() const +{ + glBindTexture(GL_TEXTURE_2D, texture); +} + +void Texture2D::unbind() const +{ + glBindTexture(GL_TEXTURE_2D, 0); +} + +void Texture2D::store_image(const GLvoid *image) const +{ + bind(); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, image); + unbind(); +} +} // namespace scigl_render diff --git a/src/example/depth_offscreen_render.cpp b/src/example/depth_offscreen_render.cpp index ca9ee446d4ff5f7dcd243e89d77ae67d27b1606e..a32655a8b506e09517329a86c6e9155043224a10 100644 --- a/src/example/depth_offscreen_render.cpp +++ b/src/example/depth_offscreen_render.cpp @@ -4,31 +4,24 @@ namespace scigl_render { // I know there is a lot of configuration but it is intendet to be flexible -DepthOffscreenRender::DepthOffscreenRender( - std::shared_ptr<GLContext> context, - GLenum tex_format, GLenum tex_type, GLint tex_internal_format, - size_t pixel_size) - : texture_format(tex_format), - texture_type(tex_type), - texture_internal_format(tex_internal_format), - pixel_size(pixel_size), - gl_context(context), - framebuffer(gl_context->get_width(), - gl_context->get_height(), - tex_internal_format), - fbo_reader(gl_context->get_width(), - gl_context->get_height(), - pixel_size), - texture_render(gl_context->get_width(), - gl_context->get_height(), - tex_internal_format), - image_buffer(gl_context->get_width() * gl_context->get_height() * +DepthOffscreenRender::DepthOffscreenRender(std::shared_ptr<GLContext> context, + std::shared_ptr<Texture2D> texture, + size_t pixel_size) + : rasterizer(context->get_width() / 2, context->get_height() / 2, + 2, 2), + image_buffer(context->get_width() * context->get_height() * pixel_size), - rasterizer(gl_context->get_width() / 2, - gl_context->get_height() / 2, - 2, 2) + gl_context(context), + fbo_texture(texture) { - check_gl_error("example render created"); + framebuffer = std::make_shared<FrameBuffer>(fbo_texture); + fbo_reader = std::unique_ptr<FramebufferReader>( + new FramebufferReader(framebuffer, pixel_size)); + // create second texture to copy the data to + quad_texture = std::make_shared<Texture2D>( + fbo_texture->get_width(), fbo_texture->get_height(), + fbo_texture->get_format(), fbo_texture->get_internal_format(), + fbo_texture->get_type()); } void DepthOffscreenRender::next_frame( @@ -39,10 +32,9 @@ void DepthOffscreenRender::next_frame( using namespace std::placeholders; check_gl_error("next frame begin"); // render to cleared fbo - framebuffer.clear(); - framebuffer.activate(); + framebuffer->clear(); + framebuffer->activate(); check_gl_error("activated fbo"); - // render 4 poses via rasterization for (int i = 0; i < 4; i++) { @@ -54,16 +46,18 @@ void DepthOffscreenRender::next_frame( } // back to default rasterizer.activate_all(); - framebuffer.deactivate(); + framebuffer->deactivate(); // read data from fbo - fbo_reader.start_read(texture_format, texture_type, framebuffer); + fbo_reader->start_read(); check_gl_error("asynchronous reading to pbo"); - auto data = fbo_reader.do_read(); + auto data = fbo_reader->do_read(); check_gl_error("synchronous reading from pbo"); + + // display the data on screen display_data(data); - fbo_reader.end_read(); - fbo_reader.swap_buffers(); + fbo_reader->end_read(); + fbo_reader->swap_buffers(); check_gl_error("end synchronous reading"); } @@ -71,30 +65,13 @@ void DepthOffscreenRender::display_data(const void *data) { // Test copying the data, buffer_size is in bytes memcpy(image_buffer.data(), data, image_buffer.size()); + quad_texture->store_image(data); // draw the copy glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - texture_render.draw(image_buffer.data(), texture_format, texture_type); + texture_render.draw(quad_texture); check_gl_error("drawing fullscreen quad"); // update glfwSwapBuffers(gl_context->get_window()); } - -void DepthOffscreenRender::resize_buffers() -{ - framebuffer = FrameBuffer( - gl_context->get_width(), - gl_context->get_height(), - texture_internal_format); - fbo_reader = FramebufferReader( - gl_context->get_width(), - gl_context->get_height(), - pixel_size); - texture_render = TextureFullscreenRender( - gl_context->get_width(), - gl_context->get_height(), - texture_internal_format), - image_buffer = std::vector<unsigned char>( - gl_context->get_width() * gl_context->get_height() * pixel_size); -} } // namespace scigl_render \ No newline at end of file diff --git a/src/example/scigl_depth_viewer.cpp b/src/example/scigl_depth_viewer.cpp index a118dbfdea0cec012120f5d68b4b7a509a30a1e7..1114f92191374e9820ac3e39cf14bb7b864ab2c0 100644 --- a/src/example/scigl_depth_viewer.cpp +++ b/src/example/scigl_depth_viewer.cpp @@ -39,11 +39,12 @@ int main(int argc, char *argv[]) "No model file provided. Run as */scigl_viwer <model_filename>!"); } // Setup renderer creates context - std::shared_ptr<GLContext> context = - std::make_shared<GLContext>(true, false, WIDTH, HEIGHT); + auto context = std::make_shared<GLContext>(true, false, WIDTH, HEIGHT); check_gl_error("Context created"); - DepthOffscreenRender render(context, GL_RED, GL_FLOAT, GL_R32F, - sizeof(float)); + auto texture = std::make_shared<Texture2D>( + WIDTH, HEIGHT, GL_RED, GL_R32F, GL_FLOAT); + check_gl_error("texture created"); + DepthOffscreenRender render(context, texture, sizeof(float)); // Intrinsics of my shitty webcam CameraIntrinsics camera_intrinsics; camera_intrinsics.width = 640; diff --git a/src/render/texture_fullscreen_render.cpp b/src/render/texture_fullscreen_render.cpp index 5c485f97c313d4b9e27913287b9378c648afc6c0..92358c1bfba6bcd708a7273cfaa2329827f41270 100644 --- a/src/render/texture_fullscreen_render.cpp +++ b/src/render/texture_fullscreen_render.cpp @@ -24,20 +24,13 @@ void main() color = texture(texture0, texture_coordinate); })"; -TextureFullscreenRender::TextureFullscreenRender( - int width, int height, GLint internal_format) - : width(width), height(height) +TextureFullscreenRender::TextureFullscreenRender() { ShaderBuilder shader_builder; shader_builder.attach_vertex_shader(vertex_source); shader_builder.attach_geometry_shader(geometry_source); shader_builder.attach_fragment_shader(fragment_source); shader = shader_builder.build(); - // create the texture buffer - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - glTexStorage2D(GL_TEXTURE_2D, 1, internal_format, width, height); - glBindTexture(GL_TEXTURE_2D, 0); // create dummy VAO glGenVertexArrays(1, &vao); glBindVertexArray(vao); @@ -45,23 +38,20 @@ TextureFullscreenRender::TextureFullscreenRender( check_gl_error("TextureFullscreenRender created"); } -void TextureFullscreenRender::draw( - const GLvoid *pixels, GLenum format, GLenum type) +void TextureFullscreenRender::draw(std::shared_ptr<Texture2D> texture) const { // With GL_LESS the cleared buffer (z = 1.0) would always win glDepthFunc(GL_LEQUAL); - // activate the shader and texure + shader.activate(); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texture); - // copy data to OpenGL - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, pixels); - // draw the image + texture->bind(); + Texture2D::activate(GL_TEXTURE0); shader.setInt("texture0", 0); glBindVertexArray(vao); glDrawArrays(GL_POINTS, 0, 1); // unbind the resources - glBindTexture(GL_TEXTURE_2D, 0); + shader.deactivate(); + texture->unbind(); glBindVertexArray(0); } } // namespace scigl_render \ No newline at end of file