I've very recently picked up Qt and am using it with OpenGL
The thing though is that when moving my SDL code to Qt and changing the texture code to use QImage it stops working.
The image does load correctly, as shown through the error checking code.
Thanks!
P.S: Please don't suggest I use glDrawPixels, I need to fix the problem at hand. Some of the reasons for that being 1. slow 2. android (which this code may be running on eventually) is OpenGL ES and does not support glDrawPixels
Here's the code:
//original image
QImage img;
if(!img.load(":/star.png"))
{
//loads correctly
qWarning("ERROR LOADING IMAGE");
}
//array for holding texture ID
GLuint texture[1];
//get the OpenGL-friendly image
QImage GL_formatted_image;
GL_formatted_image = QGLWidget::convertToGLFormat(img);
//make sure its not null
if(GL_formatted_image.isNull())
qWarning("IMAGE IS NULL");
else
qWarning("IMAGE NOT NULL");
//generate the texture name
glGenTextures(1, texture);
//bind the texture ID
glBindTexture(GL_TEXTURE_2D, texture[0]);
//generate the texture
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, GL_formatted_image.width(),
GL_formatted_image.height(),
0, GL_RGBA, GL_UNSIGNED_BYTE, GL_formatted_image.bits() );
//texture parameters
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//draw the texture
glPushMatrix();
glTranslatef(-2.0f, 0.0f, 0.0f);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_TRIANGLES);
glVertex2f(1.0f, 0.0f);
glTexCoord2f(1.0f, 0.0f);
glVertex2f(0.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f);
glVertex2f(0.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f);
glEnd();
glPopMatrix();
Here's the original texture loading function with SDL:
GLuint loadTexturewithSDL(const char* FILE, GLenum texture_format)
{
GLuint texture; // This is a handle to our texture object
SDL_Surface *surface; // This surface will tell us the details of the image
GLint nOfColors;
if ( (surface = SDL_LoadBMP(FILE)) ) {
// Check that the image's width is a power of 2
if ( (surface->w & (surface->w - 1)) != 0 ) {
printf("warning: image's width is not a power of 2\n");
}
// Also check if the height is a power of 2
if ( (surface->h & (surface->h - 1)) != 0 ) {
printf("warning: image's height is not a power of 2\n");
}
// get the number of channels in the SDL surface
nOfColors = surface->format->BytesPerPixel;
if (nOfColors == 4) // contains an alpha channel
{
if (surface->format->Rmask == 0x000000ff)
texture_format = GL_RGBA;
else
texture_format = GL_BGRA;
} else if (nOfColors == 3) // no alpha channel
{
if (surface->format->Rmask == 0x000000ff)
texture_format = GL_RGB;
else
texture_format = GL_BGR;
} else {
printf("warning: the image is not truecolor.. this will probably break\n");
// this error should not go unhandled
}
// Have OpenGL generate a texture object handle for us
glGenTextures( 1, &texture );
// Bind the texture object
glBindTexture( GL_TEXTURE_2D, texture );
// Set the texture's stretching properties
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
// Edit the texture object's image data using the information SDL_Surface gives us
glTexImage2D( GL_TEXTURE_2D, 0, nOfColors, surface->w, surface->h, 0,
texture_format, GL_UNSIGNED_BYTE, surface->pixels );
}
else {
printf("SDL could not load image %s\n", SDL_GetError());
SDL_Quit();
return 1;
}
// Free the SDL_Surface only if it was successfully created
if ( surface ) {
SDL_FreeSurface( surface );
}
return texture;
}
I have similar code that works but uses glTexSubImage2D :
void Widget::paintGL()
{
glClear (GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0,win.width(),0,win.height());
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,texture);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0,0 , image.width(), image.height(), glFormat, glType, image.bits() );
glBegin(GL_QUADS); // in theory triangles are better
glTexCoord2i(0,0); glVertex2i(0,win.height());
glTexCoord2i(0,1); glVertex2i(0,0);
glTexCoord2i(1,1); glVertex2i(win.width(),0);
glTexCoord2i(1,0); glVertex2i(win.width(),win.height());
glEnd();
glFlush();
}
void Widget::initializeGL()
{
glClearColor (0.0,0.0,0.0,1.0);
glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0,win.width(),0,win.height());
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
glGenTextures(3,&texture);
glBindTexture(GL_TEXTURE_2D,texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D,texture);
glTexImage2D(GL_TEXTURE_2D, 0, glFormat, image.width(), image.height(), 0, glFormat, glType, NULL );
glDisable(GL_TEXTURE_2D);
}
And a few perfomance tweaks in the ctor
void Widget::setDisplayOptions()
{
glFormat = GL_RGB; // QImage RGBA is BGRA
glType = GL_UNSIGNED_BYTE;
QGL::setPreferredPaintEngine(QPaintEngine::OpenGL2);
QGLFormat glFmt;
glFmt.setSwapInterval(1); // 1= vsync on
glFmt.setAlpha(GL_RGBA==glFormat);
glFmt.setRgba(GL_RGBA==glFormat);
glFmt.setDoubleBuffer(true); // default
glFmt.setOverlay(false);
glFmt.setSampleBuffers(false);
QGLFormat::setDefaultFormat(glFmt);
setAttribute(Qt::WA_OpaquePaintEvent,true);
setAttribute(Qt::WA_PaintOnScreen,true);
}
This is my solution for conversion from Qt to GL
This also can work in reverse with little changes;
Cheers -- Daimon
void Image::Convert32bitARGBtoRGBA()
{
if(!isQtImage()) return;
QImage& q = *(m_data->image);
U32 count=0, max=(U32)(q.height()*q.width());
U32* p = (U32*)(q.bits());
U32 n;
while( count<max )
{
n = p[count]; //n = ARGB
p[count] = 0x00000000 |
((n<<8) & 0x0000ff00) |
((n<<8) & 0x00ff0000) |
((n<<8) & 0xff000000) |
((n>>24) & 0x000000ff);
// p[count] = RGBA
count++;
}
}
void Image::Convert32bitRGBAtoARGB()
{
if(!isQtImage()) return;
QImage& q = *(m_data->image);
U32 count=0, max=(U32)(q.height()*q.width());
U32* p = (U32*)(q.bits());
U32 n;
while( count<max )
{
n = p[count]; //n = RGBA
p[count] = 0x00000000 |
((n>>8) & 0x000000ff) |
((n>>8) & 0x0000ff00) |
((n>>8) & 0x00ff0000) |
((n<<24) & 0xff000000);
// p[count] = ARGB
count++;
}
}
It looks like your problem is not here, as I have no problem with the following code.
You should check your GL init and display setup.
Have you a glEnable(GL_TEXTURE_2D) somewhere ?
Also note glTexCoord2f must be before glVertex2f.
#include <GL/glut.h>
#include <QtOpenGL/qgl.h>
#include <iostream>
GLuint texture[1] ;
void LoadGLTextures( const char * name )
{
QImage img;
if( ! img.load( name ) )
{
std::cerr << "error loading " << name << std::endl ;
exit( 1 );
}
QImage GL_formatted_image;
GL_formatted_image = QGLWidget::convertToGLFormat(img);
if( GL_formatted_image.isNull() )
{
std::cerr << "error GL_formatted_image" << std::endl ;
exit( 1 );
}
glGenTextures(1, texture);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA,
GL_formatted_image.width(), GL_formatted_image.height(),
0, GL_RGBA, GL_UNSIGNED_BYTE, GL_formatted_image.bits() );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
}
void resize(int Width, int Height)
{
glViewport( 0 , 0 , Width , Height );
}
void draw()
{
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f);
glClear( GL_COLOR_BUFFER_BIT );
gluOrtho2D( -1 , 1 , -1 , 1 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glShadeModel( GL_FLAT );
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glBegin(GL_TRIANGLES);
glTexCoord2f( 1.0f, 0.0f);
glVertex2f( 1.0f, 0.0f);
glTexCoord2f( 0.0f, 1.0f);
glVertex2f( 0.0f, 1.0f);
glTexCoord2f( 0.0f, 0.0f);
glVertex2f( 0.0f, 0.0f);
glEnd();
glutSwapBuffers();
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA );
glutInitWindowSize(640, 480);
glutCreateWindow("Texture");
LoadGLTextures( "star.png" );
glutDisplayFunc( & draw );
glutReshapeFunc( & resize );
glutMainLoop();
return 1;
}
Related
Learning to display images using QOpenGLWidget. However, I've met some problems.
How can I pass the GLuint texture variable (the actual texture loaded from the image) into the shader scripts? Like how to bind GLuint texture to uniform sampler2D texture? Maybe I am just not realising I already did that.
What's the difference between attribute vec4 vertexColorIn and uniform sampler2D texture? I think the color comes from the texture.
Can I use glTexCoord2f() and glVertex2f() instead of glVertexAttribPointer() and glVertexAttribPointer()? It's because they seem better to me.
I am still not clear on the concept about how openGL displays an image, although I've done many researches. I'm not quit sure what I'm doing wrong. The image is NOT showing up.
MyGLWiget.cpp
shader scipts:
#define STR(x) #x
#define VS_LOCATION 0
#define FS_LOCATION 1
const char* vertextShader = STR(
attribute vec4 position;
attribute vec4 vertexColorIn;
varying vec4 vertexColorOut;
void main(void)
{
gl_Position = position;
vertexColorOut = vertexColorIn;
}
);
const char* fragmentShader = STR(
varying vec4 vertexColorOut;
uniform sampler2D texture;
void main(void)
{
??? = texture2D(???, textureOut).r // no clue how to use it
gl_FragColor = vertexColorOut;
}
);
loading an Image texture:
void MyGLWiget::loadTexture(const char* file_path)
{
img_data = SOIL_load_image(file_path, &width, &height, &channels, SOIL_LOAD_RGB);
glEnable(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, img_data);
SOIL_free_image_data(img_data);
}
initialization:
void MyGLWiget::initializeGL()
{
initializeOpenGLFunctions();
program.addShaderFromSourceCode(QGLShader::Vertex, vertextShader);
program.bindAttributeLocation("position", VS_LOCATION);
program.addShaderFromSourceCode(QGLShader::Fragment, fragmentShader);
program.bindAttributeLocation("vertexColorIn", FS_LOCATION);
program.link();
program.bind();
static const GLfloat ver[] = {
-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f
};
static const GLfloat tex[] = {
0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f
};
glVertexAttribPointer(VS_LOCATION, 2, GL_FLOAT, 0, 0, ver);
glEnableVertexAttribArray(VS_LOCATION);
glVertexAttribPointer(FS_LOCATION, 2, GL_FLOAT, 0, 0, tex);
glEnableVertexAttribArray(FS_LOCATION);
program.setUniformValue("texture", texture);
//texture = program.uniformLocation("texture");
}
paintGL:
I'm really confused with this part. I have no idea what should I use to make it to draw an image.
void MyGLWiget::paintGL()
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, img_data);
glUniform1i(texture, 0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 1);
}
How can I pass the GLuint texture variable (the actual texture loaded from the image) into the shader scripts? Like how to bind GLuint texture to uniform sampler2D texture? Maybe I am just not realising I already did that.
This binds the texture to texture unit 0:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
This is invalid because texture is not a uniform location, so remove this line:
glUniform1i(texture, 0); // <-- invalid
This is invalid too, because the uniform texture should be set to the number of the texture unit:
program.setUniformValue("texture", texture); // <-- invalid
So replace it with:
program.setUniformValue("texture", 0); // <-- sampler2D texture uses GL_TEXTURE0
Note: I'm assuming here that setUniformValue works correctly.
What's the difference between attribute vec4 vertexColorIn and uniform sampler2D texture? I think the color comes from the texture.
vertexColorIn comes from the VAO and is different for each vertex. texture is the sampler that samples from the texture that's bound to the texture unit that you set above.
In your code you don't need a vertex color, but you do need texture coordinates. So your shaders should look like:
const char* vertextShader = STR(
attribute vec4 position;
attribute vec4 texcoordIn;
varying vec4 texcoordOut;
void main(void)
{
gl_Position = position;
texcoordOut = texcoordIn;
}
);
const char* fragmentShader = STR(
varying vec4 texcoordOut;
uniform sampler2D texture;
void main(void)
{
gl_FragColor = texture2D(texture, texcoordOut);
}
);
Can I use glTexCoord2f() and glVertex2f() instead of glVertexAttribPointer() and glVertexAttribPointer()? It's because they seem better to me.
glTexCoord2f and glVertex2f are legacy functions that were removed in OpenGL 3, and are available only in the compatibility profile. You shall not use them.
This lines are in the wrong place:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
They shall go after you bound the texture:
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, img_data);
// sets the filtering for the bound texture:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
Since the question is tagged opengl-4: you don't need to set any uniforms in this case. You can specify the locations and the bindings directly in the shaders:
const char* vertextShader =
"#version 450 core\n" STR(
layout(location = 0) in vec4 position;
layout(location = 1) in vec4 texcoordIn;
layout(location = 0) out vec4 texcoordOut;
void main(void)
{
gl_Position = position;
texcoordOut = texcoordIn;
}
);
const char* fragmentShader =
"#version 450 core\n" STR(
layout(location = 0) in vec4 texcoord;
layout(binding = 0) uniform sampler2D TEX;
layout(location = 0) out vec4 OUT;
void main(void)
{
OUT = texture(TEX, texcoord);
}
);
a few edits
const char* vertextShader = STR(
attribute vec4 position;
attribute vec4 vertexColorIn;
varying vec4 vertexColorOut;
out vec2 TexCoord;//--->add
void main(void)
{
gl_Position = position;
vertexColorOut = vertexColorIn;
TexCoord = vec2(aPos.x/2.0+0.5, 0.5-aPos.y/2.0);//a hack,ideally you need to pass the UV coordinates for proper texture mapping.UVs need to be passed in as a uniform or an attribute depending on preference.
}
);
const char* fragmentShader = STR(
varying vec4 vertexColorOut;
uniform sampler2D texture;
in vec2 TexCoord; //---->add
void main(void)
{
gl_FragColor = texture2D(texture,TexCoord) //( no clue how to use it) -->here is the change
//gl_FragColor = vertexColorOut;
}
);
I have a Microsoft visual studio application that is grabbing frames from cameras and I am trying to display those frames in a Qt application. I am doing some processing with the frames using OpenCV, so the frames are Mat objects. I use QThreads to parallelize the application. I am getting a Access Violation reading location when I try to emit a Mat signal from my CameraThread class.
main.cpp
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow window;
window.show();
return app.exec();
}
mainwindow.cpp
#include "main_window.h"
MainWindow::MainWindow()
{
// create a horizontal widget
main_layout = new QVBoxLayout;
QHBoxLayout* row1 = new QHBoxLayout;
QHBoxLayout* row2 = new QHBoxLayout;
for (int i = 0; i < 1; i++) {
camera_array[i] = new CameraWidget(i);
if (i < 4)
row1->addWidget(camera_array[i]);
else
row2->addWidget(camera_array[i]);
}
main_layout->addLayout(row1);
main_layout->addLayout(row2);
// make the central widget the main layout window
central = new QWidget();
central->setLayout(main_layout);
setCentralWidget(central);
}
camerawidget.cpp
#include "stdafx.h"
#include "camera_widget.h"
CameraWidget::CameraWidget(int id)
{
camera_id = id;
qRegisterMetaType<cv::Mat>("cv::Mat");
current_frame = cv::imread("camera_1.png");
thread = new CameraThread(camera_id);
QObject::connect(thread, SIGNAL(renderFrame(cv::Mat)), this, SLOT(updateFrame(cv::Mat)));
thread->start();
}
CameraWidget::~CameraWidget()
{
qDebug("camera widget destructor");
thread->wait(5000);
}
// initializeGL() function is called just once, before paintGL() is called.
void CameraWidget::initializeGL()
{
qglClearColor(Qt::black);
glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 480.0f, 640.0f, 0.0f, 0.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
glGenTextures(3, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 480.0f, 640.0f, GL_BGR, GL_UNSIGNED_BYTE, NULL);
glDisable(GL_TEXTURE_2D);
}
void CameraWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 480.0f, 640.0f, 0.0f, 0.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
current_frame_i = QImage(current_frame.data, current_frame.cols, current_frame.rows, current_frame.cols * 3, QImage::Format_RGB888);
glBindTexture(GL_TEXTURE_2D, texture);
// ******************************
// getting access violation here
// ******************************
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 480.0f, 640.0f, 0.0f, GL_BGR, GL_UNSIGNED_BYTE, current_frame.ptr());
glBegin(GL_QUADS);
glTexCoord2i(0, 1); glVertex2i(0, 640.0f);
glTexCoord2i(0, 0); glVertex2i(0, 0);
glTexCoord2i(1, 0); glVertex2i(480.0f, 0);
glTexCoord2i(1, 1); glVertex2i(480.0f, 640.0f);
glEnd();
glFlush();
}
void CameraWidget::resizeGL(int w, int h)
{
// setup viewport, projection etc.
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 480.0f, 640.0f, 0.0f, 0.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void CameraWidget::updateFrame(cv::Mat image)
{
current_frame = image;
update();
}
camerathread.cpp
CameraThread::CameraThread(int id)
{
camera_q = new bounded_frame_queue(50);
}
void CameraThread::run()
{
cv::Mat image;
while (true) {
if (!camera_q->empty()) {
image = camera_q->pop();
if (!image.empty())
emit renderFrame(image);
}
else {
msleep(1);
}
}
}
When I emit renderFrame from the camerathread.cpp, I get an access violation reading location. I cannot read the current_frame.ptr() value in camerawidget.cpp.
Can someone direct me on how I can fix this issue?
What I see is happenning:
You get an image from queue. As per OpenCV docs:
Mat& cv::Mat::operator= ( const Mat & m )
Assigned, right-hand-side matrix. Matrix assignment is an O(1)
operation. This means that no data is copied but the data is shared
and the reference counter, if any, is incremented. Before assigning
new data, the old data is de-referenced via Mat::release .
Then you pass it as cv::Mat image (by value) when emitting signal. The copy constructor again doesn't copy any data:
Array that (as a whole or partly) is assigned to the constructed
matrix. No data is copied by these constructors. Instead, the header
pointing to m data or its sub-array is constructed and associated with
it. The reference counter, if any, is incremented. So, when you modify
the matrix formed using such a constructor, you also modify the
corresponding elements of m . If you want to have an independent copy
of the sub-array, use Mat::clone() .
Your data pointers are queued on UI thread
You get/try-get new frame triggering release from p.1
Your queued slot is executed and crashes...
Suggestion: I haven't worked much with it, but it seems something like cv::Mat::clone to make a deep copy is what you need, to prevent release of memory before it would be used by UI thread.
Or possibly it would be enough to define image right when you pop it from queue:
cv::Mat image = camera_q->pop();
I am trying to write a 2D application with openGL (do not ask why, it a test for a stage) where I have a texture I can move around with a drag and drop, rotate or scal with the mouse. My texture is applied on a square.
For these operations, I need to know if I am clicking on the polygon, and the distance and angle between my cursor and the polygon center (which can be moved).
I did not find how to do these with the tutorial I am following does not cover that.
How can I find those 2 informations?
Here is the code :
#include "glwidget.h"
#include <QMouseEvent>
#include <QKeyEvent>
#ifdef WIN32
#include <GL/glext.h>
PFNGLACTIVETEXTUREPROC pGlActiveTexture = NULL;
#define glActiveTexture pGlActiveTexture
#endif //WIN32
GlWidget::GlWidget(QWidget *parent) :
QGLWidget(QGLFormat(), parent)
{
}
GlWidget::~GlWidget(){}
QSize GlWidget::sizeHint() const
{
return QSize(640,480);
}
void GlWidget::resizeGL(int w, int h){
h = (h<=0?1:h);
pMatrix.setToIdentity();
pMatrix.perspective(60.0,(float)w /(float)h,0.1,10);
glViewport(0,0,w,h);
//glScissor(w*0.8,0,w*0.2,h);
//glDisable(GL_SCISSOR_TEST);
}
void GlWidget::initializeGL(){
#ifdef WIN32
glActiveTexture = (PFNGLACTIVETEXTUREPROC) wglGetProcAddress((LPCSTR) "glActiveTexture");
#endif
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
qglClearColor(QColor(Qt::black));
shaderProgram.addShaderFromSourceFile(QGLShader::Vertex, ":/vertexShader.vsh");
shaderProgram.addShaderFromSourceFile(QGLShader::Fragment, ":/fragmentShader.fsh");
shaderProgram.link();
vertices << QVector3D(-1,-1,-2) << QVector3D(1,-1,-2) << QVector3D(-1,1,-2)
<< QVector3D(-1,1,-2) << QVector3D(1,-1,-2) << QVector3D(1,1,-2);
textureCoordinates << QVector2D(0, 0) << QVector2D(1, 0) << QVector2D(0, 1)
<< QVector2D(0, 1) << QVector2D(1, 0) << QVector2D(1, 1);
texture = bindTexture(QPixmap(":/icone.png"));
//mMatrix.setToIdentity();
}
void GlWidget::paintGL(){
//qglClearColor(QColor(Qt::white));
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/*qglClearColor(QColor(Qt::black));
glScissor(this->width()*0.8,0,this->width()*0.2,this->height());
glEnable(GL_SCISSOR_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);*/
QMatrix4x4 mMatrix;
QMatrix4x4 vMatrix;
/* QMatrix4x4 cameraTransformation;
cameraTransformation.rotate(25, 0, 1, 0);
cameraTransformation.rotate(-25, 1, 0, 0);
QVector3D cameraPosition = cameraTransformation * QVector3D(0, 0, 2.5);
QVector3D cameraUpDirection = cameraTransformation * QVector3D(0, 1, 0);
vMatrix.lookAt(cameraPosition, QVector3D(0, 0, 0), cameraUpDirection);*/
shaderProgram.bind();
shaderProgram.setUniformValue("mvpMatrix", pMatrix*vMatrix*mMatrix);
shaderProgram.setUniformValue("texture", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glActiveTexture(0);
shaderProgram.setAttributeArray("vertex", vertices.constData());
shaderProgram.enableAttributeArray("vertex");
shaderProgram.setAttributeArray("textureCoordinate", textureCoordinates.constData());
shaderProgram.enableAttributeArray("textureCoordinate");
glDrawArrays(GL_TRIANGLES,0,vertices.size());
shaderProgram.disableAttributeArray("vertex");
shaderProgram.disableAttributeArray("textureCoordinate");
shaderProgram.release();
}
void GlWidget::mousePressEvent(QMouseEvent *event){
if(event->button()==Qt::LeftButton)
lastPos = event->pos();
event->accept();
}
void GlWidget::mouseMoveEvent(QMouseEvent *event){
if(!event->buttons().testFlag(Qt::LeftButton)) return;
int dx = event->x() - lastPos.x();
int dy = event->y() - lastPos.y();
if(keysPressed.contains(Qt::Key_Control) && !keysPressed.contains(Qt::Key_Shift)){
//TODO : implement scaling
}
else if(!keysPressed.contains(Qt::Key_Control) && keysPressed.contains(Qt::Key_Shift)){
//TODO : implement rotate
}
else if(!keysPressed.contains(Qt::Key_Control) && !keysPressed.contains(Qt::Key_Shift)){
//TODO : implement drag
}
}
void GlWidget::keyPressEvent(QKeyEvent *event){
keysPressed.append(event->key());
}
void GlWidget::keyReleaseEvent(QKeyEvent *event){
keysPressed.remove(keysPressed.lastIndexOf(event->key()));
}
All the comments are from test I've done and the original tutorial
I am following this tutorial with a few modifications and have got this code:
#define GLSL(src) "#version 330 core\n" #src
void MainWindow::initializeGL() {
glClearColor(0, 0, 0, 1);
// Generate buffers
GLfloat verticies[] = {
+0.0f, +1.0f, +0.0f,
-1.0f, -1.0f, +0.0f,
+1.0f, -1.0f, +0.0f,
};
GLuint vertexBufferID;
glGenBuffers(1, &vertexBufferID);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
glBufferData(GL_ARRAY_BUFFER, sizeof(verticies), verticies, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void *)0);
// Generate shaders
const char *vertexShaderSrc = GLSL(
layout(location = 0) in vec3 pos;
void main() {
gl_Position.xyz = pos;
gl_Position.w = 1.0;
}
);
GLuint vertexShaderID = createGLShader(GL_VERTEX_SHADER, vertexShaderSrc);
const GLchar *fragmentShaderSrc = GLSL(
out vec4 color;
void main() {
color = vec4(0.0, 1.0, 0.0, 1.0);
}
);
GLuint fragmentShaderID = createGLShader(GL_FRAGMENT_SHADER, fragmentShaderSrc);
GLuint programID = glCreateProgram();
glAttachShader(programID, vertexShaderID);
glAttachShader(programID, fragmentShaderID);
glLinkProgram(programID);
glUseProgram(programID);
}
void MainWindow::paintGL() {
//glViewport(0, 0, width(), height());
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 3);
//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
}
GLuint MainWindow::createGLShader(GLenum type, const GLchar* src) {
GLuint shaderID = glCreateShader(type);
glShaderSource(shaderID, 1, &src, 0);
glCompileShader(shaderID);
GLint vertexCompileStatus;
glGetShaderiv(shaderID, GL_COMPILE_STATUS, &vertexCompileStatus);
if (vertexCompileStatus != GL_TRUE) {
GLint infoLogLength;
glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar buffer[infoLogLength];
glGetShaderInfoLog(shaderID, infoLogLength, 0, buffer);
qDebug(buffer);
}
return shaderID;
}
This is all contained in a QGLWidget. However when I run this code I just get a black screen. What is going wrong? I don't get an error message so the shaders are compiling.
I set up the QGLWidget:
#include "mainwindow.h"
#include <QApplication>
#include <QGLFormat>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QGLFormat glFormat;
glFormat.setVersion(3, 3);
glFormat.setProfile(QGLFormat::CoreProfile);
MainWindow w(glFormat);
w.show();
return a.exec();
}
Staying with "pure" OpenGL code, you need (at least) a Vertex Array Object. That object needs to be bound when you configure the vertex arrays, and everytime you draw from the aforementioned arrays.
So, before the calls to gl*VertexAttribArray, create and bind the VAO. Add a
GLuint m_vao;
member to your class. Then in initializeGL:
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
// now configure the arrays:
glEnableVertexAttribArray...
glVertexAttribArray...
// now release the VAO and move on
glBindVertexArray(0);
Then in paintGL we need the VAO again:
glBindVertexArray(m_vao);
glDrawArrays(...);
glBindVertexArray(0);
And now your code with Qt 5 OpenGL enablers (didn't try to compile it, but you can get the idea). You tell me which one is more readable and less error prone.
#define GLSL(src) "#version 330 core\n" #src
void MainWindow::initializeGL() {
glClearColor(0, 0, 0, 1);
// Generate buffers
GLfloat verticies[] = {
+0.0f, +1.0f, +0.0f,
-1.0f, -1.0f, +0.0f,
+1.0f, -1.0f, +0.0f,
};
m_vertexBuffer = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
m_vertexBuffer->create();
m_vertexBuffer->setusagePatter(QOpenGLBuffer::StaticDraw);
m_vertexBuffer->bind();
m_vertexBuffer->allocate(verticies, sizeof(verticies);
m_vertexBuffer->release();
// Generate shaders
const char *vertexShaderSrc = GLSL(
layout(location = 0) in vec3 pos;
void main() {
gl_Position.xyz = pos;
gl_Position.w = 1.0;
}
);
const GLchar *fragmentShaderSrc = GLSL(
out vec4 color;
void main() {
color = vec4(0.0, 1.0, 0.0, 1.0);
}
);
m_program = new QOpenGLShaderProgram;
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSrc);
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSrc);
m_program->link();
// error checking missing from the last three calls. if they return false, check log()
m_vao = new QOpenGLVertexArrayObject;
m_vao->bind();
m_program->bind();
m_vertexBuffer->bind();
m_program->enableAttributeArray("pos");
m_program->setAttributeBuffer("pos", GL_FLOAT, 0, 3);
m_vertexBuffer->release();
m_program->release();
m_vao->release();
}
void MainWindow::paintGL() {
glClear(GL_COLOR_BUFFER_BIT);
m_vao->bind();
m_program->bind();
glDrawArrays(GL_TRIANGLES, 0, 3);
m_program->release();
m_vao->release();
}
I draw my background every frames :
void Window::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
backgrd->Draw();
texture = text->loadTexture(text->amis_path.toStdString(),text->w,text->h);
amis->Draw();
texture = text->loadTexture(text->enemis_path.toStdString(),text->w,text->h);
for (int i = 0; i<liste_enemis.length(); i++){
liste_enemis[i]->Draw();
}
for (int i = 0; i<liste_missiles.length(); i++){
liste_missiles[i]->Draw();
}
swapBuffers();
}
But when I run the game, the fps a pretty bad (1fps).
Edit :
Well I'm trying to load my background in a texture one time but it doesn't work :
Background::Background(int w, int h)
{
width = w;
height = h;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D,0, GL_RGBA, back.width(), back.height(), 0,
GL_RGBA, GL_UNSIGNED_BYTE, back.bits());
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
void Background::Draw(){
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,texture);
glPushMatrix();
glTranslatef(width/2, height/2, 0.0f);
glBegin(GL_QUADS);
glTexCoord2d(0,0); glVertex3f(-width/2, -height/2, 0.0f);
glTexCoord2d(0,1); glVertex3f(-width/2, height/2, 0.0f);
glTexCoord2d(1,1); glVertex3f(width/2, height/2, 0.0f);
glTexCoord2d(1,0); glVertex3d(width/2, -height/2, 0.0f);
glEnd();
glPopMatrix();
}
The constructor load the background with glTexImage2D(...) and links it to the "texture" variable.
Then in my paintGL() function, I call the Background::Draw() function.
But when I run the game, there is no background.
If I move glTexParameteri(...) and glTexImage2D(...) functions from constructor to Draw function, it works.
You're reloading the textures each and every frame, that's what's causing your performance it (it will also consume your memory in no time).
Instead of reloading the texture every frame, load them only once, and then switch between textures using glBindTexture