Как я могу плавно перемещать изображение в окне QGLWidget?

Я пишу небольшую программу для симуляции анимации. Я использую 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 ответа

Qt QGLWidget ожидает, что вы выполняете все операции рисования из его перегруженного paintGL функция так, чтобы он прекрасно итерировал с двойным управлением буфера Qt.

Кроме того, почему вы берете этот обход над FBO, чтобы обновить текстуру? Это крайне неэффективно и вводит много точек синхронизации. Используйте Pixel Buffer Object и обновите текстуру с помощью glTexSubImage2D.

Большое спасибо! Я изменил код и поместил код рисования в paintGL и не использовал метод blit в FBO. Гораздо лучше, но все же нужно улучшить код. Ниже приведен код:

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 я не совсем уверен, мне нужно это проверить.

Огромное спасибо.

Другие вопросы по тегам