是否可以在 Qt 中完全渲染屏幕外,QGLWidget
而无需将场景重新绘制到屏幕,从而完全避免缓冲区在监视器上翻转?
我需要保存在帧缓冲区上生成的每一帧,但是,由于序列由 4000 帧组成,并且屏幕上的时间间隔是15ms
我花费的4000*15ms=60s
,但我需要比 60 秒快得多(计算在这里不是瓶颈,只是更新问题)。
在帧缓冲区上渲染屏幕外可以更快吗?我可以避免 QGLWidget 中的显示器刷新率吗?
如何在没有缓慢paintGL()
调用的情况下完全在帧缓冲区上渲染?
是否可以在 Qt 中完全渲染屏幕外,QGLWidget
而无需将场景重新绘制到屏幕,从而完全避免缓冲区在监视器上翻转?
我需要保存在帧缓冲区上生成的每一帧,但是,由于序列由 4000 帧组成,并且屏幕上的时间间隔是15ms
我花费的4000*15ms=60s
,但我需要比 60 秒快得多(计算在这里不是瓶颈,只是更新问题)。
在帧缓冲区上渲染屏幕外可以更快吗?我可以避免 QGLWidget 中的显示器刷新率吗?
如何在没有缓慢paintGL()
调用的情况下完全在帧缓冲区上渲染?
现在我假设我们正在谈论 Qt4。
是否可以在 QGLWidget 中完全渲染屏幕外
离屏渲染根本不是依赖于窗口系统的任务。在大多数工具包中,WGL(至少)和 GLX 的唯一问题是您不能拥有无表面上下文,即未绑定到窗口系统提供的可绘制对象的上下文。换句话说,只要当前上下文存在,您将始终拥有窗口系统提供的默认帧缓冲区,该缓冲区是不可变的。
有办法使用 X11 手动创建不需要窗口的上下文,但通常不值得麻烦。例如,对于 EGL 和 OpenGL ES,这个问题不存在,因为有一个扩展正好解决了这个问题,即离屏渲染。
但是,您可以在设置有效上下文后简单地隐藏 QGLWidget,并使用帧缓冲区对象来完成所有操作,而无需默认帧缓冲区干预。
我可以避免 QGLWidget 中的显示器刷新率吗?
不,据我所知,Qt4 的 OpenGL 模块无法以编程方式打开 vsync。你可以求助于 SDL 或 GLFW 来做类似的事情(不确定 FreeGLUT)。
但是,您始终可以在驱动程序设置中关闭某些功能。这也将影响 QGLWidget(或者更好的说法,底层窗口系统的交换行为。)
在帧缓冲区上渲染屏幕外可以更快吗?
最后真的应该没关系。您将需要 VRAM 以外的其他地方的图像数据,因此在将当前帧渲染到 FBO 之后,无论如何您都需要获取图像。您可以将结果传送到前缓冲区(如果需要双缓冲和交换,则将结果传送到后缓冲区),或者您需要在进一步处理当前帧之前回读内容。
但是,与任何与 OpenGL 和性能相关的东西一样,不要猜测 - 配置文件!
如何在没有缓慢的 paintGL() 调用的情况下完全在帧缓冲区上渲染?
一旦设置了上下文,您就根本不需要该小部件。您可以在没有 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 技术公司
AMD Radeon HD 7900 系列
1.4 (2.1 (4.2.12337 兼容性配置文件上下文 13.101))
请注意它QGLWidget
实际上是如何不可见的,也没有发生事件处理。Qt OpenGL 库仅用于创建上下文。其他任何事情都无需 Qt 干预即可完成。只是不要忘记根据您的需要设置视口和东西。
请注意:如果您只需要一些方便的方法来设置上下文,您可能想要切换到一些比 Qt4 更轻量级的工具包,例如 FreeGLUT。就我个人而言,我发现 FreeGLUT 在设置有效上下文时更加可靠,这正是我在某些硬件上想要的方式,例如 Sandy Bridge CPU。
我找到了一个涉及使用QGLFrameBuffer
对象和glReadPixels
.
首先,我初始化我的QGLFrameBuffer
对象,QGLWidget::initializeGL
以便拥有一个QGLFrameBuffer
可以“撒谎”的有效 GL 上下文。
这是第一个实现。帧速率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();
}