crash from context shared QOpenGLWidget - c++

I have two QOpenGLWidgets(view1, view2)as children in a top-level widget.
Qt document says 'When multiple QOpenGLWidgets are added as children to the same top-level widget, their contexts will share with each other'.
So, view1 and view2 share OpenGL context.
I have tried to render same scene which is initialized in view1's context and the application crashes in view2's paintGL().
What did I wrong?
Here's simplified code:
#include <QtGui/QtGui>
#include <QtWidgets/QOpenGLWidget>
#include <QtWidgets/QApplication>
#include <QtWidgets/QHBoxLayout>
static const char *vertexShaderSource =
"attribute vec4 posAttr;\n"
"void main() {\n"
" gl_Position = posAttr;\n"
"}\n";
static const char *fragmentShaderSource =
"void main() {\n"
" gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
"}\n";
QOpenGLShaderProgram *program = nullptr;
QOpenGLBuffer arrayBuf;
int posAttr = -1;
class View3D : public QOpenGLWidget {
public:
View3D()
{
setMinimumSize(300, 200);
}
private:
auto initializeGL() -> void override
{
if (program)
return;
program = new QOpenGLShaderProgram(this);
program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
program->link();
posAttr = program->attributeLocation("posAttr");
program->bind();
GLfloat vertices[] = {
0.0f, 0.707f, 0.f,
-0.5f, -0.5f, 0.f,
0.5f, -0.5f, 0.f,
};
arrayBuf.create();
arrayBuf.bind();
arrayBuf.allocate(vertices, sizeof(vertices));
program->enableAttributeArray(posAttr);
program->setAttributeBuffer(posAttr, GL_FLOAT, 0, 3, 0);
program->release();
}
auto paintGL() -> void override
{
auto f = context()->functions();
const auto dpr = devicePixelRatio();
f->glViewport(0, 0, width() * dpr, height() * dpr);
f->glClear(GL_COLOR_BUFFER_BIT);
program->bind();
arrayBuf.bind();
program->enableAttributeArray(posAttr);
f->glDrawArrays(GL_TRIANGLES, 0, 3);
program->disableAttributeArray(posAttr);
arrayBuf.release();
program->release();
}
};
auto main(int argc, char **argv) -> int
{
QApplication app{argc, argv};
QWidget w;
auto hbox = new QHBoxLayout{&w};
hbox->addWidget(new View3D); // view1
hbox->addWidget(new View3D); // view2
w.show();
return app.exec();
}

It seems that vertex state shall not be shared between contexts even though those contexts are shared.
Here's fixed version:
#include <QtGui/QtGui>
#include <QtWidgets/QOpenGLWidget>
#include <QtWidgets/QApplication>
#include <QtWidgets/QHBoxLayout>
static const char *vertexShaderSource =
"attribute vec4 posAttr;\n"
"void main() {\n"
" gl_Position = posAttr;\n"
"}\n";
static const char *fragmentShaderSource =
"void main() {\n"
" gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
"}\n";
QOpenGLBuffer *arrayBuf = nullptr;
QOpenGLShaderProgram *program = nullptr;
class View3D : public QOpenGLWidget {
public:
View3D()
{
setMinimumSize(300, 200);
}
private:
int posAttr = -1;
auto initializeGL() -> void override
{
if (!arrayBuf) {
arrayBuf = new QOpenGLBuffer;
arrayBuf->create();
arrayBuf->bind();
GLfloat vertices[] = {
0.0f, 0.707f, 0.f,
-0.5f, -0.5f, 0.f,
0.5f, -0.5f, 0.f,
};
arrayBuf->allocate(vertices, sizeof(vertices));
}
if (!program) {
program = new QOpenGLShaderProgram(this);
program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
program->link();
}
posAttr = program->attributeLocation("posAttr");
program->bind();
arrayBuf->bind();
program->enableAttributeArray(posAttr);
program->setAttributeBuffer(posAttr, GL_FLOAT, 0, 3, 0);
program->release();
}
auto paintGL() -> void override
{
auto f = context()->functions();
const auto dpr = devicePixelRatio();
f->glViewport(0, 0, width() * dpr, height() * dpr);
f->glClear(GL_COLOR_BUFFER_BIT);
program->bind();
arrayBuf->bind();
program->enableAttributeArray(posAttr);
f->glDrawArrays(GL_TRIANGLES, 0, 3);
program->disableAttributeArray(posAttr);
arrayBuf->release();
program->release();
}
};
auto main(int argc, char **argv) -> int
{
QApplication app{argc, argv};
QWidget w;
auto hbox = new QHBoxLayout{&w};
hbox->addWidget(new View3D); // view1
hbox->addWidget(new View3D); // view2
w.show();
return app.exec();
}
From observation, sharing vertex attribute state also can cause problem.
I knew that I cannot share VAO by OpenGL specification, but I never used VAO explictily here.
I am not sure that this is inhibited by specification or a bug of driver.
However, at least on NVidia driver, it is obvious that you should not share vertex attribute state for any cases including shared contexts.

Related

Using OpenGL with QtQuickPainted item

I am trying to figure out how to use OpenGL with QtQuickPaintedItem. According to the documentation, this seems to be possible, and there are several "helper" classes for that.
I tried this code below, and I also tried to create QOpenGLPaintDevice, QOpenGLContext, and QOpenGLFramebufferObject and pass their instances to their functions in many combinations, but it did not work either.
/// Simple3d.h
#ifndef SIMPLE3D_H
#define SIMPLE3D_H
#include<QtQuick/QQuickPaintedItem>
#include<QOpenGLFunctions>
// Not sure if I need to use any of these classes.
//#include<QOpenGLPaintDevice>
//#include<QOpenGLContext>
//#include<QOpenGLFramebufferObject>
//#include<QOpenGLShaderProgram>
//#include <GL/gl.h>
/** #brief inherets from QtQuickItem and it draws
* triangle.
*
*/
class Simple3d : public QQuickPaintedItem
{
Q_OBJECT
public:
/** Call this function to be able to instantiate this
* item in a QML file.
*/
static void register_qml_item();
Simple3d();
void paint(QPainter * painter) override;
virtual ~Simple3d() override;
private:
bool mDebug;
/** OpenGL stuff */
bool mOpenGlInitialized;
GLuint mProgramId;
static const GLuint mVertexArrayID = 0;
QOpenGLFunctions *mGlFuncs;
// Not sure when is a good time to update these:
GLsizei mWidth, mHeight;
GLuint load_shader(GLenum type, const char *shaderSrc);
void verify_initialized();
void draw_open_gl();
};
#endif
/// Simple3d.cpp
#include "simple3d.h"
#include <QSGGeometryNode>
#include <QSGVertexColorMaterial>
#include <QtMath>
#include<QOpenGLFunctions>
#include<QPainter>
#include<QGuiApplication>
using namespace std;
Simple3d::Simple3d()
{
mDebug = true;
mOpenGlInitialized = false;
mGlFuncs = nullptr;
setFlag(ItemHasContents, true);
}
void Simple3d::paint(QPainter *painter)
{
if (mDebug) qDebug("Simple3d is running paint()");
if (!mGlFuncs){
mGlFuncs = new QOpenGLFunctions();
mGlFuncs->initializeOpenGLFunctions();
}
painter->beginNativePainting();
verify_initialized();
draw_open_gl();
painter->endNativePainting();
if (mDebug) qDebug("Simple3d finished rendering.");
}
GLuint Simple3d::load_shader(GLenum type, const char *shaderSrc)
{
GLuint shader;
GLint compiled;
// Create the shader object
shader = mGlFuncs->glCreateShader(type);
if(shader == 0)
return 0;
// Load the shader source
mGlFuncs->glShaderSource(shader, 1, &shaderSrc, nullptr);
// Compile the shader
mGlFuncs->glCompileShader(shader);
// Check the compile status
mGlFuncs->glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if(!compiled)
{
GLint infoLen = 0;
mGlFuncs->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if(infoLen > 1)
{
char* infoLog = new char[size_t(infoLen)+1];
mGlFuncs->glGetShaderInfoLog(shader, infoLen, nullptr, infoLog);
qDebug("In the shader source:\n%s", shaderSrc);
qDebug("Error compiling shader:\n%s\n", infoLog);
delete [] infoLog;
}
mGlFuncs->glDeleteShader(shader);
return 0;
}
return shader;
}
void Simple3d::verify_initialized()
{
if (mOpenGlInitialized) return;
// This piece is partially taken from:
// https://www.khronos.org/assets/uploads/books/openglr_es_20_programming_guide_sample.pdf
mWidth = 50;
mHeight = 50;
const char vShaderStr[] =
"attribute vec4 vPosition;\n"
"void main()\n"
"{\n"
" gl_Position = vPosition;\n"
"}\n";
const char fShaderStr[] =
"//precision mediump float;\n"
"void main()\n"
"{\n"
" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); \n"
"}\n";
GLuint vertexShader;
GLuint fragmentShader;
GLint linked;
// Load the vertex/fragment shaders
vertexShader = load_shader(GL_VERTEX_SHADER, vShaderStr);
fragmentShader = load_shader(GL_FRAGMENT_SHADER, fShaderStr);
// Create the program object
mProgramId = mGlFuncs->glCreateProgram();
if(mProgramId == 0) {
qDebug("Failed to create the OpenGL program.");
return;
}
mGlFuncs->glAttachShader(mProgramId, vertexShader);
mGlFuncs->glAttachShader(mProgramId, fragmentShader);
// Bind vPosition to attribute 0
mGlFuncs->glBindAttribLocation(mProgramId, mVertexArrayID, "vPosition");
// Link the program
mGlFuncs->glLinkProgram(mProgramId);
// Check the link status
mGlFuncs->glGetProgramiv(mProgramId, GL_LINK_STATUS, &linked);
if(!linked)
{
qDebug("Failed to link OpenGL program.");
GLint infoLen = 0;
mGlFuncs->glGetProgramiv(mProgramId, GL_INFO_LOG_LENGTH, &infoLen);
if(infoLen > 1)
{
char* infoLog = new char[size_t(infoLen)];
mGlFuncs->glGetProgramInfoLog(mProgramId, infoLen, nullptr, infoLog);
qDebug("Error linking program:\n%s\n", infoLog);
delete [] infoLog;
}
mGlFuncs->glDeleteProgram(mProgramId);
return;
}
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
mOpenGlInitialized = true;
qDebug("OpenGL was initialized successfully.");
return;
}
void Simple3d::draw_open_gl()
{
if (!mOpenGlInitialized){
QGuiApplication::exit(1);
return;
}
mGlFuncs->glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
mGlFuncs->glEnable(GL_DEPTH_TEST);
mGlFuncs->glUseProgram(mProgramId);
GLfloat vVertices[] =
{0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f};
// Set the viewport
mGlFuncs->glViewport(0, 0, mWidth, mHeight);
// Clear the color buffer
glClear(GL_COLOR_BUFFER_BIT);
// Load the vertex data
mGlFuncs->glVertexAttribPointer(mVertexArrayID, 3, GL_FLOAT, GL_FALSE, 0, vVertices);
mGlFuncs->glEnableVertexAttribArray(0);
mGlFuncs->glDrawArrays(GL_TRIANGLES, 0, 3);
mGlFuncs->glFlush();
mGlFuncs->glFinish();
mGlFuncs->glUseProgram(0); // release the program.
}
Simple3d::~Simple3d()
{
}
void Simple3d::register_qml_item()
{
qmlRegisterType<Simple3d>("CustomGraphicsItems", 1, 0, "Simple3d");
}
/// main.cpp
#include "simple3d.h"
#include <stdexcept>
using namespace std;
int main(int argc, char *argv[]){
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
Simple3d::register_qml_item();
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/example_Simple3d.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl)
{
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
int num_root_objects = engine.rootObjects().size();
qDebug("The number of root objects: %i", num_root_objects);
if (engine.rootObjects().size()!=1){
throw logic_error("Expected exactly one root object"
"in the qml file");
}
QObject * root = engine.rootObjects()[0];
Simple3d * draw_3d_item = root->findChild<Simple3d*>("draw_3d_item");
if (!draw_3d_item){
throw logic_error("Did not find item 'draw_3d_item'.");
}
int result = app.exec();
qDebug("The app is exiting.");
return result;
}
/// example_Simple3d.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import CustomGraphicsItems 1.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
Window {
visible: true
Simple3d{
id: draw_3d_item
width: 50
height: 50
anchors.fill: parent
objectName: "draw_3d_item"
}
}
The code should display a red triangle on black background, but nothing is being drawn.
This question is similar to this: Rendering custom opengl in qt5's qtquick 2.0
I liked the answer by Gunnar Sletta. The trick is to use QQuickFramebufferObject, not QtQuickPaintedItem.
The first lines of QQuickFramebufferObject reference page say that it is an auxiliary class, but in fact it is the key class if you want to create a custom OpenGL within QtQuick framework. They should have called it
QQuickOpenGLItem.
Just read this is the example of using QQuickFramebufferObject:
https://doc.qt.io/qt-5/qtquick-scenegraph-textureinsgnode-example.html (Click on link "Example project # code.qt.io".)

how to draw OpenGL geometry in Qt 5.7 using glew

I am trying to render a triangle in a QGLWidget (Qt5.7) but by some reason, I am unable to show the triangle on screen, only the blue background is showed.
myapp.pro file:
QT += core gui opengl
CONFIG += c++11
TARGET = myapp
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
INCLUDEPATH += ../../libs/glew/include
SOURCES += main.cpp \
../../libs/glew/src/glew.c \
glwindow.cpp
HEADERS += \
../../libs/glew/include/GL/glew.h \
glwindow.h
This is the main function:
#include <QApplication>
#include <glwindow.h>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
glwindow v;
v.show();
return app.exec();
}
The window header:
#include <QtOpenGL/QGLWidget>
class glwindow : public QGLWidget
{
public:
glwindow();
protected:
void initializeGL() override;
void paintGL() override;
};
The cpp file:
#include <GL/glew.h>
#include <glwindow.h>
GLfloat vertices[] = {
+0.0f, +1.0f,
-1.0f, -1.0f,
+1.0f, -1.0f
};
glwindow::glwindow()
{}
void setupGeometry()
{
GLuint buffer_id;
glGenBuffers(1, &buffer_id);
glBindBuffer(GL_ARRAY_BUFFER, buffer_id);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
}
void glwindow::initializeGL()
{
glewInit();
setupGeometry();
}
void glwindow::paintGL()
{
glClearColor(0.0, 0.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
Update 1:
Added the shader code:
const GLchar *vs = "#version 150 // Specify which version of GLSL we are using."
"in vec2 in_Position;"
"void main() "
"{"
" gl_Position = vec4(in_Position.x, in_Position.y, 0.5, 1.0);"
"}";
const GLchar *fs = "#version 150 // Specify which version of GLSL we are using."
"precision highp float; // Video card drivers require this line to function properly"
"out vec4 fragColor;"
"void main()"
"{"
" fragColor = vec4(1.0,1.0,1.0,1.0); //Set colour of each fragment to WHITE"
"}";
The function that setup shader is:
void checkShader(GLuint ID)
{
GLint compile_status = 0;
glGetShaderiv(ID, GL_COMPILE_STATUS, &compile_status);
if(compile_status != GL_TRUE)
{
GLint info_length;
GLsizei buffer_size;
glGetShaderiv(ID, GL_INFO_LOG_LENGTH, &info_length);
GLchar *message = new GLchar[info_length];
glGetShaderInfoLog(ID, info_length, &buffer_size, message);
delete[] message;
}
}
void initShader()
{
GLuint program_id;
GLuint vs_id, fs_id;
vs_id = glCreateShader(GL_VERTEX_SHADER);
fs_id = glCreateShader(GL_FRAGMENT_SHADER);
const char *adapter[1];
adapter[0] = vs;
glShaderSource(vs_id, 1, adapter, 0);
adapter[1] = fs;
glShaderSource(fs_id, 1, adapter, 0);
glCompileShader(vs_id);
checkShader(vs_id);
glCompileShader(fs_id);
checkShader(fs_id);
program_id = glCreateProgram();
glAttachShader(program_id, vs_id);
glAttachShader(program_id, fs_id);
glLinkProgram(program_id);
glUseProgram(program_id);
}
So, the init function is changed to
void glview::initializeGL()
{
glewInit();
initGeometry();
initShader();
}
The shader initialization is failed with error message :
(GLchar *) 0x7efe21 \":1(10): error: GLSL 1.50 is not supported. Supported versions are: 1.10, 1.20, 1.30, 1.00 ES, and 3.00 ES\n\"
Qt 5.7 only supports to glsl 1.3.0. So if ppl want to use glsl 4.3, try glfw or SDL.

Why don't these GLSL shaders work?

I'm trying to render a spiral as all red using the shaders to override the colours. For some reason they compile and link but do nothing.
See the following code
fragmentshader.glsl
#version 430
out vec4 outColor;
in vec4 color;
void main(){
outColor = color;
}
vertexshader.glsl
#version 430
in layout(location=0) vec2 position;
out vec4 color;
void main(){
gl_Position = vec4(position, 0.0, 1.0);
color = vec4(1.0, 0.0f, 0.0f, 1.0f);
}
Window.cpp
#include <GL\glew.h>
#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include "MeGLWindow.h"
#include <iostream>
#include <fstream>
#define Pi 3.14159265358979
#define E 2.718281828F
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
#define MAX_SPIRAL 25000
std::string readFile(const char* relPath);
void installShaders();
bool checkShaderStatus(GLuint shaderID);
bool checkProgramStatus(GLuint programID);
GLuint programID;
void MeGLWindow::initializeGL() {
glewInit();
glEnable(GL_DEPTH_TEST);
installShaders();
const int verts_num = MAX_SPIRAL * 2;
GLfloat verts[50000];
GLuint myBufferID;
float a = 0.06f;
float b = 0.06f;
float cx = 0.0;
float cy = 0.0;
int z = 0;
for (int i = 0; i < MAX_SPIRAL; i++) {
float ang = (Pi / 720) * i;
float factor = pow(E, b * ang);
float x = cx + (a * (cos(ang)) * factor);
float y = cy - (a * (sin(ang)) * factor);
verts[2 * i] = x;
verts[(2 * i) + 1] = y;
}
glGenBuffers(1, &myBufferID);
glBindBuffer(GL_ARRAY_BUFFER, myBufferID);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
}
void MeGLWindow::paintGL() {
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glViewport(0, 0, width(), height());
glDrawArrays(GL_POINTS, 0, 2*25000);
}
void installShaders() {
GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
const char* adapter[1];
std::string file = readFile("vertexshader.glsl");
const char* vertexFile = file.c_str();
adapter[0] = vertexFile;
glShaderSource(vertexShaderID, 1, adapter, 0);
file = readFile("fragmentshader.glsl");
const char* fragmentFile = file.c_str();
adapter[0] = fragmentFile;
glShaderSource(fragmentShaderID, 1, adapter, 0);
glCompileShader(vertexShaderID);
glCompileShader(fragmentShaderID);
if (!checkShaderStatus(vertexShaderID) || !checkShaderStatus(fragmentShaderID)) {
return;
}
programID = glCreateProgram();
glAttachShader(programID, vertexShaderID);
glAttachShader(programID, fragmentShaderID);
glLinkProgram(programID);
if (!checkProgramStatus(programID)) {
return;
}
glUseProgram(programID);
}
main.cpp
#include <Qt\qapplication.h>
#include "MeGLWindow.h"
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
MeGLWindow meWindow;
meWindow.show();
return app.exec();
}
The vertices in the spiral should be red according to the linked shaders! What am i doing wrong? Please help!
No error checking on the shader program. Or rather, you check for errors, but don't signal the application that the program creation failed in any way.
You don't provide the source for checkProgramStatus, but if you checking for GL_LINK_STATUS, you're doing so before you link the problem.
It's not clear how you're initializing OpenGL from the code provided, but you're not setting a VAO, which is required for the core profile.

Can't enable depth test with OpenGL in QWIndow (Qt5)

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.

Fail to generate a triangle by openGL with Qt5.0.2

OS : mac osx 10.8.3
compiler : clang3.2
I am a beginner of opengl, trying to play opengl with Qt5
There are two problems about this simple program(plot a triangle)
I can't see the triangle
The program could not exit even I close the window
hpp
#include <QGLWidget>
#include <QtGui/QOpenGLFunctions>
#include <QtGui/QOpenGLShaderProgram>
class QWidget;
class ch1HelloTriangle : public QGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
explicit ch1HelloTriangle(QWidget *parent = 0);
protected:
virtual void initializeGL();
void initShaders();
void InitializeVertexBuffer();
virtual void resizeGL(int w, int h);
virtual void paintGL();
private:
QOpenGLShaderProgram program;
GLuint positionBufferObject;
};
.cpp
#include <locale.h>
#include <QWidget>
#include "ch1HelloTriangle.hpp"
namespace
{
float const vertexPositions[] = {
0.75f, 0.75f, 0.0f, 1.0f,
0.75f, -0.75f, 0.0f, 1.0f,
-0.75f, -0.75f, 0.0f, 1.0f,
};
}
ch1HelloTriangle::ch1HelloTriangle(QWidget *parent) :
QGLWidget(parent)
{
}
void ch1HelloTriangle::initializeGL()
{
initializeOpenGLFunctions();
InitializeVertexBuffer();
initShaders();
}
void ch1HelloTriangle::initShaders()
{
// Override system locale until shaders are compiled
setlocale(LC_NUMERIC, "C");
// Compile vertex shader
if (!program.addShaderFromSourceCode(QOpenGLShader::Vertex,
"attribute vec4 position;\n"
"void main()\n"
"{\n"
" gl_Position = position;\n"
"}\n"))
{
close();
}
// Compile fragment shader
if (!program.addShaderFromSourceCode(QOpenGLShader::Fragment,
"out vec4 outputColor;\n"
"void main()\n"
"{\n"
" outputColor = vec4(1.0f, 1.0f, 1.0f, 1.0f);\n"
"}\n"))
{
close();
}
// Link shader pipeline
if (!program.link())
close();
// Bind shader pipeline for use
if (!program.bind())
close();
// Restore system locale
setlocale(LC_ALL, "");
}
void ch1HelloTriangle::InitializeVertexBuffer()
{
glGenBuffers(1, &positionBufferObject);
glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void ch1HelloTriangle::resizeGL(int w, int h)
{
// Set OpenGL viewport to cover whole widget
glViewport(0, 0, w, h);
}
void ch1HelloTriangle::paintGL()
{
/*
//codes propose by http://stackoverflow.com/questions/13111291/displaying-a-triangle-with-qt-and-opengl?rq=1, can't see the triangle either
QSize viewport_size = size();
glViewport(0, 0, viewport_size.width(), viewport_size.height());
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1, 1, -1, 1, 5, 7); // near and far match your triangle Z distance
glMatrixMode(GL_MODELVIEW);*/
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
int vertexLocation = program.attributeLocation("position");
program.enableAttributeArray(vertexLocation);
glVertexAttribPointer(vertexLocation, 4, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
main.cpp
#include <QApplication>
#include "ch1HelloTriangle.hpp"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ch1HelloTriangle ch1;
ch1.show();
return a.exec();
}
After a lot of trial and error, I solved the problem.
Change two things :
1 : read the shader by files
// Compile vertex shader
if (!program.addShaderFromSourceFile(QOpenGLShader::Vertex, "modernOpenGLShader/ch1/vertex")){
QMessageBox::warning(this, "QOpenGLShader::Vertex", "QOpenGLShader::Vertex" + program.log());
close();
}
// Compile fragment shader
if (!program.addShaderFromSourceFile(QOpenGLShader::Fragment, "modernOpenGLShader/ch1/frag")){
QMessageBox::warning(this, "QOpenGLShader::Fragment", "QOpenGLShader::Fragment" + program.log());
close();
}
2 : change the fragment shader, remove the output variable
void main() {
//set every drawn pixel to white
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
From the websites, "modern opengl":http://www.arcsynthesis.org/gltut/
and "another site":tomdalling.com/blog/modern-opengl/01-getting-started-in-xcode-and-visual-cpp/
The output qualify should exist(atleast the codes of the second website will work on my pc)
But Qt will throw error message
QOpenGLShader::FragmentERROR: 0:4: Invalid qualifiers 'out' in global variable context
Do anyone know why?Thanks