QGraphicsView backed with QOpenGLWidget
Unsolved
General and Desktop
-
Hi, I'm trying to set up a
QGraphicsView
with aQOpenGLWidget
set to the viewport but the GL widget renders only white. I know this normally means that the texture is incorrect but that doesn't seem to be the case. I'm sure I am just missing some elementary step.I wrote a modified version of the Textures example (https://doc.qt.io/qt-5/qtopengl-textures-example.html) and then added a
QGraphicsView
andQGraphicsScene
.My code is below; you can switch off the view/scene code using the define
USE_VIEW
. You will need to supply your own image.HEADERS = window.h \ simpleglwidget.h SOURCES = main.cc \ window.cc \ simpleglwidget.cc DEFINES = USE_VIEW=1 QT += core gui widgets opengl CONFIG += qt debug warn_all c++11 QMAKE_MAC_SDK = macosx10.12
#include <QApplication> #include <QSurfaceFormat> #include "window.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); QSurfaceFormat format; format.setDepthBufferSize(24); QSurfaceFormat::setDefaultFormat(format); Window window; window.show(); window.resize(720,480); window.SetTestImage(); return app.exec(); }
window.h:
#ifndef WINDOW_H #define WINDOW_H #include <QGraphicsView> #include <QGraphicsScene> class SimpleGLWidget; class Window : public QWidget { Q_OBJECT public: Window(); void SetTestImage(); private: QGraphicsView *view; QGraphicsScene *scene; SimpleGLWidget *myGLWidget; }; #endif
#include <QtWidgets> #include "window.h" #include "simpleglwidget.h" Window::Window() { QVBoxLayout *mainLayout = new QVBoxLayout; myGLWidget = new SimpleGLWidget; myGLWidget->setClearColor(Qt::black); #if USE_VIEW view = new QGraphicsView(this); scene = new QGraphicsScene(view); view->setViewport(myGLWidget); view->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); view->setScene(scene); mainLayout->addWidget(view); #else mainLayout->addWidget(myGLWidget); #endif setLayout(mainLayout); setWindowTitle(tr("QGraphicsView Test")); } void Window::SetTestImage() { QImage input_image(QString("speedway17.png")); myGLWidget->SetImage(input_image); }
simpleglwidget.h:
#ifndef simpleglwidget_h #define simpleglwidget_h #include <QOpenGLWidget> #include <QOpenGLFunctions> #include <QOpenGLBuffer> #include <QMatrix4x4> QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram); QT_FORWARD_DECLARE_CLASS(QOpenGLTexture) class SimpleGLWidget : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT public: explicit SimpleGLWidget(QWidget *parent = 0); ~SimpleGLWidget(); void SetImage(const QImage &img); void setClearColor(const QColor &color); QSize minimumSizeHint() const Q_DECL_OVERRIDE; QSize sizeHint() const Q_DECL_OVERRIDE; void rotateBy(int xAngle, int yAngle, int zAngle); signals: void clicked(); protected: void initializeGL() Q_DECL_OVERRIDE; void paintGL() Q_DECL_OVERRIDE; void resizeGL(int width, int height) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; private: void makeObject(); void setView(int w, int h); QColor clearColour; /// Rotation angles (used in testing) int xRot; int yRot; int zRot; QPoint lastPos; QOpenGLShaderProgram *program; QOpenGLBuffer vbo; QOpenGLTexture *frame_texture; bool hasTexture; /// Compensating projection for origin position QMatrix4x4 comp; QMatrix4x4 projection; int scaledWidth, scaledHeight; float yAspect; float yAspectActual; int textureWidth, textureHeight; }; #endif
#include "simpleglwidget.h" #include <QOpenGLShaderProgram> #include <QOpenGLTexture> #include <QMouseEvent> #include <math.h> #define PI 3.14159265358979323846 SimpleGLWidget::SimpleGLWidget(QWidget *parent) : QOpenGLWidget(parent), clearColour(Qt::black), xRot(0), yRot(0), zRot(0), program(0), frame_texture(NULL) { comp = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); yAspect = 0.75; yAspectActual = 0.75; textureWidth = 1; textureHeight = 1; hasTexture = false; } SimpleGLWidget::~SimpleGLWidget() { makeCurrent(); if(vbo.isCreated()) vbo.destroy(); if(hasTexture) delete frame_texture; delete program; doneCurrent(); } // Set an image in the widget void SimpleGLWidget::SetImage(const QImage &img) { makeCurrent(); if(frame_texture) delete frame_texture; frame_texture = new QOpenGLTexture(img); textureWidth = img.width(); textureHeight = img.height(); yAspect = (float)(textureHeight)/textureWidth; hasTexture = true; makeObject(); doneCurrent(); setView(width(), height()); update(); } QSize SimpleGLWidget::minimumSizeHint() const { return(QSize(160, 90)); } QSize SimpleGLWidget::sizeHint() const { return(QSize(800, 450)); } // Rotation void SimpleGLWidget::rotateBy(int xAngle, int yAngle, int zAngle) { xRot += xAngle; yRot += yAngle; zRot += zAngle; update(); } void SimpleGLWidget::setClearColor(const QColor &color) { clearColour = color; update(); } void SimpleGLWidget::initializeGL() { initializeOpenGLFunctions(); glEnable(GL_DEPTH_TEST); // glEnable(GL_CULL_FACE); #define PROGRAM_VERTEX_ATTRIBUTE 0 #define PROGRAM_TEXCOORD_ATTRIBUTE 1 QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this); const char *vsrc = "attribute highp vec4 vertex;\n" "attribute mediump vec4 texCoord;\n" "varying mediump vec4 texc;\n" "uniform mediump mat4 matrix;\n" "void main(void)\n" "{\n" " gl_Position = matrix * vertex;\n" " texc = texCoord;\n" "}\n"; vshader->compileSourceCode(vsrc); QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this); const char *fsrc = "uniform sampler2D texture;\n" "varying mediump vec4 texc;\n" "void main(void)\n" "{\n" " gl_FragColor = texture2D(texture, texc.st);\n" "}\n"; fshader->compileSourceCode(fsrc); program = new QOpenGLShaderProgram; program->addShader(vshader); program->addShader(fshader); program->bindAttributeLocation("vertex", PROGRAM_VERTEX_ATTRIBUTE); program->bindAttributeLocation("texCoord", PROGRAM_TEXCOORD_ATTRIBUTE); program->link(); program->bind(); program->setUniformValue("texture", 0); } void SimpleGLWidget::paintGL() { glClearColor(clearColour.redF(), clearColour.greenF(), clearColour.blueF(), clearColour.alphaF()); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); QMatrix4x4 m; m = comp*projection; m.rotate(xRot / 16.0f, 1.0f, 0.0f, 0.0f); m.rotate(yRot / 16.0f, 0.0f, 1.0f, 0.0f); m.rotate(zRot / 16.0f, 0.0f, 0.0f, 1.0f); program->setUniformValue("matrix", m); program->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); program->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE); program->setAttributeBuffer(PROGRAM_VERTEX_ATTRIBUTE, GL_FLOAT, 0, 3, 5 * sizeof(GLfloat)); program->setAttributeBuffer(PROGRAM_TEXCOORD_ATTRIBUTE, GL_FLOAT, 3 * sizeof(GLfloat), 2, 5 * sizeof(GLfloat)); if(hasTexture) { frame_texture->bind(); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } } void SimpleGLWidget::resizeGL(int width, int height) { setView(width, height); } // Set up view port. (w, h) = current size of the widget void SimpleGLWidget::setView(int w, int h) { glViewport(0, 0, GLsizei(w), GLsizei(h)); // Set perspective projection if(h == 0) h = 1; float half_viewing_angle = atan(yAspect*0.25); if(((float)(h)/w) <= yAspect) { half_viewing_angle = atan(yAspect*0.25); if(h >= textureHeight) { half_viewing_angle = atan((yAspect*0.25)*(float)(h)/textureHeight); } } else { half_viewing_angle = atan(((float)(h)/w)*0.25); if(w >= textureWidth) { half_viewing_angle = atan((yAspect*0.25)*(float)(h)/textureHeight); } } float fov = 2.0*half_viewing_angle; float aspect = (float)(w)/(float)(h); float znear = 1.0f; float zfar = 1000.0f; projection = QMatrix4x4(); projection.perspective(fov * (180.0/PI), aspect, znear, zfar); // Qt uses degrees projection.translate(-0.5, -yAspect/2, -2.0); } void SimpleGLWidget::mousePressEvent(QMouseEvent *event) { lastPos = event->pos(); } void SimpleGLWidget::mouseMoveEvent(QMouseEvent *event) { int dx = event->x() - lastPos.x(); int dy = event->y() - lastPos.y(); if (event->buttons() & Qt::LeftButton) { rotateBy(8 * dy, 8 * dx, 0); } else if (event->buttons() & Qt::RightButton) { rotateBy(8 * dy, 0, 8 * dx); } lastPos = event->pos(); } void SimpleGLWidget::mouseReleaseEvent(QMouseEvent * /* event */) { emit clicked(); } void SimpleGLWidget::makeObject() { static const float coords[4][3] = { { 1.0f, yAspect, 0.0f }, { 0.0f, yAspect, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } }; QVector<GLfloat> vertData; for(int j = 0; j < 4; ++j) { // vertex position vertData.append(1.0 * coords[j][0]); vertData.append(1.0 * coords[j][1]); vertData.append(1.0 * coords[j][2]); // texture coordinate vertData.append(j == 0 || j == 3); vertData.append(j == 0 || j == 1); } vbo.create(); vbo.bind(); vbo.allocate(vertData.constData(), vertData.count() * sizeof(GLfloat)); }
I am developing on OSX El Capitan, Qt 5.5.1 but also experience the same behaviour in Debian Jessie, Qt 5.7