如何在QGLWidget窗口中平滑移动图像?

时间:2013-01-18 20:59:29

标签: image qt opengl smooth

我正在编写一个小程序来模拟动画。我使用QGLWidget来显示动态图像来模仿图片动画。基本思想是使用纹理并通过QGLFamebufferOject :: blitFramebuffer更新它。每次都会更改图像数组,然后更改帧缓冲区。

代码可以运行,图像可以向前移动,但图像有斩波问题,或轻弹,不平滑。

以下是类的代码:

GLWidget::GLWidget(QWidget *parent) : QGLWidget(QGLFormat(), parent)
{
    makeCurrent();

    if (QGLFramebufferObject::hasOpenGLFramebufferBlit()) {

        QGLFramebufferObjectFormat format;
        render_fbo = new QGLFramebufferObject(640, 480, format);
        texture_fbo = new QGLFramebufferObject(640, 480);

    } else {
        render_fbo = new QGLFramebufferObject(640, 480);
        texture_fbo = render_fbo;
    }

    tile_list = glGenLists(1);
    glNewList(tile_list, GL_COMPILE);
    glBegin(GL_QUADS);
    {
        glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
        glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
        glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
        glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
    }
    glEnd();
    glEndList();

    backbufferImg = new unsigned char[640 * 480 * sizeof(unsigned char) * 3];

}

GLWidget::~GLWidget()
{
}

void GLWidget::initializeGL()
{

}

void GLWidget::resizeGL(int width, int height)
{

}

void GLWidget::paintGL()
{

}

void GLWidget::saveGLState()
{
    glPushAttrib(GL_ALL_ATTRIB_BITS);
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
}

void GLWidget::restoreGLState()
{
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
    glPopAttrib();
}

void GLWidget::changeTextureImage(int nextColumn, int direction)
{
    // code about chaning backbufferImg
    // ...

    // call draw
    draw();
}


void GLWidget::draw()
{
    makeCurrent();

    QPainter p(this);  // used for text overlay

    // save the GL state set for QPainter
    saveGLState();

    QPainter fbo_painter(render_fbo);
    QImage renderImg(backbufferImg, 640, 480, QImage::Format_RGB888);

    fbo_painter.begin(render_fbo);
    QRect rect(640, 480);
    fbo_painter.drawImage(rect, renderImg, rect);
    fbo_painter.end();

    if (render_fbo != texture_fbo) {
        QGLFramebufferObject::blitFramebuffer(texture_fbo, rect, render_fbo, rect);
    } else {
      //  qDebug() << "render_fbo == texture_fbo";
    }

    // draw into the GL widget
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glViewport(0, 0, width(), height());
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glBindTexture(GL_TEXTURE_2D, texture_fbo->texture());

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glEnable(GL_TEXTURE_2D);

    glPushMatrix();
    glScalef(1.0f, 1.0f, 1.0f);
    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
    glCallList(tile_list);
    glPopMatrix();

    // restore the GL state that QPainter expects
    restoreGLState();

    glFinish();
}

2 个答案:

答案 0 :(得分:0)

Qt的QGLWidget期望您从其重载的paintGL函数执行所有绘图操作,以便它可以很好地与Qt的双缓冲区管理进行迭代。

另外,为什么你绕过FBO更新纹理呢?这是非常低效的并且引入了许多同步点。使用 Pixel 缓冲区对象并使用glTexSubImage2D更新纹理。

答案 1 :(得分:0)

非常感谢!我已经更改了代码并将绘图代码放在paintGL中,并且没有使用FBO的blit方法。好多了,但仍需要改进代码。以下是代码:

GLWidget::GLWidget(QWidget *parent) :
    QGLWidget(QGLFormat(), parent)
{
    makeCurrent();

    fbo = new QGLFramebufferObject(WIDTH, HEIGHT);

    tile_list = glGenLists(1);
    glNewList(tile_list, GL_COMPILE);
    glBegin(GL_QUADS);
    {
        glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
        glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
        glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
        glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
    }
    glEnd();
    glEndList();

    backbufferImg = new unsigned char[WIDTH * HEIGHT * sizeof(unsigned char) * 3];
}

GLWidget::~GLWidget()
{
    glDeleteLists(tile_list, 1);
    delete fbo;
    delete backbufferImg;
}

void GLWidget::initializeGL()
{

}

void GLWidget::resizeGL(int width, int height)
{

}

void GLWidget::paintGL()
{
    glViewport(0, 0, fbo->size().width(), fbo->size().height());

    glMatrixMode(GL_MODELVIEW);

    // render to the framebuffer object

    fbo->bind();
    glBindTexture(GL_TEXTURE_2D, refreshTexture);
    glCallList(tile_list);
    fbo->release();

 //////////////////////////////////////////////////////////////////////

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glViewport(0, 0, width(), height());
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glBindTexture(GL_TEXTURE_2D, fbo->texture());

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glEnable(GL_TEXTURE_2D);

    glPushMatrix();
    glScalef(1.0f, 1.0f, 1.0f);
    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
    glCallList(tile_list);
    glPopMatrix();

    // restore the GL state that QPainter expects
    restoreGLState();

    glFinish();
}



void GLWidget::saveGLState()
{
    glPushAttrib(GL_ALL_ATTRIB_BITS);
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
}

void GLWidget::restoreGLState()
{
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
    glPopAttrib();
}



void GLWidget::refreshTextureImage(int nextColumn, int direction)
{
    // upadte backbufferImg 
    // add code...

    QImage renderImg(backbufferImg, WIDTH, HEIGHT, QImage::Format_RGB888);

    refreshTexture = bindTexture(renderImg);
    updateGL();
}

我会尝试你建议的方法。但我认为帧缓冲和像素缓冲在同一级别。这意味着没有太多的性能差异。对于glTexSubImage2D,我不太确定,我需要检查它。

非常感谢你。