12

I want to draw a single item in QtQuick scene using raw OpenGL calls. I have decided to take approach suggested in this question.

I have created a Qt Quick item deriving from QQuickFramebufferObject and exposed it to QML as Renderer: (code is based on Qt example: Scene Graph - Rendering FBOs)

class FboInSGRenderer : public QQuickFramebufferObject {
    Q_OBJECT
public:
    Renderer *createRenderer() const;
};

source file:

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();
}

In Qml I use it as follows:

import QtQuick 2.4
import SceneGraphRendering 1.0

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

I was expecting to see that rendered "X" will fill entire scene, but instead I get the result presented below:

enter image description here

Other experiments seem to confirm that drew shape has always it's size (width/height) divided by 2.

I also checked that size parameter in createFramebufferObject has correct value.

Looking into docs led me to property textureFollowsItemSize in QQuickFramebufferObject class but it is by default set to true.

Am I doing something wrong or should I consider this behavior as Qt bug?

Community
  • 1
  • 1
pawel
  • 231
  • 2
  • 6
  • I don't know QQuick, but OpenGL and i do not see that you have set either a camera (projection or modelview) or a viewport. If you use the default opengl camera it looks down the negative z-axis. Thus, when drawing in the positive x- and y- quadrant i would expect the result you show (remember, in opengl y is upside down). To render some 2d items in a framebuffer i would set an ortho camera in z-direction and a glViewport with bounds [0,0] to [pixels_width, pixels_height] for example. – Thomas Jan 21 '15 at 17:46
  • I've used your code - in my case the green thing is rendered only once. Why is that? – Xyz May 16 '17 at 11:56

2 Answers2

3

The drawn rectangle is half the sizes you expect because the default coordinate range is [-1, 1], not [0, 1] as your code assumes. If you want to use [0, 1] scale, then you should appropriately set the projection matrix:

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
Michał W. Urbańczyk
  • 1,453
  • 1
  • 12
  • 20
  • Thank you! It solved my problem, although I run into another one: When I am resizing window which contains that item, frame-buffer object is being filled with artifacts -- item is not correctly renderer. I will try to investigate it further and if I would not find the answer I will probably post new question. – pawel Jan 29 '15 at 09:13
  • Did you find a solution about the new problem, regarding the window resizing? – Mauri May 02 '16 at 16:49
0

As Qt documentation says: "Warning: It is crucial that OpenGL operations and interaction with the scene graph happens exclusively on the rendering thread, primarily during the updatePaintNode() call. The best rule of thumb is to only use classes with the "QSG" prefix inside the QQuickItem::updatePaintNode() function." I do it in this way:

*.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;
}

In main.cpp

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

And usage:

import QtQuick 2.3
import QtQuick.Window 2.2
import MyObjects 1.0    

Window {
    visible: true
    width: 360
    height: 360

    MyObject {
        anchors.fill: parent
    }
}
folibis
  • 12,048
  • 6
  • 54
  • 97
  • Thank you, unfortunately it's not what I wanted. I know this approach but in my case I need to call raw OpenGL (I am integrating with external library which should be drawing on that Item). Things I am trying to do should be possible -- example that I mentioned in my question does it too (Scene Graph - Rendering FBOs). – pawel Jan 22 '15 at 09:11