QGLWidget和快速的屏幕外渲染

时间:2013-11-12 16:07:29

标签: c++ qt opengl framebuffer off-screen

是否可以使用Qt在QGLWidget中完全离屏,而无需将场景重新绘制到屏幕上,从而避免在监视器上完全缓冲缓冲区?

我需要保存在帧缓冲区上生成的每一帧,但由于序列是由4000帧构成的,并且屏幕上的时间间隔为15ms我花费4000*15ms=60s但我需要更多速度超过60秒(计算不是瓶颈,只是更新问题)。

可以更快地在帧缓冲区上渲染屏幕吗?我可以在QGLWidget中避免显示器刷新率吗?

如果没有慢paintGL()次调用,如何在帧缓冲区上完全渲染?

2 个答案:

答案 0 :(得分:5)

现在我假设我们正在谈论Qt4。

  

是否可以在QGLWidget中完全渲染屏幕

离屏渲染根本不是依赖于窗口系统的任务。在大多数工具包中,WGL(至少)和GLX的唯一问题是你不能拥有无表面上下文,即没有绑定到窗口系统提供的可绘制的上下文。换句话说,只要当前上下文存在,您将始终拥有窗口系统提供的默认帧缓冲,它是不可变的。

有办法用X11手动创建一个不需要窗口的上下文,但这通常不值得。例如,对于EGL和OpenGL ES,这个问题不存在,因为有一个扩展趋向于完全解决这个问题,即屏幕外渲染。

但是,您可以在设置有效上下文后隐藏QGLWidget,并使用framebuffer对象执行所有操作,而无需默认的帧缓冲干预。

  

我可以在QGLWidget中避免显示器刷新率吗?

不,据我所知,Qt4的OpenGL模块无法以编程方式转换vsync。你可以转向SDL或GLFW这样的东西(不确定FreeGLUT)。

但是,您可以随时在驱动程序设置中关闭内容。这也会影响QGLWidget(或更好的放置,底层窗口系统的交换行为。)

  

可以更快地在帧缓冲区上渲染屏幕吗?

最终真的无所谓。你想要的图像数据不是VRAM,所以在将当前帧渲染到FBO后,无论如何都需要获取图像。如果你需要双缓冲和交换,你可以将结果blit到前缓冲区(或后缓冲区),或者你需要在进一步处理当前帧之前回读内容。

但是,与任何OpenGL和与性能相关的内容一样,不要猜测 - 个人资料

  

如果没有缓慢的paintGL()调用,如何在framebuffer上完全渲染?

设置完上下文后,您根本不需要小部件。你可以在没有Qt干预的情况下自己完成所有魔法。 paintGL()存在的唯一原因是为用户提供易于使用的界面,当小部件需要更新时,该界面可以保证被调用。

编辑:关于您在评论中的查询,请参阅此最小代码示例,该示例应该可以跨平台工作而无需更改。

#include <iostream>
#include <QtOpenGL/QGLWidget>
#include <QtGui/QApplication>

void renderOffScreen ()
{
  std::cout << glGetString(GL_VENDOR)   << std::endl;
  std::cout << glGetString(GL_RENDERER) << std::endl;
  std::cout << glGetString(GL_VERSION)  << std::endl;

  // do whatever you want here, e.g. setup a FBO, 
  // render stuff, read the results back until you're done
  // pseudocode:
  //     
  //      setupFBO();
  //   
  //      while(!done)
  //      {
  //        renderFrame();
  //        readBackPixels();
  //        processImage();
  //      }
}

int main(int argc, char* argv[])
{
  QApplication app(argc, argv);
  QGLWidget gl;

  // after construction, you should have a valid context
  // however, it is NOT made current unless show() or
  // similar functions are called
  if(!gl.isValid ())
  {
    std::cout << "ERROR: No GL context!" << std::endl;
    return -1;
  }

  // do some off-screen rendering, the widget has never been made visible
  gl.makeCurrent (); // ABSOLUTELY CRUCIAL!
  renderOffScreen ();

  return 0;
}

在我当前的机器上打印程序:

  

ATI Technologies Inc.

     AMD Radeon HD 7900系列

     

1.4(2.1(4.2.12337兼容性配置文件上下文13.101))

请注意QGLWidget如何从未实际显示,也不会发生事件处理。 Qt OpenGL库仅用于上下文创建。没有Qt干预就完成了其他任务。只是不要忘记根据您的需要设置视口和内容。

请注意:如果您只需要一些方便的方法来设置上下文,您可能需要切换到一些比Qt4更轻量级的工具包,如FreeGLUT。就个人而言,我发现FreeGLUT在设置一个有效的上下文时更加可靠,因为它在某些硬件上完全符合我的需要,例如: Sandy Bridge CPU。

答案 1 :(得分:0)

我找到了一个涉及使用QGLFrameBuffer个对象和glReadPixels的解决方案。

首先,我在QGLFrameBuffer中初始化我的QGLWidget::initializeGL对象,以便拥有QGLFrameBuffer可以“撒谎”的有效总帐上下文。

这是第一个实现。帧速率高10倍,并且根据VSync不会更新任何内容!!

MyGLWidget::MyGLWidget(QWidget *parent) :
    //    QGLWidget(parent)
    QGLWidget( QGLFormat(QGL::SampleBuffers), parent) //this format doesn't matter it's the QGLWidget format on the monitor
{
    //some initializations
}



void MyGLWidget::initializeGL()
{

    qglClearColor(Qt::black);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glShadeModel(GL_SMOOTH);
    glEnable(GL_DEPTH_TEST);
    glEnable (GL_BLEND);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0,0,-10);

    this->makeCurrent();

    // Initializing frame buffer object
    // Here we create a framebuffer object with the smallest necessary precision, i.e. GL_LUMINANCE in order to make
    // the subsequent calls to glReadPixels MUCH faster because the internal format is simpler and no casts are needed
    QGLFramebufferObjectFormat fboFormat;
    fboFormat.setMipmap(false);
    fboFormat.setSamples(0);
    fboFormat.setInternalTextureFormat(GL_LUMINANCE);
    // Create the framebuffer object
    fbo = new QGLFramebufferObject(QSize(this->width(),this->height()),fboFormat);
}


void MyGLWidget::generateFrames()
{
    //keep unsigned int because of possible integer overflow 
    //when resizing the vector and consequent std::bad_alloc() exceptions
    unsigned int slicesNumber = 1000;
    unsigned int w = this->width();
    unsigned int h = this->height();

    // This vector contains all the frames generated as unsigned char.
    vector<unsigned char> allFrames;
    allFrames.resize(w*h*slicesNumber);

    fbo->bind();
    // Inside this block the rendering is done on the framebuffer object instead of the MyGLWidget
    for ( int i=0; i<slicesNumber; i++ )
    {
            this->paintGL();
            // Read the current frame buffer object
            glReadPixels(0, 0, w, h, GL_LUMINANCE, GL_UNSIGNED_BYTE, allFrames.data()+i*w*h);
        // update scene()
    }
    fbo->release();
}