Let's say I want to render an animated triangle using two threads. One thread manipulates the vertices and the other one manages the QT window and renders the GL context. I have a class cTriangle, with members Eigen::Vector3d m_A, m_B, m_C representing the triangle vertices and a method void updateCoordinates() that operates on the vertices.
cTriangle.h:
#ifndef CTRIANGLE_H
#define CTRIANGLE_H
#include <Eigen/Core>
#include <QObject>
#include <vector>
class cTriangle : public QObject
{
Q_OBJECT
public:
cTriangle();
public:
Eigen::Vector3d m_A;
Eigen::Vector3d m_B;
Eigen::Vector3d m_C;
public slots:
void updateCoordinates();
};
#endif // CTRIANGLE_H
cTriangle.cpp:
#include "cTriangle.h"
#include <iostream>
cTriangle::cTriangle()
{
m_A = Eigen::Vector3d(0.0, 0.0, 0.0);
m_B = Eigen::Vector3d(0.2, 0.0, 0.0);
m_C = Eigen::Vector3d(0.0, 0.2, 0.0);
}
void cTriangle::updateCoordinates()
{
int ctr = 0;
double t = 0.0;
while (true)
{
std::cout << "cTriangle::updateCoordinates " << ctr << std::endl;
double offset = 0.3*sin(t);
m_A = Eigen::Vector3d(offset, 0.0, 0.0);
t += 0.00001;
ctr++;
}
}
The driver creates a new cTriangle and initiates a QThread, which continuously loops over updateCoordinates(). I also initialize the MainWindow containing a GLWidget.
main.cpp:
int main(int argc, char *argv[])
{
cTriangle* tri = new cTriangle();
QThread* thread = new QThread;
tri->moveToThread(thread);
QObject::connect(thread, SIGNAL(started()), tri, SLOT(updateCoordinates()) );
thread->start();
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
glwidget.h:
#ifndef GLWIDGET_H
#define GLWIDGET_H
#include <QGLWidget>
#include "cTriangle.h"
class GLWidget : public QGLWidget
{
Q_OBJECT
public:
explicit GLWidget(QWidget *parent = 0);
void initializeGL();
void resizeGL(int w, int h);
private:
signals:
void repaint();
public slots:
void paintGL();
};
#endif // GLWIDGET_H
glwidget.cpp:
GLWidget::GLWidget(QWidget *parent) : QGLWidget(parent)
{
}
void GLWidget::initializeGL()
{
glClearColor(0.2, 0.2, 0.2, 1);
}
void GLWidget::paintGL()
{
std::cout << "paintGL " << std::endl;
// ********************************
// How do I access Triangle data here??
// ********************************
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glBegin(GL_TRIANGLES);
glColor3f (1,0,0);
glVertex3f (0, -0.5, 0);
glColor3f (0,1,0);
glVertex3f (0.5, -0.5, 0);
glColor3f (0,0,1);
glVertex3f (0.0, 0.5, 0);
glEnd();
}
void GLWidget::resizeGL(int w, int h)
{}
Finally, my question: I need to access the vertex data in the object tri from within GLWidget::painGL. What is the best method for doing so? Setting up signal-slot functionality? Passing a pointer of tri in a thread-safe manner (if so, how?). This is just a toy example, generally the vertex data could be very large. Thanks!!
=============================
One step further:
My plan now is set set up a signal->signal->slot mechanics. GLWidget::glPaint() emits a SIGNAL GLWidget::requestVertices() to another SIGNAL cTriangle::sendVertices(whateverType vertexData), which is then received by SLOT GLWidget::receiveVertices(whateverType vertexData). In main.cpp, how do I CONNECT these signals? How do I get the reference to the GLWidget object? QObject::connect( ???, SIGNAL( requestVertices() ), tri, SIGNAL( sendVertices(double arg) ) );
Start by using the signal and slot mechanism. If you emit a signal with a QList of points, the QList uses implicit sharing, so will only copy-on-write.
This is the simplest method for using data across threads and doesn't require any handling of locks by you. If at some point you find this to be too slow, then you can look into optimising the code to improve the speed.
One possible method of optimisation would be to use multiple QList of points, so you write to one, then move on to writing to the next, while emitting the first, to ensure you're not writing to the list being shared and thus minimising the chance of a copy-on-write occurring.
However, there is no need to optimise, unless you're seeing a problem in the first place.
Related
Referencing off of a 7 year old question: How do I render a triangle in QOpenGLWidget?
The accepted answer here gives a very detailed explanation of how to setup an example, but as numerous comments on that answer state (some years later), there are parts of the sample that are deprecated or are no longer best-practice.
Can anyone explain how to do this now, in Qt6+ without using glBegin/glEnd and without using GLU?
I ultimately need to be able to build a GUI around an OpenGL context, with the OpenGL being able to render 3D models as a wireframe, without any kind of shaders or textures mapped onto it.
I tried to work from the cube example. I was able to add GUI elements, but they render on top of the OpenGL window instead of above or around it and I am unsure of how to change the code to fix that. I was able to feed in a 3D geometry from file and get it to plot that, but it maps the cube.png texture from the example onto anything I plot and I haven't been able to get it to render a wireframe instead of a texture.
Edit 4: I guess I'll call this solved at this point. Referencing this thread, I learned you can add other widgets besides the central widget, just not normal widgets, they have to be dock widgets for some reason (as far as I can tell). I have updated the code below to reflect this image, which is a 'working' solution to the questions that I asked here. Huge thanks to user 'new QOpenGLWidget' for all of their help!
main.cpp
#include <QApplication>
#include <QLabel>
#include <QSurfaceFormat>
#ifndef QT_NO_OPENGL
#include "mainwidget.h"
#endif
#include "geometryengine.h"
#include "storedGeometry.h"
extern "C" {
// this fortran function is called by cpp
void rk_viz_f90(const char *geoname, int str_len=0); // length is optional, default 0, pass by value
// this cpp function is called by fortran
void send_facet(float in[][3])
{
gUseGeom.addFacet(GeometryEngine::facetData(QVector3D(in[0][0],in[0][1],in[0][2]),QVector3D(in[1][0],in[1][1],in[1][2]),QVector3D(in[2][0],in[2][1],in[2][2])));
}
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QSurfaceFormat format;
format.setDepthBufferSize(24);
QSurfaceFormat::setDefaultFormat(format);
app.setApplicationName("cube");
app.setApplicationVersion("0.1");
// Call Fortran Rk_Viz Lib version
std::string geofile = "C:\\TEMP\\qt\\demo_send_arrays\\sphere_6in_PW.RawRkViz.bin";
printf("C++ filename %s\n",geofile.c_str());
const char * geoname = geofile.c_str();
rk_viz_f90(geoname,geofile.size());
#ifndef QT_NO_OPENGL
MainWindow window;
window.setFixedSize(600,800);
window.show();
#else
QLabel note("OpenGL Support required");
note.show();
#endif
return app.exec();
}
mainwindow.h - newly added
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "vizglwidget.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
public slots:
private:
VizGlWidget *glWidget; // pointer to vizglwidget
QPushButton *loadButton;
void setupGui();
};
#endif // MAINWINDOW_H
mainwindow.cpp - newly added
#include "mainwindow.h"
#include <QGroupBox>
#include <QGridLayout>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
setWindowTitle("cube");
setupGui();
}
MainWindow::~MainWindow()
{
}
void MainWindow::setupGui()
{
// Try docking widgets with GL as central widget
glWidget = new VizGlWidget();
setCentralWidget(glWidget);
setStatusBar(new QStatusBar(this));
QDockWidget* dock1 = new QDockWidget;
this->addDockWidget(Qt::TopDockWidgetArea, dock1);
dock1->setMinimumSize(800,200);
QGridLayout *layout = new QGridLayout;
loadButton = new QPushButton(QString("Load Bin File..."),this);
layout->addWidget(loadButton,0,0,1,1,Qt::AlignHCenter);
dock1->setLayout(layout);
}
vizglwidget.h - formerly mainwidget.h
#ifndef VIZGLWIDGET_H
#define VIZGLWIDGET_H
#include "geometryengine.h"
#include "storedGeometry.h"
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QMatrix4x4>
#include <QQuaternion>
#include <QVector2D>
#include <QBasicTimer>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QPushButton>
class GeometryEngine;
class VizGlWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
using QOpenGLWidget::QOpenGLWidget;
~VizGlWidget();
protected:
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void timerEvent(QTimerEvent *e) override;
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
void initShaders();
void initTextures();
private:
std::vector<GeometryEngine::facetData> *pUseGeom = nullptr;
QBasicTimer timer;
QOpenGLShaderProgram program;
GeometryEngine *geometries = nullptr;
QOpenGLTexture *texture = nullptr;
QMatrix4x4 projection;
QVector2D mousePressPosition;
QVector3D rotationAxis;
qreal angularSpeed = 0;
QQuaternion rotation;
};
#endif // VIZGLWIDGET_H
vizglwidget.cpp - formerly mainwidget.cpp
#include "vizglwidget.h"
#include <QMouseEvent>
#include <cmath>
VizGlWidget::~VizGlWidget()
{
// Make sure the context is current when deleting the texture
// and the buffers.
makeCurrent();
delete texture;
delete geometries;
doneCurrent();
}
void VizGlWidget::mousePressEvent(QMouseEvent *e)
{
// Save mouse press position
mousePressPosition = QVector2D(e->position());
}
void VizGlWidget::mouseReleaseEvent(QMouseEvent *e)
{
// Mouse release position - mouse press position
QVector2D diff = QVector2D(e->position()) - mousePressPosition;
// Rotation axis is perpendicular to the mouse position difference
// vector
QVector3D n = QVector3D(diff.y(), diff.x(), 0.0).normalized();
// Accelerate angular speed relative to the length of the mouse sweep
qreal acc = diff.length() / 100.0;
// Calculate new rotation axis as weighted sum
rotationAxis = (rotationAxis * angularSpeed + n * acc).normalized();
// Increase angular speed
angularSpeed += acc;
}
void VizGlWidget::timerEvent(QTimerEvent *)
{
// Decrease angular speed (friction)
angularSpeed *= 0.99;
// Stop rotation when speed goes below threshold
if (angularSpeed < 0.01) {
angularSpeed = 0.0;
} else {
// Update rotation
rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angularSpeed) * rotation;
// Request an update
update();
}
}
void VizGlWidget::initializeGL()
{
initializeOpenGLFunctions();
glClearColor(0, 0, 0, 1);
initShaders();
initTextures();
// Enable depth buffer
glEnable(GL_DEPTH_TEST);
// Enable back face culling
//glEnable(GL_CULL_FACE);
geometries = new GeometryEngine();
// Use QBasicTimer because its faster than QTimer
timer.start(12, this);
}
void VizGlWidget::initShaders()
{
// Compile vertex shader
if (!program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vshader.glsl"))
close();
// Compile fragment shader
if (!program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fshader.glsl"))
close();
// Link shader pipeline
if (!program.link())
close();
// Bind shader pipeline for use
if (!program.bind())
close();
}
void VizGlWidget::initTextures()
{
// Load cube.png image
texture = new QOpenGLTexture(QImage(":/cube.png").mirrored());
// Set nearest filtering mode for texture minification
texture->setMinificationFilter(QOpenGLTexture::Nearest);
// Set bilinear filtering mode for texture magnification
texture->setMagnificationFilter(QOpenGLTexture::Linear);
// Wrap texture coordinates by repeating
// f.ex. texture coordinate (1.1, 1.2) is same as (0.1, 0.2)
texture->setWrapMode(QOpenGLTexture::Repeat);
}
void VizGlWidget::resizeGL(int w, int h)
{
// Calculate aspect ratio
qreal aspect = qreal(w) / qreal(h ? h : 1);
// Set near plane to 3.0, far plane to 7.0, field of view 45 degrees
//const qreal zNear = 3.0, zFar = 7.0, fov = 45.0;
const qreal zNear = 0.1, zFar = 10.0, fov = 30.0;
// Reset projection
projection.setToIdentity();
// Set perspective projection
projection.perspective(fov, aspect, zNear, zFar);
}
void VizGlWidget::paintGL()
{
// Clear color and depth buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
texture->bind();
// Calculate model view transformation
QMatrix4x4 matrix;
matrix.translate(0.0, 0.0, -1);
matrix.rotate(rotation);
// Set modelview-projection matrix
program.setUniformValue("mvp_matrix", projection * matrix);
// Use texture unit 0 which contains cube.png
program.setUniformValue("texture", 0);
// Draw cube geometry
geometries->drawCubeGeometry(&program);
}
geometryengine.h
#ifndef GEOMETRYENGINE_H
#define GEOMETRYENGINE_H
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
class GeometryEngine : protected QOpenGLFunctions
{
public:
struct facetData
{
QVector3D v1;
QVector3D v2;
QVector3D v3;
facetData() {
};
facetData(QVector3D iv1, QVector3D iv2, QVector3D iv3) {
v1 = iv1;
v2 = iv2;
v3 = iv3;
};
~facetData() {
v1.~QVector3D();
v2.~QVector3D();
v3.~QVector3D();
};
};
GeometryEngine();
virtual ~GeometryEngine();
void drawCubeGeometry(QOpenGLShaderProgram *program);
private:
void initCubeGeometry();
QOpenGLBuffer arrayBuf;
QOpenGLBuffer indexBuf;
};
#endif // GEOMETRYENGINE_H
geometryengine.cpp
#include "geometryengine.h"
#include "storedGeometry.h"
#include <QVector2D>
#include <QVector3D>
#include <algorithm>
GeometryEngine::GeometryEngine()
: indexBuf(QOpenGLBuffer::IndexBuffer)
{
initializeOpenGLFunctions();
// Generate 2 VBOs
arrayBuf.create();
indexBuf.create();
// Initializes cube geometry and transfers it to VBOs
initCubeGeometry();
}
GeometryEngine::~GeometryEngine()
{
arrayBuf.destroy();
indexBuf.destroy();
}
void GeometryEngine::initCubeGeometry()
{
// Get a copy of the geometry to reference here
std::vector<GeometryEngine::facetData> tGeom = gUseGeom.getGeom();
// Convert vector to array
GeometryEngine::facetData* aGeom = tGeom.data();
// Get a copy of the generated indices to reference here
std::vector<GLushort> tInd = gUseGeom.getGenIndices();
// Convert vector to array
GLushort* aInd = tInd.data();
// Transfer vertex data to VBO 0
arrayBuf.bind();
arrayBuf.allocate(aGeom, tGeom.size() * sizeof(GeometryEngine::facetData));
// Transfer index data to VBO 1
indexBuf.bind();
indexBuf.allocate(aInd, tInd.size() * sizeof(GLushort));
}
void GeometryEngine::drawCubeGeometry(QOpenGLShaderProgram *program)
{
// Tell OpenGL which VBOs to use
arrayBuf.bind();
indexBuf.bind();
// Tell OpenGL programmable pipeline how to locate vertex position data
int vertexLocation = program->attributeLocation("a_position");
program->enableAttributeArray(vertexLocation);
// setAttributeBuffer(int location, GLenum type, int offset, int tupleSize, int stride = 0)
program->setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3);
// Tell OpenGL programmable pipeline how to locate vertex texture coordinate data
int texcoordLocation = program->attributeLocation("a_texcoord");
program->enableAttributeArray(texcoordLocation);
// original: program->setAttributeBuffer(texcoordLocation, GL_FLOAT, offset, 2, sizeof(VertexData));
program->setAttributeBuffer(texcoordLocation, GL_FLOAT, 0, 3);
// Draw cube geometry using indices from VBO 1
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
glDrawElements(GL_TRIANGLES, gUseGeom.gSize() * 3, GL_UNSIGNED_SHORT, nullptr);
}
storedgeometry.h
#ifndef STOREDGEOMETRY_H
#define STOREDGEOMETRY_H
#include "geometryengine.h"
class storedGeometry
{
private:
std::vector<GeometryEngine::facetData> useGeom;
std::vector<std::vector<GLushort>> useInd;
std::vector<GLushort> genInd;
public:
// Constructor/Destructor
storedGeometry();
~storedGeometry();
// Setters
void setGeom(std::vector<GeometryEngine::facetData> inGeom);
void addFacet(GeometryEngine::facetData inFacet);
void setIndices(std::vector<std::vector<GLushort>> inInd);
void addIndices(std::vector<GLushort> inInd);
// Getters
std::vector<GeometryEngine::facetData> getGeom();
GeometryEngine::facetData getFacet(int pos);
int gSize();
int iSize();
std::vector<std::vector<GLushort>> getUseIndices();
std::vector<GLushort> getGenIndices();
std::vector<GLushort> getInd(int pos);
};
extern storedGeometry gUseGeom;
#endif // STOREDGEOMETRY_H
storedgeometry.cpp
#include "storedGeometry.h"
// Constructor
storedGeometry::storedGeometry()
{
std::vector<GeometryEngine::facetData> useGeom;
std::vector<GLushort> useInd;
std::vector<GLushort> genInd;
}
// Destructor
storedGeometry::~storedGeometry()
{
useGeom.clear();
useInd.clear();
genInd.clear();
}
// Setters
void storedGeometry::setGeom(std::vector<GeometryEngine::facetData> inGeom) {
useGeom = inGeom;
}
void storedGeometry::addFacet(GeometryEngine::facetData inFacet) {
useGeom.push_back(inFacet);
// also want to generate indices to go with this at the same time
// can take in indices from rkviz, but are not useful for this purpose
if (genInd.empty()) {
// case 1 - currently no indices, add 0, 1, 2
genInd.push_back(0);
genInd.push_back(1);
genInd.push_back(2);
} else {
// case 2 - already has indices, add n+1, n+1, n+2, n+3, n+3, where n is previous entry
GLushort tInd = genInd[genInd.size()-1];
genInd.push_back(tInd+1);
genInd.push_back(tInd+2);
genInd.push_back(tInd+3);
}
}
void storedGeometry::setIndices(std::vector<std::vector<GLushort>> inInd) {
useInd = inInd;
}
void storedGeometry::addIndices(std::vector<GLushort> inInd) {
useInd.push_back(inInd);
}
// Getters
std::vector<GeometryEngine::facetData> storedGeometry::getGeom() {
return useGeom;
}
GeometryEngine::facetData storedGeometry::getFacet(int pos) {
if (pos <= useGeom.size()) {
return useGeom[pos];
} else {
return useGeom[useGeom.size()];
}
}
int storedGeometry::gSize() {
return useGeom.size();
}
int storedGeometry::iSize() {
return useInd.size();
}
std::vector<std::vector<GLushort>> storedGeometry::getUseIndices() {
return useInd;
}
std::vector<GLushort> storedGeometry::getGenIndices() {
return genInd;
}
std::vector<GLushort> storedGeometry::getInd(int pos) {
if (pos <= useInd.size()) {
return useInd[pos];
} else {
return useInd[useInd.size()];
}
}
storedGeometry gUseGeom;
fshader.glsl
#ifdef GL_ES
// Set default precision to medium
precision mediump int;
precision mediump float;
#endif
// From example:
//uniform sampler2D texture;
//varying vec2 v_texcoord;
void main()
{
// Set fragment color from texture
//original: gl_FragColor = texture2D(texture, v_texcoord);
// Set fragment color to fixed color
gl_FragColor = vec4(1.0f,0.0f,0.0f,1.0f);
}
vshader.glsl
#ifdef GL_ES
// Set default precision to medium
precision mediump int;
precision mediump float;
#endif
uniform mat4 mvp_matrix;
attribute vec4 a_position;
attribute vec2 a_texcoord;
varying vec2 v_texcoord;
void main()
{
// Calculate vertex position in screen space
gl_Position = mvp_matrix * a_position;
// Pass texture coordinate to fragment shader
// Value will be automatically interpolated to fragments inside polygon faces
v_texcoord = a_texcoord;
}
For the GUIs, don't use QOpenGLWidget for them. If you do that it will automatically render the GUIs on top of the OpenGL stuff, because QOpenGLWidget forces the OpenGL window to appear in the entire screen. To fix this, add a wrapper class that extends QMainWindow to put both the MainWidget and the GUIs.
For the wireframe, try putting this code before calling glDrawElements:
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
More clarification on the wireframe:
Remove the texture and replace it with a uniform color like red:
gl_FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);
I have a simple form in which I add an OpenGLWidget, and then set my "MyOpenGLWidget" class to the Promoted Class,
it is successfully created, but is not displayed.
Its constructor is called, but the initializeGL, resizeGL, and paintGL functions are not called.
Here is all my code:
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
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;
}
ui_mainwindow.h
class Ui_MainWindow
{
public:
QWidget *centralwidget;
MyOpenGLWidget *openGLWidget;
void setupUi(QMainWindow *MainWindow)
{
if (MainWindow->objectName().isEmpty())
MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
MainWindow->resize(420, 300);
centralwidget = new QWidget(MainWindow);
centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
openGLWidget = new MyOpenGLWidget(centralwidget);
openGLWidget->setObjectName(QString::fromUtf8("openGLWidget"));
openGLWidget->setGeometry(QRect(60, 50, 300, 200));
MainWindow->setCentralWidget(centralwidget);
retranslateUi(MainWindow);
QMetaObject::connectSlotsByName(MainWindow);
} // setupUi
void retranslateUi(QMainWindow *MainWindow)
{
MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "MainWindow", nullptr));
} // retranslateUi
};
namespace Ui {
class MainWindow: public Ui_MainWindow {};
} // namespace Ui
QT_END_NAMESPACE
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
myopenglwidget.cpp
#include "myopenglwidget.h"
#include "globalvars.h"
#include "gl/GLU.h"
MyOpenGLWidget::MyOpenGLWidget(QWidget* parent)
{
qInfo() << "MyOpenGLWidget called";
}
MyOpenGLWidget::~MyOpenGLWidget()
{
qInfo() << "~MyOpenGLWidget called";
}
void MyOpenGLWidget::initializeGL()
{
qInfo() << "initializeGL called!";
initializeOpenGLFunctions();
globalOpenGLContext = this->context();
glClearColor(0.0, 0.0, 0.0, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-this->width() / 2, this->width() / 2, -this->height() / 2, this->height() / 2);
glViewport(0, 0, this->width(), this->height());
}
void MyOpenGLWidget::resizeGL(int w, int h)
{
qInfo() << "resizeGL called!";
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-w / 2, w / 2, -h / 2, h / 2);
glViewport(0, 0, w, h);
}
void MyOpenGLWidget::paintGL()
{
qInfo() << "paintGL called!";
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3f(1, 1, 1);
glPushMatrix();
glBegin(GL_POLYGON);
glVertex3f(-100, 100, 0);
glVertex3f(100, 100, 0);
glVertex3f(100, -100, 0);
glVertex3f(-100, -100, 0);
glEnd();
glPopMatrix();
update();
}
myopenglwidget.h
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
#include <QDebug>
#include <QOpenGLWidget>
#include <qopenglfunctions_3_3_core.h>
#include <gl/GLU.h>
class MyOpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core
{
Q_OBJECT
public:
MyOpenGLWidget(QWidget *parent = nullptr);
~MyOpenGLWidget();
protected:
void initializeGL();
void resizeGL(int w, int h);
void paintGL();
};
#endif // MYOPENGLWIDGET_H
#include "gl/GLU.h"
This is not portable and, in particular, doesn't compile on Linux. The proper include is <GL/glu.h>.
MyOpenGLWidget::MyOpenGLWidget(QWidget* parent)
{
qInfo() << "MyOpenGLWidget called";
}
You ignore the parent parameter, thus leaving your widget a window, rather than a child of its parent. But then you don't call QWidget::show() on it, so this separate window remains invisible, unlike the main window.
#include <qopenglfunctions_3_3_core.h>
It's not the public header. You should include <QOpenGLFunctions_3_3_Core> instead.
glBegin(GL_POLYGON);
glVertex3f(-100, 100, 0);
glVertex3f(100, 100, 0);
glVertex3f(100, -100, 0);
glVertex3f(-100, -100, 0);
glEnd();
Given that you include OpenGL 3.3 Core functions header, these long-obsolete functions aren't supposed to be used. They don't exist in OpenGL Core profile.
Moreover, just calling them as this, directly, ignores the QOpenGLOpenGLFunctions... class completely. You should get an instance of this class from OpenGL context and call its methods.
I'm trying to use OpenGL inside of Qt using QOpenGLWidget, but I am having a hard time finding any relevant examples. I am new to OpenGL, so I am trying to learn how to use it, but the tutorials that I find don't seem to apply particularly well in QOpenGLWidget. Right now, all I want to do is render a triangle to start with.
Here's what I have so far.
Header:
namespace Ui {
class Widget;
}
class Widget : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
explicit Widget(QWidget *parent = 0);
~Widget();
protected:
void initializeGL();
void resizeGL(int, int);
void paintGL();
private:
Ui::Widget *ui;
};
Class:
Widget::Widget(QWidget *parent) :
QOpenGLWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
void Widget::initializeGL()
{
// Set up the rendering context, load shaders and other resources, etc.:
initializeOpenGLFunctions();
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
}
void Widget::resizeGL(int w, int h)
{
// Update projection matrix and other size-related settings:
}
void Widget::paintGL()
{
// Draw the scene:
glClear(GL_COLOR_BUFFER_BIT);
}
Widget::~Widget()
{
delete ui;
}
Is there any example I could use to just render a basic triangle? I tried the one from here: https://www.khronos.org/assets/uploads/books/openglr_es_20_programming_guide_sample.pdf, but it threw a lot of errors that I couldn't work out.
I also don't know how OpenGL contexts work in QOpenGLWidget.
*EDIT: So it turns out that the examples were a separate package on my distro (Arch Linux). I was able to install them, and it looks like there is plenty there to get started.
Thanks for your help!
If you want to use QOpenGLWidget not QGLWidget, then this is the way to do it.
Open Qt Creator and choose Qt Widgets Application. Add Widget and push button as follows
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
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;
}
mainwindow.h
#ifndef MAINWINDOW_H
#define 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;
};
#endif // MAINWINDOW_H
Now add New Class and name it OGLWidget which should be inherited from QOpenGLWidget
oglwidget.h
#ifndef OGLWIDGET_H
#define OGLWIDGET_H
#include <QWidget>
#include <QOpenGLWidget>
#include <gl/GLU.h>
#include <gl/GL.h>
class OGLWidget : public QOpenGLWidget
{
public:
OGLWidget(QWidget *parent = 0);
~OGLWidget();
protected:
void initializeGL();
void resizeGL(int w, int h);
void paintGL();
};
#endif // OGLWIDGET_H
oglwidget.cpp
#include "oglwidget.h"
OGLWidget::OGLWidget(QWidget *parent)
: QOpenGLWidget(parent)
{
}
OGLWidget::~OGLWidget()
{
}
void OGLWidget::initializeGL()
{
glClearColor(0,0,0,1);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
}
void OGLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f(1.0, 0.0, 0.0);
glVertex3f(-0.5, -0.5, 0);
glColor3f(0.0, 1.0, 0.0);
glVertex3f( 0.5, -0.5, 0);
glColor3f(0.0, 0.0, 1.0);
glVertex3f( 0.0, 0.5, 0);
glEnd();
}
void OGLWidget::resizeGL(int w, int h)
{
glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, (float)w/h, 0.01, 100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0,0,5,0,0,0,0,1,0);
}
Now go back to the form and right click on the widget. Select promoted widgets and type in the promoted class name OGLWidget. Hit the add button and then promote. Now click on the background and go to its properties and change windowModality to ApplicationModel.
and this is the result you should get
the .pro file is
#-------------------------------------------------
#
# Project created by QtCreator 2015-07-20T15:15:29
#
#-------------------------------------------------
QT += core gui opengl
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = test2
TEMPLATE = app
SOURCES += main.cpp\
mainwindow.cpp \
oglwidget.cpp
HEADERS += mainwindow.h \
oglwidget.h
FORMS += mainwindow.ui
I want to control the paintGL method by a keypress-event. The aim is show an additional point by pushing return.
In other words: I have painted a nice background scene and now i want to push return (in a lineEdit) and there appears a red point in front of the already shown background.
//MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
glWidget = new GLWidget;
connect(ui->lineEdit, SIGNAL(returnPressed()), glWidget, SLOT (set_draw()));
}
//glwidget.h
#ifndef GLWIDGET_H
#define GLWIDGET_H
#include <QGLWidget>
#include <QMessageBox>
#include "mainwindow.h"
#include "cstdio"
class MainWindow;
class GLWidget : public QGLWidget
{
Q_OBJECT
MainWindow *myMainWindow;
public:
GLWidget(QWidget *parent = 0);
//~GLWidget;
int draw;
void initializeGL();
void paintGL();
void resizeGL(int w, int h);
public slots:
void set_draw();
};
#endif // GLWIDGET_H
//glwidget.cpp
GLWidget::GLWidget(QWidget *parent) : QGLWidget(parent)
{
draw = 0;
}
//-------------
void GLWidget::set_draw() //this SLOT is activated by pushing return
{
draw = 1;
updateGL(); //updating paintGL...
}
//-------------
void GLWidget::paintGL()
{
swapBuffers();
glClear(GL_COLOR_BUFFER_BIT);
/* drawing a lot of stuff*/
if( draw == 1 )
{
/*the following messagebox is shown at the screen*/
QMessageBox* Box = new QMessageBox();
Box->setText("Bert");
Box->show();
/*this big red point is NOT shown at the screen*/
glPointSize(30);
glBegin(GL_POINTS);
glColor3f(1.0, 0.0, 0.0);
glVertex3f(45,45,0);
glEnd();
}
}
Can someone explain, why this is not working? The red point does not appear... Is the value of int draw influenced by the paintGL method?
In OpenGL you always redraw the whole scene. Store the additional point in some array. When drawing you iterate over that array and draw the points according to the array's content.
I'm working on getting a simple animation loop going in a wxWidets openGl app. I am having a problem with the loop iterations not fully drawing the screen before displaying it on each iteration. It seems like .1s is more than enough to fully draw the simple scene.... Can anyone give me an idea of why it might not?
Thanks!
#include <wx/wx.h>
#include <wx/glcanvas.h>
#ifdef __WXMAC__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
#ifndef WIN32
#include <unistd.h> // FIXME: ¿This work/necessary in Windows?
//Not necessary, but if it was, it needs to be replaced by process.h AND io.h
#endif
//#include "GlBox.cpp"
#include <iostream>
using namespace std;
class wxGLCanvasSubClass: public wxGLCanvas {
public:
wxGLCanvasSubClass(wxFrame* parent);
void Paintit(wxPaintEvent& event);
void Render();
protected:
DECLARE_EVENT_TABLE()
//GlBox myBox;
};
wxGLCanvasSubClass *thing;
static void timerRender(int count)
{
thing->Render();
cout << "render timeout was called \n";
glutTimerFunc(25, timerRender, 0);
}
static void myIdleFunc()
{
cout << "idleing \n";
cout.flush();
}
static void setRender(wxGLCanvasSubClass *thing2)
{
thing = thing2;
cout << "global thing was set \n";
glutTimerFunc(1100, timerRender, 0);
}
BEGIN_EVENT_TABLE(wxGLCanvasSubClass, wxGLCanvas)
EVT_PAINT (wxGLCanvasSubClass::Paintit)
END_EVENT_TABLE()
wxGLCanvasSubClass::wxGLCanvasSubClass(wxFrame *parent)
:wxGLCanvas(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, wxT("GLCanvas")){
int argc = 1;
char* argv[1] = { wxString((wxTheApp->argv)[0]).char_str() };
/*
NOTE: this example uses GLUT in order to have a free teapot model
to display, to show 3D capabilities. GLUT, however, seems to cause problems
on some systems. If you meet problems, first try commenting out glutInit(),
then try comeenting out all glut code
*/
glutInit(&argc, argv);
glutIdleFunc(myIdleFunc);
}
void wxGLCanvasSubClass::Paintit(wxPaintEvent& WXUNUSED(event)){
Render();
}
void wxGLCanvasSubClass::Render()
{
SetCurrent();
wxPaintDC(this);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, (GLint)GetSize().x, (GLint)GetSize().y);
glBegin(GL_POLYGON);
glColor3f(1.0, 1.0, 1.0);
glVertex2f(-0.5, -0.5);
glVertex2f(-0.5, 0.5);
glVertex2f(0.5, 0.5);
glVertex2f(0.5, -0.5);
glColor3f(0.4, 0.5, 0.4);
glVertex2f(0.0, -0.8);
glEnd();
// using a little of glut
glColor4f(0,0,1,1);
glutWireTeapot(0.4);
glLoadIdentity();
glColor4f(2,0,1,1);
glutWireTeapot(0.6);
//myBox.getIt();
SwapBuffers();
glFlush();
//
}
class MyApp: public wxApp
{
virtual bool OnInit();
void onIdle(wxIdleEvent& evt);
time_t lastEvent;
bool slept ;
wxGLCanvas * MyGLCanvas;
wxGLCanvasSubClass * myCanvas;
};
IMPLEMENT_APP(MyApp)
bool MyApp::OnInit()
{
slept = false;
lastEvent = time( &lastEvent);
wxFrame *frame = new wxFrame((wxFrame *)NULL, -1, wxT("Hello GL World"), wxPoint(50,50), wxSize(200,200));
myCanvas = new wxGLCanvasSubClass(frame);
frame->Show(TRUE);
Connect( wxID_ANY, wxEVT_IDLE, wxIdleEventHandler(MyApp::onIdle) );
return TRUE;
}
void MyApp::onIdle(wxIdleEvent& evt)
{
if(slept)
{
slept = false;
cout << "event hit " << lastEvent;
myCanvas->Render();
evt.RequestMore();
} else {
cout << "idleing \n " << lastEvent;
usleep(100000);
slept = true;
evt.RequestMore();
}
}
GLUT is not a mandatory thing to use when it comes to OpenGL. GLUT is a third party framework, wxWidgets is a third party framework. And both implement a event loop.
You should not mix these. Just use wxWidgets only, which provides a very fine OpenGL widget.