[QOpenGLWidget] How to port the Qt Hello triangle example with my own shaders?
-
Hello,
After exercising outside of Qt with OpenGL with Glew and GLFW in Xcode on my mac, I followed the example of the OpenGL Window with the triangle in the Qt OpenGL Window example. This went fine. After this, I realised that since Qt 5.4, QOpenGLWidget was introduced. I followed a very simple example there. This example is rather different from what i'm used to. No custom shaders are used, and the vertices are not nicely packed in an array but sent one by one with gl commands.
So, I tried to "port" this example to something closer to the Qt OpenGL Window example while using QOpenGLWidget instead, but I was unsuccessful, neither the black background nor the triangle display.
What I did uses a class "OGLWidget" subclass of QOpenGLWidget, with a a basic UI made of one "Widget", just like in the answer to the post here. In the OGLWidget class, I tried to put commands in a similar manner as the Qt OpenGL Window example:oglwidget.h :
#include <QWidget> #include <QOpenGLWidget> #include <QtGui/QWindow> #include <QtGui/QOpenGLFunctions> #include <QtGui/QOpenGLShaderProgram> class QPainter; class QOpenGLContext; class QOpenGLPaintDevice; class OGLWidget : public QOpenGLWidget { public: OGLWidget(QWidget *parent = 0); ~OGLWidget(); protected: void initializeGL(); void resizeGL(int w, int h); void paintGL(); private: QOpenGLContext *m_context; QOpenGLPaintDevice *m_device; GLuint loadShader(GLenum type, const char *source); GLuint m_posAttr; GLuint m_colAttr; GLuint m_matrixUniform; QOpenGLShaderProgram *m_program; };
oglwidget.cpp
#include "oglwidget.h" #include <QOpenGLWidget> #include <QtGui/QWindow> #include <QtGui/QOpenGLFunctions> #include <QtGui/QOpenGLShaderProgram> OGLWidget::OGLWidget(QWidget *parent) :QOpenGLWidget(parent) , m_program(0) { } OGLWidget::~OGLWidget() { delete m_device; } static const char *vertexShaderSource = "attribute highp vec4 posAttr;\n" "attribute lowp vec4 colAttr;\n" "varying lowp vec4 col;\n" "uniform highp mat4 matrix;\n" "void main() {\n" " col = colAttr;\n" " gl_Position = matrix * posAttr;\n" "}\n"; static const char *fragmentShaderSource = "varying lowp vec4 col;\n" "void main() {\n" " gl_FragColor = col;\n" "}\n"; GLuint OGLWidget::loadShader(GLenum type, const char *source) { GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &source, 0); glCompileShader(shader); return shader; } void OGLWidget::initializeGL() { m_program = new QOpenGLShaderProgram(this); m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource); m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource); m_program->link(); m_posAttr = m_program->attributeLocation("posAttr"); m_colAttr = m_program->attributeLocation("colAttr"); m_matrixUniform = m_program->uniformLocation("matrix"); } void OGLWidget::paintGL() { const qreal retinaScale = devicePixelRatio(); glViewport(0, 0, width() * retinaScale, height() * retinaScale); glClear(GL_COLOR_BUFFER_BIT); m_program->bind(); QMatrix4x4 matrix; matrix.perspective(60.0f, 4.0f/3.0f, 0.1f, 100.0f); matrix.translate(0, 0, -2); m_program->setUniformValue(m_matrixUniform, matrix); GLfloat vertices[] = { 0.0f, 0.707f, -0.5f, -0.5f, 0.5f, -0.5f }; GLfloat colors[] = { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f }; glVertexAttribPointer(m_posAttr, 2, GL_FLOAT, GL_FALSE, 0, vertices); glVertexAttribPointer(m_colAttr, 3, GL_FLOAT, GL_FALSE, 0, colors); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glDrawArrays(GL_TRIANGLES, 0 , 3); glDisableVertexAttribArray(1); glDisableVertexAttribArray(0); } void OGLWidget::resizeGL(int w, int h) { }
You will note that I was not sure if I shall put commands in the resize, since i'm using a Widget in the UI that cannot be resized. So it is left empty.
mainwindow.h :
#include <QMainWindow> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private: Ui::MainWindow *ui; };
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; }
main.cpp
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }
The end goal though is to display a QImage as a texture, in a QScrollArea so I can scroll through an image that may be bigger than my screen.
In fact, I don't know if QOpenGLWidget will be necessary at all, and if I should stick to something like the Qt OpenGL Window example which relies on this more custom class:openglwindow.h :
class OpenGLWindow : public QWindow, protected QOpenGLFunctions { Q_OBJECT public: explicit OpenGLWindow(QWindow *parent=0); ~OpenGLWindow(); virtual void render(QPainter *painter); virtual void render(); virtual void initialize(); void setAnimating(bool animating); public slots: void renderLater(); void renderNow(); protected: bool event(QEvent *event) Q_DECL_OVERRIDE; void exposeEvent(QExposeEvent *event) Q_DECL_OVERRIDE; private: bool m_update_pending; bool m_animating; QOpenGLContext *m_context; QOpenGLPaintDevice *m_device; };
openglwindow.cpp
#include <QtCore/QCoreApplication> #include <QtGui/QOpenGLContext> #include <QtGui/QOpenGLPaintDevice> #include <QtGui/QPainter> OpenGLWindow::OpenGLWindow(QWindow *parent) : QWindow(parent) , m_update_pending(false) , m_animating(false) , m_context(0) , m_device(0) { setSurfaceType(QWindow::OpenGLSurface); // the window is to be used for OpenGL rendering } OpenGLWindow::~OpenGLWindow() { delete m_device; } void OpenGLWindow::render(QPainter *painter) { Q_UNUSED(painter); } void OpenGLWindow::initialize() { } void OpenGLWindow::render() { if (!m_device) m_device = new QOpenGLPaintDevice; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); m_device->setSize(size()); QPainter painter(m_device); render(&painter); } void OpenGLWindow::renderLater() { if (!m_update_pending) { m_update_pending = true; QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); } } bool OpenGLWindow::event(QEvent *event) { switch (event->type()) { case QEvent::UpdateRequest: m_update_pending = false; renderNow(); return true; default: return QWindow::event(event); } } void OpenGLWindow::exposeEvent(QExposeEvent *event) { Q_UNUSED(event); if (isExposed()) renderNow(); } void OpenGLWindow::renderNow() { if (!isExposed()) return; bool needsInitialize = false; if (!m_context) { m_context = new QOpenGLContext(this); m_context->setFormat(requestedFormat()); m_context->create(); needsInitialize = true; } m_context->makeCurrent(this); if (needsInitialize) { initializeOpenGLFunctions(); initialize(); } render(); m_context->swapBuffers(this); if (m_animating) renderLater(); } void OpenGLWindow::setAnimating(bool animating) { m_animating = animating; if (animating) renderLater(); }
which is then subclassed with:
trianglewindow.h :
#include <QtGui/QWindow> #include <QtGui/QOpenGLFunctions> #include <QtGui/QOpenGLShaderProgram> #include <QScreen> #include "openglwindow.h" class TriangleWindow : public OpenGLWindow { public: TriangleWindow(); void initialize() Q_DECL_OVERRIDE; void render() Q_DECL_OVERRIDE; private: GLuint loadShader(GLenum type, const char *source); GLuint m_posAttr; GLuint m_colAttr; GLuint m_matrixUniform; QOpenGLShaderProgram *m_program; int m_frame; }; TriangleWindow::TriangleWindow() : m_program(0) , m_frame(0) { } static const char *vertexShaderSource = "attribute highp vec4 posAttr;\n" "attribute lowp vec4 colAttr;\n" "varying lowp vec4 col;\n" "uniform highp mat4 matrix;\n" "void main() {\n" " col = colAttr;\n" " gl_Position = matrix * posAttr;\n" "}\n"; static const char *fragmentShaderSource = "varying lowp vec4 col;\n" "void main() {\n" " gl_FragColor = col;\n" "}\n"; GLuint TriangleWindow::loadShader(GLenum type, const char *source) { GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &source, 0); glCompileShader(shader); return shader; } void TriangleWindow::initialize() { m_program = new QOpenGLShaderProgram(this); m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource); m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource); m_program->link(); m_posAttr = m_program->attributeLocation("posAttr"); m_colAttr = m_program->attributeLocation("colAttr"); m_matrixUniform = m_program->uniformLocation("matrix"); } void TriangleWindow::render() { const qreal retinaScale = devicePixelRatio(); glViewport(0, 0, width() * retinaScale, height() * retinaScale); glClear(GL_COLOR_BUFFER_BIT); m_program->bind(); QMatrix4x4 matrix; matrix.perspective(60.0f, 4.0f/3.0f, 0.1f, 100.0f); matrix.translate(0, 0, -2); matrix.rotate(100.0f * m_frame / screen()->refreshRate(), 0, 1, 0); m_program->setUniformValue(m_matrixUniform, matrix); GLfloat vertices[] = { 0.0f, 0.707f, -0.5f, -0.5f, 0.5f, -0.5f }; GLfloat colors[] = { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f }; glVertexAttribPointer(m_posAttr, 2, GL_FLOAT, GL_FALSE, 0, vertices); glVertexAttribPointer(m_colAttr, 3, GL_FLOAT, GL_FALSE, 0, colors); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glDrawArrays(GL_TRIANGLES, 0 , 3); glDisableVertexAttribArray(1); glDisableVertexAttribArray(0); }
And which is the only thing that works for me so far.