Fail to generate a triangle by openGL with Qt5.0.2 - c++

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

Related

wxGLCanvas huge memory usage after resizing

I have created a simple wxGLCanvas for demonstrating OpenGl using wxWidgets. The demo is working fine except when resizing the window the memory usage increases from a few megabytes to almost 400 megabytes and it stays there and doesn't decrease, here are the code snippets.
// ctor
TriangleCanvas::TriangleCanvas(wxWindow* parent, wxGLAttributes& attribList)
: wxGLCanvas(parent, attribList, wxID_ANY, { 0,0 }, wxDefaultSize),
m_vbo(0), m_vao(0), ctx_attr(new wxGLContextAttrs)
{
ctx_attr->CoreProfile().OGLVersion(4, 3).EndList();
m_context = new wxGLContext(this, NULL, ctx_attr);
Bind(wxEVT_PAINT, &TriangleCanvas::OnPaint, this);
Bind(wxEVT_SIZE, &TriangleCanvas::Resize, this);
}
// Paint method
void TriangleCanvas::OnPaint(wxPaintEvent& event)
{
wxPaintDC(this);
SetCurrent(*m_context);
shader->use();
// set background to black
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// draw the graphics
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glFlush();
SwapBuffers();
}
void TriangleCanvas::Resize(wxSizeEvent& event) {
event.Skip();
glViewport(0, 0, event.GetSize().x, event.GetSize().y);
if (!setup) {
InitializeGLEW();
SetupGraphics();
}
}
I think the best way to use wxGLCanvas with an extension loader is to use a helper class and keep all OpenGL drawing in the cpp portion of that helper class.
For example, here is a small helper class for drawing a triangle:
glhelper.h
#ifndef GLHELPER_H_INCLUDED
#define GLHELPER_H_INCLUDED
class GLHelper
{
public:
bool InitGlew();
void Render();
void SetSize(int w, int h);
bool InitData();
void Cleanup();
private:
unsigned int m_VBO, m_VAO, m_shaderProgram;
};
#endif // GLHELPER_H_INCLUDED
glhelper.cpp
#include <GL/glew.h>
#ifdef __WXMSW__
#include <GL/wglew.h>
#elif defined(__WXGTK__)
#include <GL/glxew.h>
#endif // defined
#include "glhelper.h"
static const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
static const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
bool GLHelper::InitGlew()
{
GLenum initStatus = glewInit();
return initStatus == GLEW_OK;
}
bool GLHelper::InitData()
{
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// check for shader compile errors
int success;
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
return false;
}
// fragment shader
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
return false;
}
// link shaders
m_shaderProgram = glCreateProgram();
glAttachShader(m_shaderProgram, vertexShader);
glAttachShader(m_shaderProgram, fragmentShader);
glLinkProgram(m_shaderProgram);
// check for linking errors
glGetProgramiv(m_shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
return false;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float vertices[] = {
-0.5f, -0.5f, 0.0f, // left
0.5f, -0.5f, 0.0f, // right
0.0f, 0.5f, 0.0f // top
};
//unsigned int VBO, VAO;
glGenVertexArrays(1, &m_VAO);
glGenBuffers(1, &m_VBO);
// bind the Vertex Array Object first, then bind and set vertex buffer(s),
// and then configure vertex attributes(s).
glBindVertexArray(m_VAO);
glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// note that this is allowed, the call to glVertexAttribPointer registered
//VBO as the vertex attribute's bound vertex buffer object so afterwards we
//can safely unbind
glBindBuffer(GL_ARRAY_BUFFER, 0);
// You can unbind the VAO afterwards so other VAO calls won't accidentally
//modify this VAO, but this rarely happens. Modifying other VAOs requires a
//call to glBindVertexArray anyways so we generally don't unbind VAOs (nor
// VBOs) when it's not directly necessary.
glBindVertexArray(0);
return true;
}
void GLHelper::Cleanup()
{
glDeleteVertexArrays(1, &m_VAO);
glDeleteBuffers(1, &m_VBO);
glDeleteProgram(m_shaderProgram);
}
void GLHelper::Render()
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// draw our first triangle
glUseProgram(m_shaderProgram);
glBindVertexArray(m_VAO);
// seeing as we only have a single VAO there's no need to bind it every
//time, but we'll do so to keep things a bit more organized
glDrawArrays(GL_TRIANGLES, 0, 3);
}
void GLHelper::SetSize(int width, int height)
{
glViewport(0, 0, width, height);
}
And here is a short demo that uses this helper class to draw the trinagle.
demo.cpp
#include "wx/wx.h"
#include <wx/glcanvas.h>
#include "glhelper.h"
class wxGlewFrame: public wxFrame
{
public:
wxGlewFrame(wxWindow*);
~wxGlewFrame();
private:
void OnCanvasSize(wxSizeEvent&);
void OnCanvasPaint(wxPaintEvent&);
void InitGL();
wxGLCanvas* m_canvas;
wxGLContext* m_context;
GLHelper m_helper;
};
wxGlewFrame::wxGlewFrame(wxWindow* parent)
: wxFrame(parent, wxID_ANY, wxString())
{
// Create the canvas and context.
#if wxCHECK_VERSION(3,1,0)
// These settings should work with any GPU from the last 10 years.
wxGLAttributes dispAttrs;
dispAttrs.PlatformDefaults().RGBA().DoubleBuffer().EndList();
wxGLContextAttrs cxtAttrs;
cxtAttrs.PlatformDefaults().CoreProfile().OGLVersion(3, 3).EndList();
m_canvas = new wxGLCanvas(this, dispAttrs);
m_context = new wxGLContext(m_canvas, NULL, &cxtAttrs);
if ( !m_context->IsOK() )
{
SetTitle("Failed to create context.");
return;
}
#else
int dispAttrs[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_CORE_PROFILE,
WX_GL_MAJOR_VERSION ,3, WX_GL_MINOR_VERSION, 3, 0 };
m_canvas = new wxGLCanvas(this, wxID_ANY, dispAttrs);
m_context = new wxGLContext(m_canvas, NULL);
// Unfortunately, there doesn't seem to be any way to check if the
// context is ok prior to wxWidgets 3.1.0.
#endif // wxCHECK_VERSION
// On Linux, we must delay delay initialization until the canvas has
// been full created. On windows, we can finish now.
#ifdef __WXMSW__
InitGL();
#elif defined(__WXGTK__)
m_canvas->Bind(wxEVT_CREATE, [this](wxWindowCreateEvent&){InitGL();});
#endif // defined
}
wxGlewFrame::~wxGlewFrame()
{
m_helper.Cleanup();
delete m_context;
}
void wxGlewFrame::OnCanvasSize(wxSizeEvent& event)
{
wxSize sz = event.GetSize();
m_helper.SetSize(sz.GetWidth(), sz.GetHeight());
event.Skip();
}
void wxGlewFrame::OnCanvasPaint(wxPaintEvent&)
{
wxPaintDC dc(m_canvas);
m_helper.Render();
m_canvas->SwapBuffers();
}
void wxGlewFrame::InitGL()
{
// First call SetCurrent or GL initialization will fail.
m_context->SetCurrent(*m_canvas);
// Initialize GLEW.
bool glewInialized = m_helper.InitGlew();
if ( !glewInialized )
{
SetTitle("Failed it initialize GLEW.");
return;
}
SetTitle("Context and GLEW initialized.");
// Initialize the triangle data.
m_helper.InitData();
// Bind event handlers for the canvas. Binding was delayed until OpenGL was
// initialized because these handlers will need to call OpenGL functions.
m_canvas->Bind(wxEVT_SIZE, &wxGlewFrame::OnCanvasSize, this);
m_canvas->Bind(wxEVT_PAINT, &wxGlewFrame::OnCanvasPaint, this);
}
class MyApp : public wxApp
{
public:
virtual bool OnInit()
{
wxGlewFrame* frame = new wxGlewFrame(NULL);
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(MyApp);
(This is basically the Hello Triangle sample from learnopengl.com except rewritten to use wxGLCanvas and GLEW instead of GLFW and GLAD.
This takes up about 19MB of memory on my system and only increases up to about 24 or 25MB when resizing. That might sound like a lot for such a simple program, but the running the official "Hello Triangle" sample uses 26MB. So I think the memory usage is about what should be expected.

QT OpenGL, glDrawElements and glDrawArray show only blank screen

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};

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.

QOpenGLWidget's resizeGL is NOT the place to call glViewport?

I am experimenting with the new QOpenGLWidget class (note that this is not the QGLWidget class).
I am drawing a triangle. I have a trivial vertex shader which receives coordinates in clip space, so no matrices or projections are involved. One of the vertices has coordinates -1, -1, 0, 1, and another one has coordinates 1, 1, 0, 1.
When I have no call to glViewport whatsoever, the program renders as if I am calling glViewport(0, 0, w, h); in my resizeGL function, which I am not. Namely, the two vertices of the triangle are attached to the lowerleft and upperright corners of the window no matter how I resize the window.
When I actually add a call to glViewport in my resizeGL function, it is apparently ignored - doesn't matter if I pass w/2, h/2 or any other value, the rendering is exactly the same as it would be if I called glViewport(0, 0, w, h); (for instance, I would expect the triangle to appear in the lower-left quarter of the window in case of glViewport(0, 0, w/2, h/2);)
When I call glViewport(0, 0, width()/2, height()/2) in paingGL function, the rendering is as expected - everything is drawn in the lower-left quarter of the window.
So it seems that the glViewport is overridden somewhere between resizeGL and paintGL. What is going on and how do I fix it? Do I have to resort to doing viewport transformations in my paintGL function?
One of the differences between QGLWidget and QOpenGLWidgets listed in the documentation is that the latter renders to a framebuffer rather than directly to the screen. Could this hold the key to the explanation?
Just in case, I'm attaching the complete code for reference.
//triangle.h
#ifndef TRIANGLE_H
#define TRIANGLE_H
#include <QOpenGLBuffer>
#include <QOpenGLFunctions>
class Triangle
{
public:
Triangle();
void render();
void create();
private:
QOpenGLBuffer position_vbo;
QOpenGLFunctions *glFuncs;
};
#endif // TRIANGLE_H
//triangle.cpp
#include "triangle.h"
Triangle::Triangle()
:position_vbo(QOpenGLBuffer::VertexBuffer)
{
}
void Triangle::create()
{
glFuncs = QOpenGLContext::currentContext()->functions();
position_vbo.create();
float val[] = {
-1.0f, -1.0f, 0.0f, 1.0f,
0.0f, -0.366f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
};
position_vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
position_vbo.bind();
position_vbo.allocate(val, sizeof(val));
position_vbo.release();
}
void Triangle::render()
{
position_vbo.bind();
glFuncs->glEnableVertexAttribArray(0);
glFuncs->glEnableVertexAttribArray(1);
glFuncs->glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
glFuncs->glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void*)(3*4*sizeof(float)));
glFuncs->glDrawArrays(GL_TRIANGLES, 0, 3);
glFuncs->glDisableVertexAttribArray(0);
glFuncs->glDisableVertexAttribArray(1);
position_vbo.release();
}
//widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include "triangle.h"
class Widget : public QOpenGLWidget
, protected QOpenGLFunctions
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
protected:
virtual void initializeGL() override;
virtual void paintGL() override;
virtual void resizeGL(int w, int h) override;
private:
QOpenGLShaderProgram* program;
Triangle t;
};
#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include <exception>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QOpenGLWidget(parent)
{
}
Widget::~Widget()
{
}
void Widget::initializeGL()
{
initializeOpenGLFunctions();
program = new QOpenGLShaderProgram(this);
if(!program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vertexshader.vert"))
{
throw std::exception(("Vertex Shader compilation error: " + program->log()).toLocal8Bit().constData());
}
if(!program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fragmentshader.frag"))
{
throw std::exception(("Fragment Shader compilation error: " + program->log()).toLocal8Bit().constData());
}
if(!program->link())
{
throw std::exception(("Program Link error: " + program->log()).toLocal8Bit().constData());
}
t.create();
}
void Widget::paintGL()
{
glClearColor(0.f, 0.15f, 0.05f, 0.f);
glClear(GL_COLOR_BUFFER_BIT);
//glViewport(0, 0, width()/2, height()/2); //works!!
program->bind();
t.render();
program->release();
}
void Widget::resizeGL(int w, int h)
{
glViewport(0, 0, w/2, h/2); //doesn't work
}
//main.cpp
#include "widget.h"
#include <exception>
#include <QApplication>
#include <QMessageBox>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
try
{
Widget w;
w.show();
return a.exec();
}
catch(std::exception const & e)
{
QMessageBox::warning(nullptr, "Error", e.what());
}
}
//vertex shader
#version 330
layout (location = 0) in vec4 position;
layout (location = 1) in vec4 color;
smooth out vec4 theColor;
void main()
{
gl_Position = position;
theColor = color;
}
//fragment shader
#version 330
out vec4 fragColor;
smooth in vec4 theColor;
void main()
{
fragColor = theColor;
}
So it seems that the glViewport is overridden somewhere between resizeGL and paintGL. What is going on and how do I fix it? Do I have to resort to doing viewport transformations in my paintGL function?
Qt5 may use OpenGL for its own drawing. Also the content of widgets being children to a QOpenGLWindow are rendered to FBOs. So that means, that a lot of glViewport calls are made between your code and what Qt does.
When I actually add a call to glViewport in my resizeGL function, it is apparently ignored (…)
yes. And your expectation is what exactly? The only valid place to call glViewport for a OpenGL program to be robust is in the drawing code. Each and every tutorial out there, that places glViewport in the window resize handler is wrong and should be burned.
When I call glViewport(0, 0, width()/2, height()/2) in paingGL function, the rendering is as expected
Yes, that's how you're supposed to use it.

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.