Browse Source

Improved draw_buffer (#2010)

* better doc of ViewerCore::draw_buffer and auto sizing

* draw_buffer at per core level, including depth, templated output
Alec Jacobson 3 years ago
parent
commit
142fd6026a

+ 23 - 2
include/igl/opengl/ViewerCore.cpp

@@ -252,7 +252,8 @@ IGL_INLINE void igl::opengl::ViewerCore::draw(
     draw_labels(data, data.meshgl.custom_labels);
 }
 
-IGL_INLINE void igl::opengl::ViewerCore::draw_buffer(ViewerData& data,
+IGL_INLINE void igl::opengl::ViewerCore::draw_buffer(
+  ViewerData& data,
   bool update_matrices,
   Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& R,
   Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& G,
@@ -264,8 +265,28 @@ IGL_INLINE void igl::opengl::ViewerCore::draw_buffer(ViewerData& data,
 
   unsigned width = R.rows();
   unsigned height = R.cols();
-
+  if(width == 0 && height == 0)
+  {
+    width = viewport(2);
+    height = viewport(3);
+  }
+  R.resize(width,height);
+  G.resize(width,height);
+  B.resize(width,height);
+  A.resize(width,height);
+
+  ////////////////////////////////////////////////////////////////////////
+  // PREPARE width×height BUFFERS does *not* depend on `data`
+  //   framebuffer
+  //   textureColorBufferMultiSampled
+  //   rbo
+  //   intermediateFBO
+  //   screenTexture
+  //
+  ////////////////////////////////////////////////////////////////////////
   // https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing
+
+  // Create an initial multisampled framebuffer
   unsigned int framebuffer;
   glGenFramebuffers(1, &framebuffer);
   glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

+ 19 - 0
include/igl/opengl/ViewerCore.h

@@ -72,7 +72,26 @@ public:
   // Draw everything
   //
   // data cannot be const because it is being set to "clean"
+  //
+  // Inputs:
+  //   data  which ViewerData to draw
+  //   update_matrices  whether to update view, proj, and norm matrices in
+  //     shaders
   IGL_INLINE void draw(ViewerData& data, bool update_matrices = true);
+  // Render given ViewerData to a buffer. The width and height are determined by
+  // non-zeros dimensions of R (and G,B,A should match) or – if both are zero —
+  // are set to this core's viewport sizes.
+  //
+  // Inputs:
+  //   data  which ViewerData to draw
+  //   update_matrices  whether to update view, proj, and norm matrices in
+  //     shaders
+  // Outputs:
+  //   R  width by height red pixel color values
+  //   G  width by height green pixel color values
+  //   B  width by height blue pixel color values
+  //   A  width by height alpha pixel color values
+  //
   IGL_INLINE void draw_buffer(
     ViewerData& data,
     bool update_matrices,

+ 160 - 0
include/igl/opengl/glfw/Viewer.cpp

@@ -14,6 +14,7 @@
 #include <Eigen/LU>
 
 #include "../gl.h"
+#include "../report_gl_error.h"
 #include <GLFW/glfw3.h>
 
 #include <cmath>
@@ -938,6 +939,159 @@ namespace glfw
     }
   }
 
+  template <typename T>
+  IGL_INLINE void Viewer::draw_buffer(
+    igl::opengl::ViewerCore & core,
+    Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & R,
+    Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & G,
+    Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & B,
+    Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & A,
+    Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & D)
+  {
+    // follows igl::opengl::ViewerCore::draw_buffer, image is transposed from
+    // typical matrix view
+    const int width = R.rows() ? R.rows() :  core.viewport(2);
+    const int height = R.cols() ? R.cols() : core.viewport(3);
+    R.resize(width,height);
+    G.resize(width,height);
+    B.resize(width,height);
+    A.resize(width,height);
+    D.resize(width,height);
+
+    ////////////////////////////////////////////////////////////////////////
+    // Create an initial multisampled framebuffer
+    ////////////////////////////////////////////////////////////////////////
+    unsigned int framebuffer;
+    unsigned int color_buffer;
+    unsigned int depth_buffer;
+    {
+      glGenFramebuffers(1, &framebuffer);
+      glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+      // create a multisampled color attachment texture (is a texture really
+      // needed? Could this be a renderbuffer instead?)
+      glGenTextures(1, &color_buffer);
+      glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, color_buffer);
+      glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA, width, height, GL_TRUE);
+      glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
+      glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, color_buffer, 0);
+      // create a (also multisampled) renderbuffer object for depth and stencil attachments
+      glGenRenderbuffers(1, &depth_buffer);
+      glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
+      glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, width, height);
+      glBindRenderbuffer(GL_RENDERBUFFER, 0);
+      glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depth_buffer);
+
+
+      assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
+      report_gl_error("glCheckFramebufferStatus: ");
+      glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    }
+
+    ////////////////////////////////////////////////////////////////////////
+    // configure second post-processing framebuffer
+    ////////////////////////////////////////////////////////////////////////
+    unsigned int intermediateFBO;
+    unsigned int screenTexture, depthTexture;
+    {
+      glGenFramebuffers(1, &intermediateFBO);
+      glBindFramebuffer(GL_FRAMEBUFFER, intermediateFBO);
+      // create a color attachment texture
+      glGenTextures(1, &screenTexture);
+      glBindTexture(GL_TEXTURE_2D, screenTexture);
+      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+      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, screenTexture, 0);
+      
+      // create depth attachment texture
+      glGenTextures(1, &depthTexture);
+      glBindTexture(GL_TEXTURE_2D, depthTexture);
+      glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL);
+      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+      glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
+
+      assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
+      glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    }
+
+    ////////////////////////////////////////////////////////////////////////
+    // attach initial framebuffer and draw all `data`
+    ////////////////////////////////////////////////////////////////////////
+    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+    // Clear the buffer
+    glClearColor(
+      core.background_color(0), 
+      core.background_color(1), 
+      core.background_color(2),
+      core.background_color(3));
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    // Save old viewport
+    Eigen::Vector4f viewport_ori = core.viewport;
+    core.viewport << 0,0,width,height;
+    // Draw all `data`
+    for (auto& data : data_list)
+    {
+      if (data.is_visible & core.id)
+      {
+        core.draw(data);
+      }
+    }
+    // Restore viewport
+    core.viewport = viewport_ori;
+
+    ////////////////////////////////////////////////////////////////////////
+    // attach second framebuffer and redraw (for anti-aliasing?)
+    ////////////////////////////////////////////////////////////////////////
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, intermediateFBO);
+    report_gl_error("before: ");
+    glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+    report_gl_error("glBlitFramebuffer: ");
+
+
+    ////////////////////////////////////////////////////////////////////////
+    // Read pixel data from framebuffer, write into buffers
+    ////////////////////////////////////////////////////////////////////////
+    glBindFramebuffer(GL_FRAMEBUFFER, intermediateFBO);
+    // Copy back in the given Eigen matrices
+    {
+      typedef typename std::conditional< std::is_floating_point<T>::value,GLfloat,GLubyte>::type GLType;
+      GLenum type = std::is_floating_point<T>::value ?  GL_FLOAT : GL_UNSIGNED_BYTE;
+      GLType* pixels = (GLType*)calloc(width*height*4,sizeof(GLType));
+      GLType * depth = (GLType*)calloc(width*height*1,sizeof(GLType));
+      glReadPixels(0, 0,width, height,GL_RGBA,            type, pixels);
+      glReadPixels(0, 0,width, height,GL_DEPTH_COMPONENT, type, depth);
+      int count = 0;
+      for (unsigned j=0; j<height; ++j)
+      {
+        for (unsigned i=0; i<width; ++i)
+        {
+          R(i,j) = pixels[count*4+0];
+          G(i,j) = pixels[count*4+1];
+          B(i,j) = pixels[count*4+2];
+          A(i,j) = pixels[count*4+3];
+          D(i,j) = depth[count*1+0];
+          ++count;
+        }
+      }
+      // Clean up
+      free(pixels);
+      free(depth);
+    }
+
+    // Clean up
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    glDeleteTextures(1, &screenTexture);
+    glDeleteTextures(1, &depthTexture);
+    glDeleteTextures(1, &color_buffer);
+    glDeleteRenderbuffers(1, &depth_buffer);
+    glDeleteFramebuffers(1, &framebuffer);
+    glDeleteFramebuffers(1, &intermediateFBO);
+  }
+
   IGL_INLINE void Viewer::resize(int w,int h)
   {
     if (window) {
@@ -1136,3 +1290,9 @@ namespace glfw
 } // end namespace
 } // end namespace
 }
+
+#ifdef IGL_STATIC_LIBRARY
+template void igl::opengl::glfw::Viewer::draw_buffer<unsigned char>(igl::opengl::ViewerCore&, Eigen::Matrix<unsigned char, -1, -1, 0, -1, -1>&, Eigen::Matrix<unsigned char, -1, -1, 0, -1, -1>&, Eigen::Matrix<unsigned char, -1, -1, 0, -1, -1>&, Eigen::Matrix<unsigned char, -1, -1, 0, -1, -1>&, Eigen::Matrix<unsigned char, -1, -1, 0, -1, -1>&);
+template void igl::opengl::glfw::Viewer::draw_buffer<double>(igl::opengl::ViewerCore&, Eigen::Matrix<double, -1, -1, 0, -1, -1>&, Eigen::Matrix<double, -1, -1, 0, -1, -1>&, Eigen::Matrix<double, -1, -1, 0, -1, -1>&, Eigen::Matrix<double, -1, -1, 0, -1, -1>&, Eigen::Matrix<double, -1, -1, 0, -1, -1>&);
+template void igl::opengl::glfw::Viewer::draw_buffer<float>(igl::opengl::ViewerCore&, Eigen::Matrix<float, -1, -1, 0, -1, -1>&, Eigen::Matrix<float, -1, -1, 0, -1, -1>&, Eigen::Matrix<float, -1, -1, 0, -1, -1>&, Eigen::Matrix<float, -1, -1, 0, -1, -1>&, Eigen::Matrix<float, -1, -1, 0, -1, -1>&);
+#endif

+ 28 - 0
include/igl/opengl/glfw/Viewer.h

@@ -72,6 +72,34 @@ namespace glfw
     IGL_INLINE bool save_scene(std::string fname);
     // Draw everything
     IGL_INLINE void draw();
+    // Render given ViewerCore to a buffer. The width and height are determined
+    // by non-zeros dimensions of R or – if both are zero — are set to this
+    // core's viewport sizes. Other buffers are resized to fit if needed.
+    //
+    // Template:
+    //   T  image storage type, e.g., unsigned char (values ∈ [0,255]), double
+    //     (values ∈ [0.0,1.0]).
+    // Inputs:
+    //   data  which ViewerData to draw
+    //   update_matrices  whether to update view, proj, and norm matrices in
+    //     shaders
+    // Outputs:
+    //   R  width by height red pixel color values
+    //   G  width by height green pixel color values
+    //   B  width by height blue pixel color values
+    //   A  width by height alpha pixel color values
+    //   D  width by height depth pixel values. Depth values are _not_
+    //     anti-aliased like RGBA.
+    //
+    template <typename T>
+    IGL_INLINE void draw_buffer(
+      // can't be const because of writing in and out of `core.viewport`
+      /*const*/ igl::opengl::ViewerCore & core, 
+      Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & R,
+      Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & G,
+      Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & B,
+      Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & A,
+      Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & D);
     // OpenGL context resize
     IGL_INLINE void resize(int w,int h); // explicitly set window size
     IGL_INLINE void post_resize(int w,int h); // external resize due to user interaction