Проблема с рисованием Qml-элемента с необработанными вызовами OpenGL

Я хочу нарисовать один элемент в сценарии QtQuick, используя необработанные вызовы OpenGL. Я решил использовать подход, предложенный в этом вопросе.

Я создал элемент Qt Quick, полученный из QQuickFramebufferObject и выставил его QML как Renderer: (код основан на примере Qt: Сцена График - рендеринг 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.

Я также проверил, что параметр size в createFramebufferObject имеет правильное значение.

Взгляд в документы привел меня к свойству textureFollowsItemSize в классе QQuickFramebufferObject, но по умолчанию он установлен на true.

Я делаю что-то неправильно или должен ли я рассматривать это поведение как ошибку Qt?

Ответ 1

Нарисованный прямоугольник равен половине ваших ожиданий, поскольку диапазон координат по умолчанию - [-1, 1], а не [0, 1], как предполагает ваш код. Если вы хотите использовать шкалу [0, 1], то вы должны соответствующим образом установить матрицу проецирования:

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

Ответ 2

Как говорится в документации Qt: "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." Я делаю это следующим образом:

*. Ч

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
    }
}