简体   繁体   English

在 OpenGL 的全屏四边形中将 QGraphicsScene 显示为纹理

[英]Showing a QGraphicsScene as a texture in a full Screen Quad of OpenGL

I need to draw a Quad that occupies my entire view (I'm not sure if viewport is technically accurate) and the load a QGraphicsScene scene onto the Quad as a texture.我需要绘制一个占据我整个视图的四边形(我不确定视口在技术上是否准确)并将 QGraphicsScene 场景作为纹理加载到四边形上。 Here is my code for OpenGLCanvas which simply inherits and reimplements a QOpenGLWidget.这是我的 OpenGLCanvas 代码,它简单地继承并重新实现了 QOpenGLWidget。

I'm now including all files to compile and reproduce the example我现在包括所有文件来编译和重现示例

openglcanvas.h: openglcanvas.h:

#ifndef OPENGLCANVAS_H
#define OPENGLCANVAS_H

#include <QOpenGLWidget>
#include "targettest.h"
#include <QOpenGLFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLShader>

class OpenGLCanvas : public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT
public:
    explicit OpenGLCanvas(QWidget *parent = nullptr);

    void initializeGL() override;
    void paintGL() override;
    void resizeGL(int w, int h) override;

private:
    float xrot,yrot,zrot;
    TargetTest targetTest;
//    QOpenGLBuffer buffer;
//    QOpenGLShaderProgram shaderProg;

};

#endif // OPENGLCANVAS_H

openglcanvas.cpp openglcanvas.cpp

#include "openglcanvas.h"

OpenGLCanvas::OpenGLCanvas(QWidget *parent):QOpenGLWidget(parent)
{
    xrot = yrot = zrot = 0.0;
    targetTest.initialize(100,100);

    initializeOpenGLFunctions();

}

void OpenGLCanvas::initializeGL(){
    glClearColor(0.0,0.0,1.0,0.0);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
}

void OpenGLCanvas::paintGL(){

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-2, +2, -2, +2, 1.0, 15.0);
    //glOrtho(-1, +1, -1, +1, 1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glTranslatef(0.0, 0.0, -7.0);
    glRotatef(xrot, 1.0, 0.0, 0.0);
    glRotatef(yrot, 0.0, 1.0, 0.0);
    glRotatef(zrot, 0.0, 0.0, 1.0);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    targetTest.renderCurrentPosition(0,0,0,0);

    glBindTexture(GL_TEXTURE_2D, targetTest.getFBO()->texture());
    glEnable(GL_TEXTURE_2D);

    glBegin(GL_QUADS);
    glNormal3d(0,0,+1);
    glTexCoord2f(0.0, 0.0); glVertex3d(-1,-1,0);
    glTexCoord2f(0.0, 2.0); glVertex3d(-1,1,0);
    glTexCoord2f(2.0, 2.0); glVertex3d(1,1,0);
    glTexCoord2f(2.0, 0.0); glVertex3d(1,-1,0);
    glEnd();

    glFlush();


}

void OpenGLCanvas::resizeGL(int width, int height)
{

    Q_UNUSED(width) Q_UNUSED(height)

//    int side = qMin(width, height);
//    glViewport((width - side) / 2, (height - side) / 2, side, side);

//    glMatrixMode(GL_PROJECTION);
//    glLoadIdentity();
//    glOrtho(-2, +2, -2, +2, 1.0, 15.0);
//    //glOrtho(-1, +1, -1, +1, 1.0, 1.0);
//    glMatrixMode(GL_MODELVIEW);
}

And here is the code of target test wich draws some circles as a QGraphicsScene and renders them to an QOpenGLFramebufferObject:这是目标测试的代码,它绘制了一些圆圈作为 QGraphicsScene 并将它们呈现给 QOpenGLFramebufferObject:

targettest.h目标测试.h

#ifndef TARGETTEST_H
#define TARGETTEST_H

#include <QPainter>
#include <QGraphicsScene>
#include <QGraphicsEllipseItem>
#include <QOpenGLContext>
#include <QOpenGLFramebufferObject>
#include <QOffscreenSurface>
#include <QDebug>
#include <QOpenGLPaintDevice>


class TargetTest
{
public:
    TargetTest();
    ~TargetTest();

    void initialize(qint32 screenw, qint32 screenh);
    void finalize();
    void renderCurrentPosition(qint32 rx, qint32 ry, qint32 lx, qint32 ly);
    QOpenGLFramebufferObject * getFBO() { return m_pFbo;}


private:

    const qreal K_LARGE_D = 0.1;
    const qreal K_SMALL_D = 0.02;

    QOpenGLContext *m_pOpenGLContext;
    QOpenGLFramebufferObject *m_pFbo;
    QOffscreenSurface *m_pOffscreenSurface;

    QGraphicsScene *canvas;
    QGraphicsEllipseItem *leftEye;
    QGraphicsEllipseItem *rightEye;
    qreal r;
};

#endif // TARGETTEST_H

targettest.cpp目标测试文件

#include "targettest.h"

TargetTest::TargetTest()
{
    canvas = nullptr;
    leftEye = nullptr;
    rightEye = nullptr;
}

void TargetTest::initialize(qint32 screenw, qint32 screenh){

    canvas = new QGraphicsScene(0,0,screenw,screenh);
    canvas->setBackgroundBrush(QBrush(Qt::gray));

    // Qt OpenGL Initialization.
    QSurfaceFormat format;
    format.setMajorVersion( 4 );
    format.setMinorVersion( 1 );
    format.setProfile( QSurfaceFormat::CompatibilityProfile );

    m_pOpenGLContext = new QOpenGLContext();
    m_pOpenGLContext->setFormat( format );
    if( !m_pOpenGLContext->create() ){
        qDebug() << "Open GL Context initialization failed";
    }


    // create an offscreen surface to attach the context and FBO to
    m_pOffscreenSurface = new QOffscreenSurface();
    m_pOffscreenSurface->create();
    m_pOpenGLContext->makeCurrent( m_pOffscreenSurface );

    m_pFbo = new QOpenGLFramebufferObject(screenw, screenh, GL_TEXTURE_2D );

    qreal R = K_LARGE_D*screenw/2;
    r = K_SMALL_D*screenw/2;

    qreal horizontal_target_space =  (screenw -4*2*R)/2;
    qreal vertical_target_space   =  (screenh -4*2*R)/2;
    qreal horizontal_margin       =  R;
    qreal vertical_margin         =  R;
    qreal offset = (R-r);



    QList<QPointF> largeTargetUpperRight;
    largeTargetUpperRight << QPointF(horizontal_margin                                  ,vertical_margin)
                          << QPointF(horizontal_margin + 2*R + horizontal_target_space  ,vertical_margin)
                          << QPointF(horizontal_margin + 4*R + 2*horizontal_target_space,vertical_margin)
                          << QPointF(horizontal_margin                                  ,vertical_margin + 2*R + vertical_target_space)
                          << QPointF(horizontal_margin + 2*R + horizontal_target_space  ,vertical_margin + 2*R + vertical_target_space)
                          << QPointF(horizontal_margin + 4*R + 2*horizontal_target_space,vertical_margin + 2*R + vertical_target_space)
                          << QPointF(horizontal_margin                                  ,vertical_margin + 4*R + 2*vertical_target_space)
                          << QPointF(horizontal_margin + 2*R + horizontal_target_space  ,vertical_margin + 4*R + 2*vertical_target_space)
                          << QPointF(horizontal_margin + 4*R + 2*horizontal_target_space,vertical_margin + 4*R + 2*vertical_target_space);

    for (qint32 i = 0; i < largeTargetUpperRight.size(); i++){
        QGraphicsEllipseItem *circle = canvas->addEllipse(0,0,2*R,2*R,QPen(Qt::black),QBrush(Qt::darkBlue));
        QGraphicsEllipseItem *innerCircle = canvas->addEllipse(0,0,2*r,2*r,QPen(Qt::black),QBrush(Qt::yellow));
        qreal x = largeTargetUpperRight.at(i).x();
        qreal y = largeTargetUpperRight.at(i).y();
        circle->setPos(x,y);
        innerCircle->setPos(x+offset,y+offset);
    }

    // Initializing the
    leftEye = canvas->addEllipse(0,0,2*r,2*r,QPen(),QBrush(QColor(0,0,255,100)));
    rightEye = canvas->addEllipse(0,0,2*r,2*r,QPen(),QBrush(QColor(0,255,0,100)));


}

void TargetTest::renderCurrentPosition(qint32 rx, qint32 ry, qint32 lx, qint32 ly){

    if (!canvas) return;
    leftEye->setPos(lx-r,ly-r);
    rightEye->setPos(rx-r,ry-r);

//    m_pOpenGLContext->makeCurrent( m_pOffscreenSurface );
//    m_pFbo->bind();

    QOpenGLPaintDevice device( m_pFbo->size() );
    QPainter painter( &device );
    canvas->render( &painter );
//    m_pFbo->release();

}

TargetTest::~TargetTest(){
    finalize();
}

void TargetTest::finalize(){
    if (canvas){
        delete canvas;
        canvas = nullptr;
    }
    leftEye = nullptr;
    rightEye = nullptr;
}

For completion this my mainwindow.h为了完成这个我的 mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

And my mainwindow.cpp还有我的 mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

my main.cpp我的 main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

And finally my mainwindow.ui file最后是我的 mainwindow.ui 文件

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0">
     <widget class="OpenGLCanvas" name="openGLWidget"/>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>20</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <customwidgets>
  <customwidget>
   <class>OpenGLCanvas</class>
   <extends>QOpenGLWidget</extends>
   <header location="global">openglcanvas.h</header>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>

So what happens is that when I run the program I simply see the screen being filled with the color loaded in glClearColor (I've tested changing the color from black and I can confirm this is what I see) and that is it.所以发生的情况是,当我运行程序时,我只是看到屏幕填充了 glClearColor 中加载的颜色(我已经测试过将颜色从黑色更改,我可以确认这是我看到的),就是这样。

So what am I doing wrong?那么我做错了什么?

UPDATE: I have modified the code in paintGL() with @Rabbid76, however the problem has not changed更新:我用@Rabbid76 修改了paintGL() 中的代码,但是问题没有改变

UPDATE2: After removing some code as suggested by a comment Now I see this: UPDATE2:按照评论的建议删除一些代码后,现在我看到了:

我看到的错误

What is supposed to look like is the little gray square with the circle should occupy the full screen as it should cover the full quad.看起来应该是带有圆圈的灰色小方块应该占据整个屏幕,因为它应该覆盖整个四边形。

There are a few problems with the code shown but the main one is with the way you create and use the FBO.显示的代码存在一些问题,但主要问题在于您创建和使用 FBO 的方式。 In TargetTest::initialize you have...TargetTest::initialize你有...

QSurfaceFormat format;
format.setMajorVersion( 4 );
format.setMinorVersion( 1 );
format.setProfile( QSurfaceFormat::CompatibilityProfile );

m_pOpenGLContext = new QOpenGLContext();
m_pOpenGLContext->setFormat( format );
if( !m_pOpenGLContext->create() ){
    qDebug() << "Open GL Context initialization failed";
}


// create an offscreen surface to attach the context and FBO to
m_pOffscreenSurface = new QOffscreenSurface();
m_pOffscreenSurface->create();
m_pOpenGLContext->makeCurrent( m_pOffscreenSurface );

m_pFbo = new QOpenGLFramebufferObject(screenw, screenh, GL_TEXTURE_2D );

In this case, however, there's no need to create a new GL context -- just create the FBO in the current context.但是,在这种情况下,不需要创建新的 GL 上下文——只需在当前上下文中创建 FBO。 So the code above becomes simply...所以上面的代码变得简单......

m_pFbo = new QOpenGLFramebufferObject(screenw, screenh, GL_TEXTURE_2D);

Now consider the TargetTest::renderCurrentPosition implementation...现在考虑TargetTest::renderCurrentPosition实现...

if (!canvas)
    return;
leftEye->setPos(lx-r,ly-r);
rightEye->setPos(rx-r,ry-r);
QOpenGLPaintDevice device( m_pFbo->size() );
QPainter painter( &device );
canvas->render( &painter );

Here you don't bind the FBO so the QOpenGLPaintDevice is associated with the default frame buffer and the QGraphicsScene is rendered to that rather than the texture attached to the FBO.在这里,您没有绑定 FBO,因此QOpenGLPaintDevice与默认帧缓冲区相关联,并且QGraphicsScene被渲染到该缓冲区,而不是附加到 FBO 的纹理。 The above should become...以上应该变成...

if (!canvas)
    return;
leftEye->setPos(lx-r,ly-r);
rightEye->setPos(rx-r,ry-r);
m_pFbo->bind();
QOpenGLPaintDevice device( m_pFbo->size() );
QPainter painter( &device );
canvas->render( &painter );
m_pFbo->release();

Now the QGraphicsScene will be rendered to the correct texture and used as expected by OpenGLCanvas::paintGL .现在QGraphicsScene将被渲染为正确的纹理, OpenGLCanvas::paintGL预期使用。

The two changes shown above are the main part of the fix but there are additional issues.上面显示的两个更改是修复的主要部分,但还有其他问题。 Firstly, from the QOpenGLPaintDevice documentation ...首先,从QOpenGLPaintDevice文档...

When painting to a QOpenGLPaintDevice using QPainter, the state of the current OpenGL context will be altered by the paint engine to reflect its needs.当使用 QPainter 绘制到 QOpenGLPaintDevice 时,绘制引擎将更改当前 OpenGL 上下文的状态以反映其需求。 Applications should not rely upon the OpenGL state being reset to its original conditions, particularly the current shader program, OpenGL viewport, texture units, and drawing modes.应用程序不应依赖于将 OpenGL 状态重置为其原始条件,尤其是当前的着色器程序、OpenGL 视口、纹理单元和绘图模式。

This is a bit vague but in the case in hand it is necessary to fix up the viewport once rendering to the FBO is complete.这有点含糊,但在手头的情况下,一旦渲染到 FBO 完成,就有必要修复视口。 So change OpenGLCanvas::paintGL such that it calls glViewport immediately after the call to `TargetTest::renderCurrentPosition)...因此更改OpenGLCanvas::paintGL使其在调用 `TargetTest::renderCurrentPosition) 后立即调用glViewport ...

targetTest.renderCurrentPosition(0,0,0,0);
glViewport(0, 0, width(), height());

One other issue is that you potentially make use of GL calls before any context has been created and made current.另一个问题是,您可能会在创建任何上下文并使其成为当前上下文之前使用 GL 调用。 Your OpenGLCanvas ctor implementation is...您的OpenGLCanvas ctor 实现是...

OpenGLCanvas::OpenGLCanvas(QWidget *parent):QOpenGLWidget(parent)
{
    xrot = yrot = zrot = 0.0;
    targetTest.initialize(100,100);
    initializeOpenGLFunctions();
}

Both targetTest.initialize(100,100) and initializeOpenGLFunctions() assume a valid GL context. targetTest.initialize(100,100)initializeOpenGLFunctions()都假定一个有效的 GL 上下文。 These should be removed from the ctor and added to the top of OpenGLCanvas::initializeGL ...这些应该从OpenGLCanvas::initializeGL删除并添加到OpenGLCanvas::initializeGL ...

void OpenGLCanvas::initializeGL ()
{
    initializeOpenGLFunctions();        
    targetTest.initialize(100,100);
    glClearColor(0.0,0.0,1.0,0.0);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
}

Doing all of the above results in (for me at least) the following frame...执行上述所有操作会导致(至少对我而言)以下框架...

在此处输入图片说明


As an aside, you're currently using the old fixed pipeline functionality such as glVertex3d etc. That has been deprecated for quite some time and may not even be available on certain platforms. glVertex3dglVertex3d ,您目前正在使用旧的固定管道功能,例如glVertex3d等。该功能已被弃用一段时间,甚至可能无法在某些平台上使用。 Instead you should move to 'modern' OpenGL that makes use of buffer objects, shaders etc. To that end you might want to look at a website such as learnopengl.com .相反,您应该转向使用缓冲区对象、着色器等的“现代”OpenGL。为此,您可能需要查看诸如learnopengl.com 之类的网站。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM