如何使 OpenGL 视口具有渲染到其中的 QML 项目的确切大小和位置?

Posted

技术标签:

【中文标题】如何使 OpenGL 视口具有渲染到其中的 QML 项目的确切大小和位置?【英文标题】:How to make OpenGL viewport have the exact size and position of my QML item which renders into it? 【发布时间】:2019-06-24 22:34:37 【问题描述】:

我正在尝试在 1280x720 窗口内渲染 640x360 红色方块。问题是 OpenGL 的视口不会自动进入 Qt 窗口系统的正方形占据的区域内。也就是说,如果我的视频对象具有 640x360 尺寸,则 OpenGL 视口仍然具有 1280x720 尺寸(为什么?)。

无论如何,我可以使用glViewport(this->x, this->y, this->width, this->height); 来解决这个问题。问题是在 OpenGL 中,坐标系的原点在左下角,this->x, this->y, this->width, this->height 来自 QML 语法中的对象坐标,原点在左上角。结果是这样的:

ma​​in.qml

import QtQuick 2.0

import OpenGlVideoQtQuick2 1.0


Grid 
    columns: 2
    spacing: 2    
    width: 1280
    height: 720
    OpenGlVideoQtQuick2 
        width: 640
        height: 360
    


但是红色方块占据了第三格而不是第一格:

我可以轻松通过执行glViewport(this->x, WINDOW_HEIGHT-this->y, this->width, this->height); 来解决这个问题,其中WINDOW_HEIGHT 的值是720,但是我认为我不应该相信 OpenGL 视口的大小始终为窗口,所以我应该得到 OpenGL 视口尺寸,但我认为这是不可能的。

我试过了

GLint dims[2] = 9999, 9999;
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, dims);
std::cout << "dimenson x:" << dims[0] << " dimenson y:" << dims[1] << std::endl;

这给了我dimenson x:8192 dimenson y:8192,这对我来说毫无意义。

这里是OpenGL渲染代码:https://github.com/lucaszanella/QQuickPaintedItemBug/blob/0a8ef6b5229afa7113ec1e4e3838981042792329/OpenGlVideoQtQuick2.cpp

您可以在此处查看整个可构建项目:https://github.com/lucaszanella/QQuickPaintedItemBug/tree/0a8ef6b5229afa7113ec1e4e3838981042792329

【问题讨论】:

你看过Qt的例子吗? doc.qt.io/qt-5/qtquick-scenegraph-openglunderqml-example.html @BenjaminT 据我了解,此示例将始终将视口置于坐标 (0,0) 中,并且将具有窗口*纵横比的大小,这不是我需要的。我需要它来显示我的 QML 项目的大小和位置 【参考方案1】:

所以这里是伪代码的解决方案:

int w = item->width() * window->devicePixelRatio();
int h = item->height() * window->devicePixelRatio();
int x = item->x();
int y = window->height()* window->devicePixelRatio() - h - item->y()

glViewport(x, y, w, h);

我已经用 Qt Scene Graph - OpenGL Under QML 示例进行了测试。 以下是我所做的更改:

ma​​in.qml

Squircle 
    x: 80
    y: 120
    width: 160
    height: 240
    [...]

squircle.cpp

void Squircle::sync()

    if (!m_renderer) 
        m_renderer = new SquircleRenderer();
        connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::paint, Qt::DirectConnection);
    
    m_renderer->setViewportSize(this->size().toSize() * window()->devicePixelRatio());
    m_renderer->setPosition(this->position().toPoint());
    m_renderer->setT(m_t);
    m_renderer->setWindow(window());


void SquircleRenderer::paint()

    [...]
    int y = (m_window->size()* m_window->devicePixelRatio()).height() - m_viewportSize.height() -  m_position.y();
    glViewport(m_position.x(), y, m_viewportSize.width(), m_viewportSize.height());
    [...]

squircle.h

class SquircleRenderer : public QObject, protected QOpenGLFunctions

    Q_OBJECT
public:

    void setPosition(const QPoint &position)  m_position = position; 

    [...]

private:
    [...]
    QPoint m_position;
;

结果如下:

【讨论】:

谢谢,它有点工作。但是您是否注意到窗口的其余部分仍然是黑色的?就像 OpenGL 仍在那个领域工作一样。你知道怎么去掉吗? @LucasZanella 它是黑色的,因为那里什么都没有。你希望它是什么样子? 但它隐藏了背景的颜色 @LucasZanella 它没有。在 Qt 完成任何渲染之前渲染您的 OpenGL 代码。所以事实上,如果你的应用程序中确实有一个明确的背景(例如,通过将 Grid 替换为 Rectangle),那么背景将呈现在你的 OpenGL 代码之上并隐藏它。让你感觉后台被隐藏的,是电话win-&gt;setClearBeforeRendering(false)。此调用将阻止 Qt 呈现窗口的默认背景,但没有它,窗口背景将呈现在您的 OpenGL 之上。 @LucasZanella 我不认为你可以用你当前的设计来改进。另一种方法是将QSGSimpleMaterialShaderQSGMaterialShaderQSGGeometryNode 一起使用。有一个关于如何使用它的示例:doc.qt.io/qt-5/qtquick-scenegraph-simplematerial-example.html 如果你这样做,你的着色器应该像任何普通项目一样呈现在 QML 项目树内。您当前的设计无法做到这一点。

以上是关于如何使 OpenGL 视口具有渲染到其中的 QML 项目的确切大小和位置?的主要内容,如果未能解决你的问题,请参考以下文章

GPU如何渲染到正确的窗口?

修复具有多个视口的窗口中的严重闪烁

OpenGL的glViewport视口变换函数详解[转]

如何使用正交项目将 3d 渲染图像(透视项目)绘制回另一个视口。同时使用多个视口和 OpenGL [关闭]

QML下的OpenGL

使用 Vulkan 渲染 Qml