关于qt:如何从QGraphicsItem :: paint()内将QGLFrameBufferObject绘制到画家上

How to draw QGLFrameBufferObject onto the painter from within QGraphicsItem::paint()

我的问题的小版本

在QGraphicsItem :: paint()函数中,我有一个QGLFrameBufferObject。如何在作为参数传递的painter的paintdevice上获取它? (前提是QGraphicsItem位于由QGraphicsView渲染的场景中,该场景具有QGLWidget作为视口=> painter使用的是opengl引擎)

1
2
3
4
5
6
7
8
9
QGraphicsItem::paint(QPainter* painter, ...)
{
    QGLFramebufferObject fbo;
    QPainter p(fbo);
    ... // Some painting code on the fbo
    p.end();

    // What now? How to get the fbo content drawn on the painter?
}

我看了Qt提供的framebufferobject和pbuffer示例。使用自定义的opengl代码在QGLWidget中绘制了fbo / pbuffer。是否可以在QGraphicsItem的paint()方法中做同样的事情,并考虑QGraphisItem在场景/视图中的位置?

我的问题的大版本

情况草图

我有一个QGraphicsScene。它是一个具有QGraphicsEffect的项目(通过重写QGraphicsEffect的draw()实现的实现)。该场景由具有QGLWidget作为视口的QGraphicsView渲染。

在QGraphicsEffect :: draw(QPainter *)中,我必须生成一些像素图,然后使用提供的绘画程序(绘画程序将QGLWidget作为绘画设备)进行绘制。构造像素图是一些绘制调用的组合,我希望这些可以在硬件中完成。

简化的示例:(我没有在我的draw()方法中调用sourcepixmap,因为不需要它来演示我的问题)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class OwnGraphicsEffect: public QGraphicsEffect
{
     virtual void draw(QPainter* painter);
}

void OwnGraphicsEffect::draw(QPainter* painter)
{
    QRect rect(0,0,100,100);
    QGLPixelBuffer pbuffer(rect.size(), QGLFormat(QGL::Rgba));
    QPainter p(pbuffer);
    p.fillRect(rect, Qt::transparent);
    p.end();

    painter->drawImage(QPoint(0,0), pbuffer->toImage(),rect);
}

实际问题

我担心的是代码的最后一行:pbuffer-> toImage()。我不想用这个。由于性能原因,我不想进行QImage转换。有没有办法从我的glpixelbuffer获取一个像素图,然后使用painter-> drawpixmap()?

我知道我也可以使用以下方法将pbuffer复制到纹理:

1
2
GLuint dynamicTexture = pbuffer.generateDynamicTexture();
pbuffer.updateDynamicTexture(dynamicTexture);

但我不知道如何将这种纹理粘贴到"画家"上。


扩展leemes的答案,这是一个还可以处理多样本帧缓冲对象的解决方案。

首先,如果要在QGLWidget上绘制,则只需使用OpenGL命令
莱姆斯在回答中建议。请注意,有一个现成的drawTexture()
命令可用,此代码简化为以下内容:

1
2
3
4
5
6
void Widget::drawFBO(QPainter &painter, QGLFramebufferObject &fbo, QRect target)
{
    painter.beginNativePainting();
    drawTexture(target, fbo.texture());
    painter.endNativePainting();
}

要绘制多样本FBO,可以将其转换为非多样本FBO。
使用QGLFramebufferObject::blitFramebuffer(请注意,并非每个硬件
/驱动程序组合支持此功能!):

1
2
3
4
5
6
7
8
9
10
if(fbo.format().samples() > 1)
{
    QGLFramebufferObject texture(fbo.size()); // the non-multisampled fbo
    QGLFramebufferObject::blitFramebuffer(
            &texture, QRect(0, 0, fbo.width(), fbo.height()),
            &fbo, QRect(0, 0, fbo.width(), fbo.height()));
    drawTexture(targetRect, texture.texture());
}
else
    drawTexture(targetRect, fbo.texture());

但是,据我所知,您不能在非OpenGL上下文中使用OpenGL命令进行绘制。
为此,您首先需要将帧缓冲区转换为(软件)图像,例如
使用fbo.toImage()的QImage并使用您的QPainter而不是
fbo直接。


我想我知道了。我使用QPainter::beginNativePainting()在paintEvent中混合OpenGL命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void Widget::drawFBO(QPainter &painter, QGLFramebufferObject &fbo, QRect target)
{
    painter.beginNativePainting();

    glEnable(GL_TEXTURE_2D);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glBindTexture(GL_TEXTURE_2D, fbo.texture());
    glBegin(GL_QUADS);
    glTexCoord2d(0.0,1.0); glVertex2d(target.left(),      target.top());
    glTexCoord2d(1.0,1.0); glVertex2d(target.right() + 1, target.top());
    glTexCoord2d(1.0,0.0); glVertex2d(target.right() + 1, target.bottom() + 1);
    glTexCoord2d(0.0,0.0); glVertex2d(target.left(),      target.bottom() + 1);
    glEnd();

    painter.endNativePainting();
}

我希望这也可以在QGraphicsItem的paintEvent中使用(因为QGraphicsView使用QGLWidget作为视口),因为我只是直接在QGLWidget::paintEvent中对其进行了测试。

但是,仍然存在以下问题:我不知道如何绘制多样本帧缓冲区。 QGLFramebufferObject::texture()的文档说:

If a multisample framebuffer object is used then the value returned from this function will be invalid.