使用原始OpenGL调用绘制Qml项存在问题

12
我想使用原生OpenGL调用在QtQuick场景中绘制单个项。我决定采取这篇问题中建议的方法。我创建了一个从QQuickFramebufferObject派生的Qt Quick项,并将其作为Renderer暴露给QML:(代码基于Qt示例:Scene Graph - Rendering FBOs)
class FboInSGRenderer : public QQuickFramebufferObject {
    Q_OBJECT
public:
    Renderer *createRenderer() const;
};

原始文件:

class LogoInFboRenderer : public QQuickFramebufferObject::Renderer {
    public:
        LogoInFboRenderer() { }

        void render() {
            int width = 1, height = 1;
            glEnable(GL_BLEND);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
            glColor4f(0.0, 1.0, 0.0, 0.8);
            glBegin(GL_QUADS);
            glVertex2f(0, 0);
            glVertex2f(width, 0);
            glVertex2f(width, height);
            glVertex2f(0, height);
            glEnd();

            glLineWidth(2.5);
            glColor4f(0.0, 0.0, 0.0, 1.0);
            glBegin(GL_LINES);
            glVertex2f(0, 0);
            glVertex2f(width, height);
            glVertex2f(width, 0);
            glVertex2f(0, height);
            glEnd();

            update();
        }

        QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) {
            QOpenGLFramebufferObjectFormat format;
            format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
            format.setSamples(4);
            return new QOpenGLFramebufferObject(size, format);
        }
};

QQuickFramebufferObject::Renderer *FboInSGRenderer::createRenderer() const {
    return new LogoInFboRenderer();
}

在Qml中,我使用它如下:

import QtQuick 2.4
import SceneGraphRendering 1.0

Rectangle {
    width: 400
    height: 400
    color: "purple"
    Renderer {
        id: renderer
        anchors.fill: parent
    }
}

我原本希望看到渲染的 "X" 填满整个场景,但实际上我得到了下面呈现的结果:

enter image description here

其他试验似乎证实了绘制的形状始终具有其大小(宽度/高度)除以2的尺寸。

我还检查了 createFramebufferObject 中的 size 参数是否具有正确的值。

查阅文档后,我发现在 QQuickFramebufferObject 类中有一个名为textureFollowsItemSize 的属性,默认情况下设置为true

我是做错了什么还是应该将这种行为视为Qt的错误?


我不熟悉QQuick,但是我看到你没有设置相机(投影或模型视图)或视口。如果您使用默认的OpenGL相机,则会朝向负z轴。因此,在正x和y象限中绘制时,我会期望您展示的结果(请记住,在OpenGL中,y是倒置的)。要在帧缓冲区中呈现一些2D项目,我会在z方向上设置一个正交相机,并使用边界[0,0]至[pixels_width,pixels_height]的glViewport,例如。 - Thomas
我已经使用了你的代码 - 在我的情况下,绿色的东西只呈现了一次。为什么会这样? - Xyz
2个回答

3

绘制的矩形大小只有您预期大小的一半,因为默认坐标范围为[-1, 1],而不是您的代码所假定的[0, 1]。如果要使用[0, 1]比例,则应适当设置投影矩阵:

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);

谢谢!它解决了我的问题,尽管我遇到了另一个问题:当我调整包含该项的窗口大小时,帧缓冲对象被填充了伪像--该项没有被正确渲染。我将尝试进一步调查,如果我找不到答案,我可能会发布新的问题。 - pawel
你有没有找到关于窗口调整大小的新问题的解决方案? - Mauri

0
如Qt文档所述:"警告:OpenGL操作和与场景图的交互必须在渲染线程上独占进行,主要是在updatePaintNode()调用期间。最好的经验法则是仅在QQuickItem::updatePaintNode()函数内使用带有“QSG”前缀的类。" 我是这样做的:

*.h

class MyQuickItem : public QQuickItem
{
    Q_OBJECT
public:
    MyQuickItem();
    ~MyQuickItem();

protected:
    QSGNode *updatePaintNode(QSGNode * oldNode, UpdatePaintNodeData * updatePaintNodeData);
    QSGNode *addNode(QSGGeometry *geometry, const QColor &color);
};

*.cpp

MyQuickItem::MyQuickItem()
{
    setFlag(QQuickItem::ItemHasContents,true);
}

MyQuickItem::~MyQuickItem()
{

}

QSGNode *MyQuickItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *updatePaintNodeData)
{
    Q_UNUSED(updatePaintNodeData)

    QSGTransformNode *root = static_cast<QSGTransformNode *>(oldNode);
    if(!root) root = new QSGTransformNode;
    QSGNode *node;
    QSGGeometry *geometry;

    QSGSimpleRectNode *rect = new QSGSimpleRectNode();
    rect->setColor(Qt::green);
    rect->setRect(boundingRect());
    root->appendChildNode(rect);

    geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2);
    geometry->setDrawingMode(GL_LINES);
    geometry->setLineWidth(5.0);
    geometry->vertexDataAsPoint2D()[0].set(x(), y());
    geometry->vertexDataAsPoint2D()[1].set(width(), height());
    node = addNode(geometry,Qt::blue);
    root->appendChildNode(node);

    geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2);
    geometry->setDrawingMode(GL_LINES);
    geometry->setLineWidth(5.0);
    geometry->vertexDataAsPoint2D()[0].set(width(), y());
    geometry->vertexDataAsPoint2D()[1].set(x(), height());
    node = addNode(geometry,Qt::blue);
    root->appendChildNode(node);

    return root;
}

QSGNode *MyQuickItem::addNode(QSGGeometry *geometry, const QColor &color)
{
    QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
    material->setColor(color);
    QSGGeometryNode *node = new QSGGeometryNode;
    node->setGeometry(geometry);
    node->setFlag(QSGNode::OwnsGeometry);
    node->setMaterial(material);
    node->setFlag(QSGNode::OwnsMaterial);
    return node;
}

main.cpp
qmlRegisterType<MyQuickItem>("MyObjects", 1, 0, "MyObject");

使用方法:

import QtQuick 2.3
import QtQuick.Window 2.2
import MyObjects 1.0    

Window {
    visible: true
    width: 360
    height: 360

    MyObject {
        anchors.fill: parent
    }
}

谢谢,不幸的是这不是我想要的。我知道这种方法,但在我的情况下,我需要调用原始的OpenGL(我正在集成外部库,该库应该在该项上进行绘制)。我正在尝试做的事情应该是可能的 - 我在问题中提到的示例也是如此(场景图 - 渲染FBOs)。 - pawel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接