I can't seem to get Qt5 to update my scene at a meaningful rate. My scene is a 512x512 textured rectangle. The rate I am getting is about 1 frame per second (!).
In my constructor
aTimer.setSingleShot(false);
aTimer->setTimerType(Qt::PreciseTimer);
connect(&aTimer,SIGNAL(timeout()),this,SLOT(animate()));
aTimer.start(50);
setAutoFillBackground(false);
and
void GLWidget::animate()
{
//Logic for every time step
updateGL();
}
Is there a way to set priority? Am I doing something totally, wrong? Is there some sort of intrinsic update limitation in Qt, and its certainly not on the order of 1 FPS? My theory is that Qt is ignoring my calls to actually update the screen.
I have tried
to insert a QCoreApplication::processEvents(); but this doesn't help
Call update on the parent widget, and run the timer from the parent
Create a function called animate() that ran forever{} and call update from within it
The wigglywidget example seems to work, which hints to me that QT OpenGL is somehow collapsing frames, ignoring my calls to update. Is there a heuristic that controls this?
Minimal Code to Recreate
(The version is a bit different, being modeled after the wigglywidget class, but has the exact same problem)
git clone https://bitbucket.org/FunFarm/qtcapturesoftware.git
glwidget.h
/****************************************************************************/
#ifndef GLWIDGET_H
#define GLWIDGET_H
#include <QGLWidget>
#include <QtOpenGL/qglshaderprogram.h>
#include <QTimer>
#include <math.h>
#include "time.h"
#include <assert.h>
#include <random>
class GLWidget : public QGLWidget
{
Q_OBJECT
public:
GLWidget(QWidget *parent = 0);
~GLWidget();
void addNoise();
protected:
void initializeGL();
void paintGL();
void timerEvent(QTimerEvent *event);
void resizeGL(int width, int height);
private:
QBasicTimer timer;
QPoint lastPos;
GLuint textures[6];
QVector<QVector2D> vertices;
QVector<QVector2D> texCoords;
QGLShaderProgram program1;
int vertexAttr1;
int vertexTexr1;
//
int heightGL;
int widthGL;
//
GLubyte* noise;
//
QTimer* aTimer;
//
};
#endif
glwidget.cpp
#include <QtWidgets>
#include <QtOpenGL>
#include "glwidget.h"
#ifndef GL_MULTISAMPLE
#define GL_MULTISAMPLE 0x809D
#endif
GLWidget::GLWidget(QWidget *parent)
: QGLWidget(QGLFormat(QGL::SampleBuffers), parent)
{
setAutoFillBackground(false);
aTimer = new QTimer();
timer.start(30, this); // 30 fps?
}
void GLWidget::timerEvent(QTimerEvent *event)
{
addNoise();
update();// Doesn't matter which update function I call, this is the one from the wigglywidget example
}
GLWidget::~GLWidget(){}
void GLWidget::initializeGL()
{
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glShadeModel(GL_SMOOTH);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_MULTISAMPLE);
static GLfloat lightPosition[4] = { 0.5, 5.0, 7.0, 1.0 };
glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
QGLShader *vshader1 = new QGLShader(QGLShader::Vertex, this);
const char *vsrc1 =
"attribute vec2 coord2d; \n"
"attribute mediump vec4 texCoord;\n"
"varying mediump vec4 texc;\n"
"void main() \n"
"{ \n"
" gl_Position = vec4(coord2d, 0.0, 1.0); \n"
" texc = texCoord;\n"
"} \n";
vshader1->compileSourceCode(vsrc1);
QGLShader *fshader1 = new QGLShader(QGLShader::Fragment, this);
const char *fsrc1 =
"uniform sampler2D texture;\n"
"varying mediump vec4 texc;\n"
"void main(void)\n"
"{\n"
" gl_FragColor = texture2D(texture, texc.st);\n"
"}\n" ;
fshader1->compileSourceCode(fsrc1);
program1.addShader(vshader1);
program1.addShader(fshader1);
program1.link();
vertexAttr1 = program1.attributeLocation( "coord2d");
vertexTexr1 = program1.attributeLocation( "texCoord");
// Create the vertex buffer.
vertices.clear();
float u=1;
#define AVEC -u,u
#define BVEC -u,-u
#define CVEC u,u
#define DVEC u,-u
vertices << QVector2D(AVEC);
vertices << QVector2D(BVEC);
vertices << QVector2D(CVEC);
vertices << QVector2D(BVEC);
vertices << QVector2D(DVEC);
vertices << QVector2D(CVEC);
// Create the texture vertex buffer
#define TAVEC 0,1
#define TBVEC 0,0
#define TCVEC 1,1
#define TDVEC 1,0
texCoords << QVector2D(TAVEC);
texCoords << QVector2D(TBVEC);
texCoords << QVector2D(TCVEC);
texCoords << QVector2D(TBVEC);
texCoords << QVector2D(TDVEC);
texCoords << QVector2D(TCVEC);
QPixmap aMap(":/images/testmap.jpg");
assert(aMap.width());
heightGL = aMap.height();
widthGL = aMap.width();
textures[0] =bindTexture(aMap,GL_TEXTURE_2D,GL_LUMINANCE);
noise = (GLubyte*) calloc(1*widthGL*heightGL,sizeof(GLubyte));//GL_RGB8
memset(noise,100,1*widthGL*heightGL);
//
}
void GLWidget::addNoise()
{
std::default_random_engine e((unsigned int)(time(0)));
GLubyte c = e()%256;
assert(noise);
for (int i = 0; i<heightGL;i++)
{
for(int j =0;j<widthGL;j++)
noise[i*widthGL+j]= c;
}
}
void GLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
//
//
program1.bind();
program1.setUniformValue("texture", 0);
program1.enableAttributeArray(vertexAttr1);
program1.enableAttributeArray(vertexTexr1);
program1.setAttributeArray(vertexAttr1, vertices.constData());
program1.setAttributeArray(vertexTexr1, texCoords.constData());
glBindTexture(GL_TEXTURE_2D, textures[0]);
glTexSubImage2D(GL_TEXTURE_2D,0,0,0,
widthGL,heightGL,GL_LUMINANCE,GL_UNSIGNED_BYTE, //lets hope these are correct
noise);
glDrawArrays(GL_TRIANGLES, 0, vertices.size());
program1.disableAttributeArray(vertexTexr1);
program1.disableAttributeArray(vertexAttr1);
program1.release();
}
void GLWidget::resizeGL(int width, int height)
{
int side = qMin(width, height);
glViewport((width - side) / 2, (height - side) / 2, side, side);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-0.5, +0.5, -0.5, +0.5, 4.0, 15.0);
glMatrixMode(GL_MODELVIEW);
}
main.cpp
/****************************************************************************/
#include <QApplication>
#include <QDesktopWidget>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//cameraView cV;
//cV.show();
GLWidget mW;
mW.show();
return a.exec();
}
test.pro
HEADERS = glwidget.h \
cameraview.h
SOURCES = glwidget.cpp \
main.cpp \
cameraview.cpp
QT += opengl widgets
CONFIG += console
CONFIG += c++11
# install
target.path = $$[QT_INSTALL_EXAMPLES]/qt_OpenGL_3x/02_First_Triangle
sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS 02-first-triangle.pro
sources.path = $$[QT_INSTALL_EXAMPLES]/qt_OpenGL_3x/02_First_Triangle
INSTALLS += target sources
simulator: warning(This example might not fully work on Simulator platform)
FORMS += \
cameraview.ui
RESOURCES += \
images.qrc
OTHER_FILES +=
I cloned the repository and made a few changes to the code to help you debug the problem. First, print a message in the beginning of paintGL() with std::cout:
void GLWidget::paintGL()
{
static int xyz = 0;
std::cout << "PaintGL begin " << xyz << std::endl;
and another message at the end of paintGL. After that, increment the counter variable:
program1.release();
std::cout << "PaintGL end " << xyz << std::endl;
xyz++;
}
This approach clearly shows that paintGL() is called nearly 30 times per second due to the amount of messages that are printed to the console in 2 seconds:
PaintGL begin 0
PaintGL end 0
PaintGL begin 1
PaintGL end 1
...
...
PaintGL begin 60
PaintGL end 60
PaintGL begin 61
PaintGL end 61
So the theory that there is a problem with the timer can be discarded. The timer is fine, and you are indeed painting the window 30 timer per second or so.
The problem is probably somewhere else in your code. Since you shared only a piece of the application in the repository I'm afraid I won't be able to help you any further.
EDIT:
I noticed that inside addNoise() you have a random number generator. If you are expecting the drawing to change at every paintGL() call based on the number generated by the other method you will be disappointed.
The current number generation mechanism that is implemented is not sensitive to millisecond changes, so all the calls to addNoise() within the same second will generate the same number, which in turn will paint the same thing on your window for the entire second. That explains why the drawing changes only once per second.
To fix this problem I suggest taking a look at:
Seed random from milliseconds in Windows and Linux
Random numbers every millisecond?
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 am trying to create a very simple qt opengl project on mac (drawing a dot in the center of the widget). Following are my code:
myOpenglWidget.h
//myopenglwidget.h
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
class QOpenGLShaderProgram;
class MyOpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
explicit MyOpenGLWidget(QWidget *parent = 0);
protected:
void initializeGL() override;
void paintGL() override;
void resizeGL(int width, int height) override;
private:
QOpenGLShaderProgram *program;
};
#endif // MYOPENGLWIDGET_H
myOpenglWidget.cpp
//myopenglwidget.cpp
#include "myopenglwidget.h"
#include <QOpenGLShaderProgram>
MyOpenGLWidget::MyOpenGLWidget(QWidget *parent)
: QOpenGLWidget(parent)
{
}
void MyOpenGLWidget::initializeGL()
{
initializeOpenGLFunctions();
// Create vertex shader
QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
const char *vsrc =
"void main() { \n"
" gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"
"} \n";
vshader->compileSourceCode(vsrc);
// Create fragment shader
QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
const char *fsrc =
"void main() { \n"
" gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
"} \n";
fshader->compileSourceCode(fsrc);
// create shader
program = new QOpenGLShaderProgram;
program->addShader(vshader);
program->addShader(fshader);
program->link();
program->bind();
}
void MyOpenGLWidget::resizeGL(int , int )
{
}
void MyOpenGLWidget::paintGL()
{
//draw
glDrawArrays(GL_POINTS, 0, 1);
}
main.cpp
//main.cpp
#include <QApplication>
#include "myopenglwidget.h"
int main(int argc, char *argv[])
{
QApplication app(argc,argv);
MyOpenGLWidget w;
w.resize(400, 300);
w.show();
return app.exec();
}
I tried it on windows and it works perfectly fine. However, when I wrtie the same code on Qt creator on mac, what it shows me is only a blank black window. I know Apple has started to stop support opengl, but have no idea whether it's related to my issue. I wonder is it possible to use shader in this way to make it work on mac?
My mac version is MAC OSX 10.15
I'm having trouble with a QtWidgets test program to visualize joints in old FVF mesh data, highlighting the user's chosen individual joints. I have reduced the program and included all parts below.
The program loads a mesh consisting of vertex and element array data, renders it as a white wireframe, but colours all vertices of a specified blend index red. Blend indices are present in the vertex data. They are collected and presented in a list widget. Clicking on an index forwards that index to the fragment shader, which overrides the default colour, white, with red, for vertices with matching blend index.
Here is an example of the program colouring vertices with a blend index of 0:
The coloured part makes sense as the root of the skeleton.
The problem is that this is only working when the chosen blend index is 0. Selecting any other index results in the wireframe being rendered all white. The expected behaviour is that other parts of the mesh would be coloured red. The vertex data is confirmed correct, as it works in its original application, and is certainly not the source of the error.
I'm unsure how to explore this problem. I have next to no experience in debugging shaders - assuming they are the source of the error. One idea I wondered about is data alignment. Here is the layout of the FVF data in an enum:
openglwidget.h:
#ifndef OPENGLWIDGET_H
#define OPENGLWIDGET_H
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLWidget>
#include <QVector3D>
enum VertexLayout {
XyzOffset = 0,
NormalOffset = 12,
TexcoordOffset = 24,
TangentOffset = 32,
BinormalOffset = 44,
BlendIndexOffset0 = 56,
BlendWeightOffset0 = 57,
BlendIndexOffset1 = 61,
BlendIndexWeight1 = 62,
BlendIndexOffset2 = 66,
BlendWeightOffset2 = 67,
Size = 71 // Size in bytes of one raw vertex
};
class OpenGLWidget : public QOpenGLWidget {
Q_OBJECT
public:
OpenGLWidget(QWidget* parent=nullptr);
~OpenGLWidget() override;
void initGeometry(const QByteArray& rawVertices, const QByteArray& rawIndices);
void setIndex(int index);
void initializeGL() override;
void paintGL() override;
void resizeGL(int w, int y) override;
signals:
void readyForData();
private:
void initShaders();
float aspect = 1.33f;
qint32 numIndices = 0;
int blendIndex = 0;
QOpenGLShaderProgram program;
QOpenGLVertexArrayObject vao;
QOpenGLBuffer arrayBuf;
QOpenGLBuffer indexBuf;
QVector3D eye, target, up;
};
#endif // OPENGLWIDGET_H
While all other attributes are floats, the blend indices are bytes. I specify them in initGeometry below with GL_UNSIGNED_BYTE:
openglwidget.cpp:
#include "openglwidget.h"
#include <QOpenGLBuffer>
#include <QOpenGLVersionProfile>
#include <QOpenGLWidget>
OpenGLWidget::OpenGLWidget(QWidget* parent)
:QOpenGLWidget(parent),
arrayBuf(QOpenGLBuffer::VertexBuffer),
indexBuf(QOpenGLBuffer::IndexBuffer),
up(0.0f, 0.0f, 1.0f)
{
}
OpenGLWidget::~OpenGLWidget()
{
}
void OpenGLWidget::initGeometry(const QByteArray& rawVertices, const QByteArray& rawIndices)
{
vao.create();
vao.bind();
arrayBuf.create();
indexBuf.create();
arrayBuf.bind();
arrayBuf.allocate(rawVertices, rawVertices.size());
indexBuf.bind();
indexBuf.allocate(rawIndices, rawIndices.size());
numIndices = rawIndices.size() / 2; // sizeof(qint16)
int vertexLocation = program.attributeLocation("a_position");
program.enableAttributeArray(vertexLocation);
program.setAttributeBuffer(vertexLocation,
GL_FLOAT,
VertexLayout::XyzOffset,
3,
VertexLayout::Size);
int blend0Location = program.attributeLocation("a_blend0");
program.enableAttributeArray(blend0Location);
program.setAttributeBuffer(blend0Location,
GL_UNSIGNED_BYTE,
VertexLayout::BlendIndexOffset0,
1,
VertexLayout::Size);
vao.release();
update();
}
void OpenGLWidget::setIndex(int index)
{
if (index != blendIndex) {
blendIndex = index;
update();
}
}
void OpenGLWidget::initializeGL()
{
QSurfaceFormat format;
QOpenGLVersionProfile profile(format);
auto functions = context()->versionFunctions(profile);
QOpenGLWidget::initializeGL();
functions->initializeOpenGLFunctions();
glClearColor(0.2f, 0.2f, 0.3f, 1.0f);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
initShaders();
emit readyForData();
}
void OpenGLWidget::paintGL()
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // simple wireframe
glClearColor(0.2f, 0.2f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Hard-coded pose
QMatrix4x4 mv;
eye = QVector3D(-1.50f, 2.33f, 1.43f);
target = QVector3D(0.50f, 0.28f, 0.94f);
mv.lookAt(eye, target, up);
QMatrix4x4 mp;
mp.perspective(75.0f, aspect, 1.0f, 200.0f);
QMatrix4x4 mm;
mm.translate(0.0f, 0.0f, 0.0f);
program.setUniformValue("mvp_matrix", mp * mv * mm);
program.setUniformValue("boneToColor", blendIndex);
vao.bind();
glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, nullptr);
}
void OpenGLWidget::resizeGL(int w, int h)
{
aspect = h == 0 ? 1 : w / h;
}
static const char* vertexShaderSource =
"#version 330 core\n"
"in vec4 a_position;\n"
"in int a_blend0;\n"
"flat out int v_blend0;\n"
"uniform mat4 mvp_matrix;\n"
"void main() {\n"
"gl_Position = mvp_matrix * a_position;\n"
"v_blend0 = a_blend0;\n"
"\n}";
static const char* fragmentShaderSource =
"#version 330 core\n"
"flat in int v_blend0;\n"
"out vec4 fragmentColor;\n"
"uniform int boneToColor;\n"
"void main() {\n"
"if (v_blend0 == boneToColor)\n"
"fragmentColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
"else\n"
"fragmentColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
"\n}";
void OpenGLWidget::initShaders()
{
if (!program.addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource)) {
qCritical() << "Failed to load vertex shader";
close();
}
if (!program.addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource)) {
qCritical() << "Failed to load fragment shader";
close();
}
if (!program.link()) {
qCritical() << "Failed to link program";
close();
}
if (!program.bind()) {
qCritical() << "Failed to bind program";
close();
}
}
I understand that GLSL will convert this type to int. Perhaps this conversion is the cause of trouble, but if so, I'm unsure how to test it. Perhaps there is some other error in the shaders, but then, the case of blendIndex == 0 works.
It is worth noting that this mesh associates three blend indices and three blend weights with each vertex. The program only pays attention to the first blend index. I'm too new to this topic to understand yet whether this could cause the problem.
Although this problem is likely due to a lack in my understanding of OpenGL or the underlying data, I have also tagged the question with Qt in case some wrinkle in their OpenGL interface is involved.
Here are the remaining files needed to run the project:
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class QListWidget;
class QListWidgetItem;
class OpenGLWidget;
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget* parent=nullptr);
~MainWindow();
private:
void loadGeometry();
void onIndexSelected(QListWidgetItem* item);
QListWidget* selector;
OpenGLWidget* openGLWidget;
};
#endif // MAINWINDOW_H
mainwindow.cpp:
#include "mainwindow.h"
#include <set>
#include <QByteArray>
#include <QDebug>
#include <QFile>
#include <QHBoxLayout>
#include <QListWidget>
#include "openglwidget.h"
MainWindow::MainWindow(QWidget* parent)
:QMainWindow(parent),
selector(new QListWidget(this)),
openGLWidget(new OpenGLWidget(this))
{
selector->setFixedWidth(80);
auto dummy = new QWidget(this);
auto hbox = new QHBoxLayout(dummy);
hbox->addWidget(selector);
hbox->addWidget(openGLWidget);
setCentralWidget(dummy);
setMinimumSize(640, 480);
connect(openGLWidget, &OpenGLWidget::readyForData,
this, &MainWindow::loadGeometry);
connect(selector, &QListWidget::itemClicked,
this, &MainWindow::onIndexSelected);
}
MainWindow::~MainWindow()
{
}
void MainWindow::onIndexSelected(QListWidgetItem* item)
{
int index = item->text().toInt();
openGLWidget->setIndex(index);
}
void MainWindow::loadGeometry()
{
// Read raw geometry from file:
QFile inf("boneShaderTestGeometry.dat");
if (!inf.open(QIODevice::ReadOnly)) {
qCritical() << "Failed to open geometry file";
return;
}
QDataStream ins(&inf);
ins.setByteOrder(QDataStream::LittleEndian);
qint32 numVerts;
ins >> numVerts;
QByteArray rawVertices(numVerts * VertexLayout::Size, 0);
ins.readRawData(rawVertices.data(), rawVertices.size());
qint32 numIndices;
ins >> numIndices;
QByteArray rawIndices(numIndices * 2 /* sizeof(qint16) */, 0);
ins.readRawData(rawIndices.data(), rawIndices.size());
// Parse raw vertices for blend indices:
std::set<char> blendIndices;
for (int i=0; i<numVerts; ++i) {
auto offset = VertexLayout::Size * i + VertexLayout::BlendIndexOffset0;
blendIndices.insert(rawVertices[offset]);
}
// Populate selector:
for (auto blendIndex: blendIndices)
selector->addItem(QString::number(blendIndex));
selector->setCurrentRow(0);
// Forward raw geometry:
openGLWidget->initGeometry(rawVertices, rawIndices);
}
main.cpp:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
BoneShaderTest.pro:
QT += core gui opengl
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = BoneShaderTest
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
CONFIG += c++11
SOURCES += \
main.cpp \
mainwindow.cpp \
openglwidget.cpp
HEADERS += \
mainwindow.h \
openglwidget.h
LIBS += opengl32.lib
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
The raw mesh data at tinyupload.com
I've had a look through, and not a lot seems wrong. The only thing that might be an issue is the call to:
program.setAttributeBuffer(blend0Location,
GL_UNSIGNED_BYTE,
VertexLayout::BlendIndexOffset0,
1,
VertexLayout::Size);
If you are specifying an unsigned integer type for the blend index, then make sure you are doing so with: glVertexArrayAttribIFormat (and not glVertexArrayAttribFormat). But then again, if it's working for the case when the index is zero, it kinda indicates something is going wrong when setting the uniform index value.
When tracking down stuff like this, usually it's a good idea to hook into the OpenGL debug API to get it to output the current errors. At worst, you can always fall back to the old glGetError approach, and wrap all calls into GL with a simple check macro to see if any errors are occurring.
inline void checkGlError(const char* const file, const char* const op, int line)
{
switch(glGetError())
{
case GL_NO_ERROR: return;
case GL_INVALID_ENUM: fprintf(stderr, "file: %s\n\t%s: line %d: GL_INVALID_ENUM\n", file, op, line); break;
case GL_INVALID_VALUE: fprintf(stderr, "file: %s\n\t%s: line %d: GL_INVALID_VALUE\n", file, op, line); break;
case GL_INVALID_OPERATION: fprintf(stderr, "file: %s\n\t%s: line %d: GL_INVALID_OPERATION\n", file, op, line); break;
case GL_INVALID_FRAMEBUFFER_OPERATION: fprintf(stderr, "file: %s\n\t%s: line %d: GL_INVALID_FRAMEBUFFER_OPERATION\n", file, op, line); break;
case GL_OUT_OF_MEMORY: fprintf(stderr, "file: %s\n\t%s: line %d: GL_OUT_OF_MEMORY\n", file, op, line); break;
default: fprintf(stderr, "file: %s\n\t%s: line %d: unknown Gl error\n", file, op, line); break;
}
}
#define CHECK_GL_ERROR(X) X; checkGlError(__FILE__, #X, __LINE__);
The hints given by robthebloke led to the solution. Two changes were necessary.
First, it was necessary to use glVertexAttribIPointer to handle integer type correctly:
program.setAttributeBuffer(blend0Location,
GL_UNSIGNED_BYTE,
VertexLayout::BlendIndexOffset0,
1,
VertexLayout::Size);
was replaced with:
#include <QOpenGLFunctions_4_0_Core>
auto fun = new QOpenGLFunctions_4_0_Core();
fun->initializeOpenGLFunctions();
fun->glVertexAttribIPointer(blend0Location, 1, GL_UNSIGNED_BYTE,
VertexLayout::Size, reinterpret_cast<const void*>(qintptr(VertexLayout::BlendIndexOffset0)));
Secondly, it was necessary to update the shaders. The vertex shader:
#version 330 core
in vec4 a_position;
in int a_blend0;
flat out int v_blend0;
uniform mat4 mvp_matrix;
void main() {
gl_Position = mvp_matrix * a_position;
v_blend0 = a_blend0;
}
and the fragment shader:
#version 330 core
flat in int v_blend0;
out vec4 fragmentColor;
uniform int boneToColor;
void main() {
if (v_blend0 == boneToColor)
fragmentColor = vec4(1.0, 0.0, 0.0, 1.0);
else
fragmentColor = vec4(1.0, 1.0, 1.0, 1.0);
}
I'm using Qt (5.6.0) to make a dungeon crawler/rogue-like with the interface done in Qt and the gameplay scene (3D map, character, monsters..) in OpenGL. The view is a promoted QWidget and I am able to draw using the old glBegin/glEnd method but whenever I try using glDrawArrays or glDrawElements, I get a blank screen.
The clear color is working and is set to be slightly lighter than black, so a black shape should show up. I am using the glBegin/glEnd method for testing with the same vertices and it does render as it should. I have tried a more or less straight OpenGL approach as shown by Jamie King, through several more examples and tutorials, finally ending on this example for using the QOpenGLShaderProgram and QOpenGLVertexArrayObject objects along with the example in the QOpenGLShaderProgram Qt doc. Currently the shader code in the initializeGL function is preventing the glBegin/glEnd triangle from being drawn.
Current Code:
oglwidget.cpp:
#include "GL/glew.h"
#include "GL/glext.h"
#include "oglwidget.h"
#include "general.h"
#define BUFFER_OFFSET(bytes) ((GLubyte*) NULL + (bytes))
extern const char * vertexShader;
extern const char * fragmentShader;
OGLWidget::OGLWidget(QWidget *parent): QOpenGLWidget(parent){
}
OGLWidget::~OGLWidget(){
}
void OGLWidget::initializeGL(){
QOpenGLFunctions gl;
gl.initializeOpenGLFunctions();
cout<<"Init"<<endl;
//-----Create Shader
shader2.create();
shader2.addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShader);
shader2.addShaderFromSourceCode(QOpenGLShader::Fragment,fragmentShader);
shader2.link();
shader2.bind();
int vertexLocation = shader2.attributeLocation("vertex");
int matrixLocation = shader2.uniformLocation("matrix");
int colorLocation = shader2.uniformLocation("color");
QMatrix4x4 pmvMatrix;
pmvMatrix.ortho(rect());
QColor color(0, 255, 0, 255);
shader2.enableAttributeArray(vertexLocation);
shader2.setAttributeArray(vertexLocation, verts, 3);
shader2.setUniformValue(matrixLocation, pmvMatrix);
shader2.setUniformValue(colorLocation, color);
//-----Create VAO2
VAO2=new QOpenGLVertexArrayObject(this);
VAO2->create();
VAO2->bind();
//-----Create VBO2
VBO2.create();
VBO2.setUsagePattern(QOpenGLBuffer::StaticDraw);
VBO2.bind();
VBO2.allocate(verts,sizeof(verts));
}
void OGLWidget::paintGL(){
cout<<"Painting"<<endl;
QOpenGLFunctions gl;
gl.initializeOpenGLFunctions();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.05,0.05,0.05,1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0f,0.0f,-2.0f);
//draw();
shader2.bind();
VAO2->bind();
glDrawElements(GL_TRIANGLES,3,GL_UNSIGNED_INT,inds);
VAO2->release();
}
void OGLWidget::resizeGL(int w, int h){
cout<<"Resizing"<<endl;
glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//glFrustum (-1.0, 1.0, -1.0, 1.0, 1, 1000.0); //-----Assuming this is right?
glOrtho(-5,5,-5,5,-5,5);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
int OGLWidget::loadModel(string path){
//-----loads the model path if not already loaded, returns the index of the model
//---check if already loaded
for(int p=0;p<loadedPaths.size();p++){
if(!loadedPaths[p].compare(path)){
return p;
}
}
loadedPaths.push_back(path);
//-----continue with path loading
Model m;
m.loadModel(path);
return loadedPaths.size()-1;
}
void OGLWidget::draw(){
cout<<"drawing..."<<endl;
glBegin(GL_TRIANGLES);
for(int i=0;i<sizeof(verts)/sizeof(GLfloat);i+=3){
//cout<<i<<endl;
glVertex3f(verts[i],verts[i+1],verts[i+2]);
}
glEnd();
}
oglwidget.h:
#ifndef OGLWIDGET_H
#define OGLWIDGET_H
#include <QWidget>
#include <QOpenGLWidget>
#include <glm/glm.hpp>
#include <QOpenGLFunctions>
#include <vector>
#include "entity.h"
#include "general.h"
#include <qopenglfunctions_3_3_core.h>
//#include <GL/glu.h>
//#include <GL/gl.h>
#define VERTEXATTR 0
#define INDEXATTR 1
#define POSITIONATTR 2
#define ROTATIONATTR 3
using namespace std;
class OGLWidget : public QOpenGLWidget
{
//QOpenGLFunctions gl;
//QOpenGLFunctions_3_3_Core core;
QOpenGLVertexArrayObject *VAO2;
QOpenGLBuffer VBO2;
QOpenGLShaderProgram shader2;
QOpenGLContext *m_context;
vector<GLuint>statics; //-----buffer number for static models
vector<GLuint>dynamics; //-----buffer number of dynamic models
vector<GLfloat[1]>staticVerts; //-----vertex data for static models
vector<GLfloat[1]>dynamicVerts; //-----vertex data for dynamic models
vector<vector<GLfloat>*>staticPos; //-----position data for static models
vector<vector<GLfloat>*>dynamicPos; //-----position data for dynamic models
vector<GLfloat>staticRot; //-----rotation data for static models
vector<GLfloat>dynamicRot; //-----rotation data for dynamic models
vector<string>loadedPaths; //-----name in folder of matching VBO
GLuint VBO;
GLuint IND;
GLuint FragID;
GLuint VertID;
GLuint shader;
//-----Testing
GLfloat verts[9]={-1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
1.0f, 0.0f, 0.0f};
GLuint inds[3]={0,1,2};
public:
OGLWidget(QWidget *parent = 0);
~OGLWidget();
int loadModel(string path);
private:
void draw();
QGLShaderProgram m_shader;
QGLBuffer m_vertexBuffer;
protected:
void initializeGL();
void resizeGL(int w, int h);
void paintGL();
};
#endif // OGLWIDGET_H
shader.cpp:
const char * vertexShader=
"attribute highp vec4 vertex;\n"
"uniform highp mat4 matrix;\n"
"void main(void)\n"
"{\n"
" gl_Position = matrix * vertex;\n"
"}";
const char * fragmentShader=
"uniform mediump vec4 color;\n"
"void main(void)\n"
"{\n"
" gl_FragColor = color;\n"
"}";
From what I've read (correct me if I'm wrong) the objective it to load the vertex, color, texture and other data to the GPU memory using a VAO, rebind the VAO before drawing, draw using glDrawArrays or glDrawElements and release the VAO. Using the indexed version of the function will allow for changes in position, scale and rotation, meaning faster rendering for larger quantities of the same object (ie. the game tiles) and GL_STATIC_DRAW should be used for objects that are not updated frequently, with GL_DYNAMIC_DRAW for everything else.
I'm wanting to know what I'm missing or doing wrong with what really should be a simple exercise. I've redone this at least 5 times over and am at a complete loss.
OS: Debian Testing
GPU: GeForce 610m
OpenGL Core: 3.3
Qt: 5.6
Software: Qt Creator
Solution thanks to #PeterT in the comments: move the shader enableAttributeArray and setAttributeBuffer (changed from setAttributeArray)to after the VBO allocation and modify the vertex coordinates and ortho to be 'larger'.
void OGLWidget::initializeGL()
{
QOpenGLFunctions gl;
gl.initializeOpenGLFunctions();
glDisable(GL_CULL_FACE);
cout<<"Init"<<endl;
shader2.create();
shader2.addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShader);
shader2.addShaderFromSourceCode(QOpenGLShader::Fragment,fragmentShader);
shader2.link();
shader2.bind();
int vertexLocation = shader2.attributeLocation("vertex");
int matrixLocation = shader2.uniformLocation("matrix");
int colorLocation = shader2.uniformLocation("color");
QMatrix4x4 pmvMatrix;
//pmvMatrix.ortho(rect());
pmvMatrix.ortho(-100,100,-100,100,-1000,1000);
QColor color(0, 255, 0, 255);
shader2.enableAttributeArray(vertexLocation);
shader2.setUniformValue(matrixLocation, pmvMatrix);
shader2.setUniformValue(colorLocation, color);
//-----Create VAO2
VAO2=new QOpenGLVertexArrayObject(this);
VAO2->create();
VAO2->bind();
VBO2.create();
VBO2.setUsagePattern(QOpenGLBuffer::StaticDraw);
VBO2.bind();
VBO2.allocate(verts,sizeof(verts));
shader2.enableAttributeArray("vertex");
shader2.setAttributeBuffer("vertex",GL_FLOAT,0,3,0);
}
*
GLfloat verts[9]={-100.0f, 0.0f, 30.0f,
0.0f, 100.0f, 50.0f,
100.0f, 0.0f, 30.0f};
I'm trying to write a simple OpenGL application in Qt 5.2 using a QWindow. I can't seem to enable depth testing. I've simplified the QWindow OpenGL example heavily: I'm drawing a triangle with coloured vertices, followed by a triangle with white vertices. The white triangle has larger Z coordinates, so should appear behind the coloured triangle. It doesn't.
I'm explicitly setting the QWindow's surface format's depth buffer size to 24, but when I check with the QWindow::format() function, the depth buffer size is 0. With QGLFormat, I know there's a setDepth() function that you can use to turn on the depth buffer, but there's no similar function in QSurfaceFormat.
What am I doing wrong?
My code...
testWindow.cpp:
#include <QApplication>
#include <QDialog>
#include <QLabel>
#include <QResizeEvent>
#include <QSurfaceFormat>
#include <QWidget>
#include <QWindow>
#include <QVBoxLayout>
#include "SphereWindow.h"
class GLDialog : public QDialog
{
public:
GLDialog(QWidget *parent = 0, Qt::WindowFlags f = 0) : QDialog(parent, f)
{
QVBoxLayout *layout = new QVBoxLayout(this);
QSurfaceFormat format;
format.setSamples(16);
format.setDepthBufferSize(24);
qDebug() << "requested format:" << format;
window = new SphereWindow;
window->setFormat(format);
qDebug() << "actual format:" << window->format();
window->render();
QWidget *glWidget = QWidget::createWindowContainer(window, this);
layout->addWidget(glWidget);
}
~GLDialog()
{
delete window;
}
protected:
void resizeEvent(QResizeEvent *event)
{
window->resize(event->size());
window->render();
}
private:
SphereWindow *window;
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QDialog *dlg = new GLDialog;
dlg->resize(640,480);
dlg->show();
return app.exec();
}
SphereWindow.h:
#include <QColor>
#include <QEvent>
#include <QExposeEvent>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLPaintDevice>
#include <QOpenGLShaderProgram>
#include <QPainter>
#include <QResizeEvent>
#include <QSize>
#include <QWindow>
class SphereWindow : public QWindow, protected QOpenGLFunctions
{
Q_OBJECT
public:
SphereWindow(QWindow * = 0);
virtual ~SphereWindow();
virtual void render();
virtual void initialize();
public slots:
void resizeViewport(const QSize &);
protected:
virtual void resizeEvent(QResizeEvent *);
private:
bool _initialized;
QOpenGLContext *_context;
QOpenGLPaintDevice *_device;
QOpenGLShaderProgram *_program;
QColor _backgroundColour;
GLuint _posAttr;
GLuint _colAttr;
};
SphereWindow.cpp:
#include <QCoreApplication>
#include <QMatrix4x4>
#include <QOpenGLShader>
#include <QScreen>
#include <QSurfaceFormat>
#include <QDebug>
#include "SphereWindow.h"
SphereWindow::SphereWindow(QWindow *parent)
: QWindow(parent),
_initialized(0),
_program(0),
_backgroundColour(Qt::black)
{
setSurfaceType(QWindow::OpenGLSurface);
create();
_context = new QOpenGLContext(this);
_context->setFormat(requestedFormat());
_context->create();
}
SphereWindow::~SphereWindow()
{ }
void SphereWindow::resizeEvent(QResizeEvent *event)
{
resizeViewport(event->size());
}
void SphereWindow::resizeViewport(const QSize &size)
{
int width = size.width();
int height = size.height();
int side = qMin(width, height);
int hoffset = (int)((width - side) / 2.0 + 0.5);
int voffset = (int)((height - side) / 2.0 + 0.5);
glViewport(hoffset, voffset, side, side);
}
void SphereWindow::render()
{
if (! _initialized)
initialize();
if (! isExposed()) return;
glEnable(GL_DEPTH_TEST);
_context->makeCurrent(this);
glClearColor(_backgroundColour.redF(), _backgroundColour.greenF(), _backgroundColour.blueF(), 1.0);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
_program->bind();
GLfloat vertices[] = {
-0.75f, 0.75f, 0.0f,
-0.75f, -0.75f, 0.0f,
0.75f, -0.75f, 0.0f,
0.75f, 0.75f, 0.5f,
0.75f, -0.75f, 0.5f,
-0.75f, -0.75f, 0.5f,
};
GLfloat colors[] = {
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
};
glVertexAttribPointer(_posAttr, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glVertexAttribPointer(_colAttr, 3, GL_FLOAT, GL_FALSE, 0, colors);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(0);
_program->release();
_context->swapBuffers(this);
_context->doneCurrent();
}
static const char *vertexShaderSource =
"attribute highp vec4 posAttr;\n"
"attribute lowp vec4 colAttr;\n"
"varying lowp vec4 col;\n"
"void main() {\n"
" col = colAttr;\n"
" gl_Position = posAttr;\n"
"}\n";
static const char *fragmentShaderSource =
"varying lowp vec4 col;\n"
"void main() {\n"
" gl_FragColor = col;\n"
"}\n";
void SphereWindow::initialize()
{
_context->makeCurrent(this);
_program = new QOpenGLShaderProgram(this);
_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
_program->link();
_program->bind();
_posAttr = _program->attributeLocation("posAttr");
_colAttr = _program->attributeLocation("colAttr");
_program->release();
initializeOpenGLFunctions();
}
testqwindow.pro:
######################################################################
# Automatically generated by qmake (3.0) Sat May 3 05:01:55 2014
######################################################################
TEMPLATE = app
TARGET = testqwindow
INCLUDEPATH += .
QT += widgets
CONFIG += debug
# Input
HEADERS += SphereWindow.h
SOURCES += SphereWindow.cpp testWindow.cpp
The problem was this: I was setting the surface format after calling my SphereWindow constructor, but creating a QOpenGLContext and setting its format inside the constructor. The result was that the context wasn't getting a depth buffer. I've moved the calls to QOpenGLContext::setFormat() and QOpenGLContext::create() to my SphereWindow::initialize() function, and depth buffering is working for me now.
SphereWindow::SphereWindow(QWindow *parent)
: QWindow(parent),
_initialized(0),
_program(0),
_backgroundColour(Qt::black)
{
setSurfaceType(QWindow::OpenGLSurface);
create();
qDebug() << "format:" << format();
_context = new QOpenGLContext(this);
/* Move these lines into initialize() */
// _context->setFormat(requestedFormat());
// _context->create();
}
...
void SphereWindow::initialize()
{
_context->setFormat(requestedFormat());
_context->create();
_context->makeCurrent(this);
_program = new QOpenGLShaderProgram(this);
_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
_program->link();
_program->bind();
_posAttr = _program->attributeLocation("posAttr");
_colAttr = _program->attributeLocation("colAttr");
_program->release();
initializeOpenGLFunctions();
}
As I mentioned in a comment above, I had pretty much identical code in a QGLWidget-based setup, and depth buffering worked "out of the box". I guess QGLWidget (or QGLFormat?) must have a depth buffer turned on by default, and QWindow (or QSurfaceFormat?) doesn't.