Reading fragment shader outputs into application memory - qt

I'm trying to use OpenGL for visibility testing for complex geometries. What I want to do is simple: assign each primitive an integer ID, and then count the number of pixels with that ID. That allows me to calculate the relative visible area of each primitive. (Ultimately, this will be expanded to some minor finite-element calculations on the visible area.)
My problem is this. I'm trying to read the output of a fragment shader into my application memory: specifically, the primitive ID output. I'm using QT 4.7.4 and its OpenGL wrapper classes. When I bind and enable a buffer (a "PixelPack" buffer), and attempt to read from the OpenGL buffer into the memory, it reports a successful read. But the values stored in the array are not what I expect- they're all 0, even though for testing purposes I've set the ID to 1 for all primitives.
Here's my fragment shader:
#version 130
in vec4 Color;
flat in uint VertId;
out vec4 FragColor;
out uint FragId;
void main()
{
FragColor = Color;
// Changed to simpler version for debugging.
// FragId = VertId;
FragId = uint( 1 );
}
And here's my application code, with some irrelevant parts stripped off (test harness hookups, etc.):
#include <QtOpenGL/QGLShader>
#include <QtOpenGL/QGLShaderProgram>
#include <QtOpenGL/QGLBuffer>
using namespace std;
string loadSource( string file );
int
testSelfShadow::
shader( ostream& error )
{
bool fail = false;
// Create the OpenGL context.
int argc = 0;
char* argv;
QApplication* app = new QApplication( argc, &argv );
QGLWidget* widget = new QGLWidget();
widget->makeCurrent();
// Create the shader program.
QGLShaderProgram* prog = new QGLShaderProgram();
bool success = false;
success = prog->addShaderFromSourceCode( QGLShader::Vertex,
loadSource( "vertex.glsl" ).c_str() );
if ( ! success )
{
ErrorOStream msg;
msg << "Error trying to load vertex shader into a shader program.\n"
<< prog->log().toStdString();
throw ERRORLOG( msg.str() );
}
success = prog->addShaderFromSourceCode( QGLShader::Fragment,
loadSource( "fragment.glsl" ).c_str() );
if ( ! success )
{
ErrorOStream msg;
msg << "Error trying to load fragment shader into a shader program.\n"
<< prog->log().toStdString();
throw ERRORLOG( msg.str() );
}
success = prog->link();
if ( ! success )
{
ErrorOStream msg;
msg << "Error trying to link shaders into a shader program.\n"
<< prog->log().toStdString();
throw ERRORLOG( msg.str() );
}
prog->bind();
// Create the buffer for vertex position.
QGLBuffer* vBuf = new QGLBuffer( QGLBuffer::VertexBuffer );
vBuf->create();
vBuf->setUsagePattern( QGLBuffer::DynamicDraw );
vBuf->bind();
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, 0.0f, 0.1f, 1.0f,
-1.0f, 1.0f, 0.1f, 1.0f,
1.0f, 1.0f, 0.1f, 1.0f,
1.0f, 0.0f, 0.1f, 1.0f };
vBuf->allocate( vertices, sizeof( vertices ) );
prog->setAttributeBuffer( "Vertex", GL_FLOAT, 0, 4, 0 );
prog->enableAttributeArray( "Vertex" );
// Create the buffer for Grayscale brightness value.
// Not important for final program, just for debugging during
// development.
QGLBuffer* bBuf = new QGLBuffer( QGLBuffer::VertexBuffer );
bBuf->create();
bBuf->bind();
GLfloat brightness[] = {
1.0, 0.9, 0.8, 0.7,
0.5, 0.4, 0.3, 0.2
};
bBuf->allocate( brightness, sizeof( brightness ) );
prog->setAttributeBuffer( "Brightness", GL_FLOAT, 0, 1, 0 );
prog->enableAttributeArray( "Brightness" );
// Create the buffer for polygon ID.
QGLBuffer* idBuf = new QGLBuffer( QGLBuffer::VertexBuffer );
idBuf->create();
idBuf->bind();
GLuint polyId[] = {
1, 1, 1, 1,
2, 2, 2, 2
};
idBuf->allocate( polyId, sizeof( polyId ) );
prog->setAttributeBuffer( "PolyId", GL_UNSIGNED_INT, 0, 1, 0 );
prog->enableAttributeArray( "PolyId" );
// Create the index buffer. Not trying to do any optimization
// here yet.
QGLBuffer* iBuf = new QGLBuffer( QGLBuffer::IndexBuffer );
iBuf->create();
iBuf->bind();
GLuint indices[] = {
0, 1, 2, 3, 4, 5, 6, 7
};
iBuf->setUsagePattern( QGLBuffer::StaticDraw );
iBuf->allocate( indices, sizeof( indices ) );
// Create the buffer for reading back polygon id per fragment.
QGLBuffer* fBuf = new QGLBuffer( QGLBuffer::PixelPackBuffer );
fBuf->create();
fBuf->setUsagePattern( QGLBuffer::DynamicRead );
fBuf->bind();
fBuf->allocate( 640 * 480 * sizeof( GLuint ) );
prog->setAttributeBuffer( "FragId", GL_UNSIGNED_INT, 0, 1, 0 );
prog->enableAttributeArray( "FragId" );
GLuint* fBufData = new GLuint[ 640 * 480 * sizeof( GLuint ) ];
glDrawElements( GL_QUADS, 8, GL_UNSIGNED_INT, 0 );
widget->show();
widget->updateGL();
// Trying this two different ways; neither way works.
bool readSuccess = fBuf->read( 0, fBufData, 640 * 480 * sizeof( GLuint ) );
GLuint* fBufMap =
static_cast< GLuint* >( fBuf->map( QGLBuffer::ReadOnly ) );
cout << "\n"
<< "Read Successful: " << readSuccess << endl;
cout << "Buffer map location and sample data: "
<< fBufMap << ":" << fBufMap[640] << endl;
cout << "Read data pointer: " << fBufData << endl;
cout << "Sample fragment ID: " << fBufData[ 640 * 480 / 2 ] << endl;
app->exec();
return fail;
}
Here are sample outputs for a program run:
Read Successful: true
Buffer map location and sample data: 0x5a5d9000:0
Read data pointer: 0x59e48008
Sample fragment ID: 0
That's not what I would expect. I would expect all fragment IDs to be 1, because I explicitly set FragId = uint( 1 ) in the fragment shader. Am I setting up my reads wrong? Am I doing something wrong in my binding of buffers, or enabling the names?
I would prefer to use QT code if possible, for reasons beyond the scope of this question.

There's so much Qt stuff in here that it's almost impossible to find actual OpenGL calls. But you seem to have two problems:
You're rendering to the screen. Your screen uses some kind of normalized integer image format. Which basically means "float, but takes up 8 bits". You're writing integers from your shader. These don't match. Therefore, your rendering yields undefined behavior.
What you need to do is render to an FBO that contains a GL_R8UI texture. Then your uint fragment shader output type will match your buffer. You will probably want a depth buffer too.
You never actually read the pixel data. QGLBuffer::read reads from the buffer object. But you haven't put anything into the buffer object yet. You never told OpenGL to copy the framebuffer data you rendered and store it in the buffer object. You need to do that first; after doing that, then you can read from it.
After you render to your FBO, you need to call glReadPixels. When you do that, you need to provide the correct pixel transfer parameters for what you've rendered. Namely, you need to use GL_RED_INTEGER for the format and GL_UNSIGNED_BYTE for the type. And since you're reading into a pixel buffer, you need to make sure it is bound before reading.

Related

Qt signal slot cv::Mat unable to read memory access violation

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();

How the vertex shader access the vertex buffer data bound with another shaderprogram attribute?

I have created two shader programs shaderProgram0 and shaderProgram1. I have appended all related shaders and variables with either 0 or 1 to show their relation with either shaderProgram0 or shaderProgram1.
Both shader programs work as designed. shaderProgram0 use SimpleVertexShader0.vert as a vertex shader:
#version 330
in vec3 vertexPosition0;
void main()
{
gl_Position = vec4(vertexPosition0, 1);
}
The output of shaderProgram0 is like this:
shaderProgram1 use SimpleVertexShader1.vert as a vertex shader:
#version 330
in vec3 vertexPosition1;
void main()
{
gl_Position = vec4(vertexPosition1, 1);
}
The output of shaderProgram1 is like this:
Now the fun part is this; when using shaderProgram1, I accidentally commented the binding of vertex attribute array vao1 and left the binding of vao0 uncommented which resulted in output like the following picture which is in fact the output which (I think) could be generated only by shaderProgram0!:
Code is simplified and is written using Qt Creator in Windows:
void OpenGLWidget::initializeGL()
{
initializeOpenGLFunctions();
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
shaderProgram0.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/SimpleVertexShader0.vert");
shaderProgram0.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/SimpleFragmentShader0.frag");
shaderProgram0.link();
shaderProgram1.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/SimpleVertexShader1.vert");
shaderProgram1.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/SimpleFragmentShader1.frag");
shaderProgram1.link();
}
void OpenGLWidget::resizeGL(int w, int h)
{
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
}
void OpenGLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GLfloat vertexBufferData0[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
};
GLuint vbo0;
glGenBuffers(1, &vbo0);
glBindBuffer(GL_ARRAY_BUFFER, vbo0);
glBufferData(GL_ARRAY_BUFFER,
sizeof(vertexBufferData0),
vertexBufferData0,
GL_STATIC_DRAW);
GLuint vao0;
glGenVertexArrays(1, &vao0);
glBindVertexArray(vao0);
glBindBuffer(GL_ARRAY_BUFFER, vbo0);
glVertexAttribPointer(glGetAttribLocation(shaderProgram0.programId(),"vertexPosition0"), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
GLfloat vertexBufferData1[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
};
GLuint vbo1;
glGenBuffers(1, &vbo1);
glBindBuffer(GL_ARRAY_BUFFER, vbo1);
glBufferData(GL_ARRAY_BUFFER,
sizeof(vertexBufferData1),
vertexBufferData1,
GL_STATIC_DRAW);
GLuint vao1;
glGenVertexArrays(1, &vao1);
glBindVertexArray(vao1);
glBindBuffer(GL_ARRAY_BUFFER, vbo1);
glVertexAttribPointer(glGetAttribLocation(shaderProgram1.programId(),"vertexPosition1"), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
// Now Rendering-----------------------------------------------------
glBindVertexArray(vao0);
glEnableVertexAttribArray(glGetAttribLocation(shaderProgram0.programId(),"vertexPosition0"));
// glBindVertexArray(vao1);
// glEnableVertexAttribArray(glGetAttribLocation(shaderProgram1.programId(),"vertexPosition1"));
shaderProgram1.bind();
glDrawArrays(GL_TRIANGLES, 0, 3);
}
Isn't it strange that the vertex shader of shaderProgram1 access the buffer data which is bound with shaderProgram0 attribute? I thought it should not have generated any output as the valid vertex attribute array is not enabled!
Please explain this scenario if somebody knows how this works. If you don't understand what i am asking then please look at the code thoroughly you will get the point or i will explain further.
EDIT:
// Now Rendering-----------------------------------------------------
glBindVertexArray(vao0);
glEnableVertexAttribArray(glGetAttribLocation(shaderProgram0.programId(),"vertexPosition0"));
shaderProgram0.bind();
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(vao1);
glEnableVertexAttribArray(glGetAttribLocation(shaderProgram1.programId(),"vertexPosition1"));
shaderProgram1.bind();
glDrawArrays(GL_TRIANGLES, 0, 3);
Output of the edited code is:
Here a question arise if both programs are using the same location for the only attribute then they should either generate one or the other triangle not both due overwriting!?
Bear with me please, i have just started learning it.
Isn't it strange that the vertex shader of shaderProgram1 access the buffer data which is bound with shaderProgram0 attribute?
No.
If you are not explicitly specifying attribute locations from your shader, or using glBindAttribLocation before linking the program, then the implementation will arbitrarily assign vertex attribute locations for you. There is no requirement that separate programs use separate attribute locations. Indeed, it's generally advised that you try to make your attribute location interfaces compatible between programs where possible.
In your case, the implementation happened to assign them both to the same location. So either VAO will work with either program.

Qt5 OpenGL Texture Sampling

I'm trying to render a QImage using OpenGL wrapper classes of Qt5 and shader programs. I have the following shaders and a 3.3 core context. I'm also using a VAO for the attributes. However, I keep getting a blank red frame (red is the background clear color that I set). I'm not sure if it is a problem with the MVP matrices or something else. Using a fragment shader which sets the output color to a certain fixed color (black) still resulted in a red frame. I'm totally lost here.
EDIT-1: I also noticed that attempting to get the location of texRGB uniform from the QOpenGLShaderProgram results in -1. But I'm not sure if that has anything to do with the problem I'm having. Uniforms defined in the vertex shader for the MVP matrices have the locations 0 and 1.
Vertex Shader
#version 330
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec2 inTexCoord;
out vec2 vTexCoord;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
void main(void)
{
gl_Position = projectionMatrix * modelViewMatrix * vec4(inPosition, 1.0);
// pass the input texture coordinates to fragment shader
vTexCoord = inTexCoord;
}
Fragment Shader
#version 330
uniform sampler2DRect texRGB;
in vec2 vTexCoord;
out vec4 fColor;
void main(void)
{
vec3 rgb = texture2DRect(texRGB, vTexCoord.st).rgb;
fColor = vec4(rgb, 0.0);
}
OGLWindow.h
#include <QOpenGLWindow>
#include <QOpenGLFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLTexture>
#include <QDebug>
#include <QString>
class OGLWindow : public QOpenGLWindow, protected QOpenGLFunctions
{
public:
OGLWindow();
~OGLWindow();
// OpenGL Events
void initializeGL();
void resizeGL(int width, int height);
void paintGL();
// a method for cleanup
void teardownGL();
private:
bool isInitialized;
// OpenGL state information
QOpenGLBuffer m_vbo_position;
QOpenGLBuffer m_vbo_index;
QOpenGLBuffer m_vbo_tex_coord;
QOpenGLVertexArrayObject m_object;
QOpenGLShaderProgram* m_program;
QImage m_image;
QOpenGLTexture* m_texture;
QMatrix4x4 m_projection_matrix;
QMatrix4x4 m_model_view_matrix;
};
OGLWindow.cpp
#include "OGLWindow.h"
// vertex data
static const QVector3D vertextData[] = {
QVector3D(-1.0f, -1.0f, 0.0f),
QVector3D( 1.0f, -1.0f, 0.0f),
QVector3D( 1.0f, 1.0f, 0.0f),
QVector3D(-1.0f, 1.0f, 0.0f)
};
// indices
static const GLushort indices[] = {
0, 1, 2,
0, 2, 3
};
OGLWindow::OGLWindow() :
m_vbo_position (QOpenGLBuffer::VertexBuffer),
m_vbo_tex_coord (QOpenGLBuffer::VertexBuffer),
m_vbo_index (QOpenGLBuffer::IndexBuffer),
m_program (nullptr),
m_texture (nullptr),
isInitialized (false)
{
}
OGLWindow::~OGLWindow()
{
makeCurrent();
teardownGL();
}
void OGLWindow::initializeGL()
{
qDebug() << "initializeGL()";
initializeOpenGLFunctions();
isInitialized = true;
QColor backgroundColor(Qt::red);
glClearColor(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF(), 1.0f);
// load texture image
m_image = QImage(":/images/cube.png");
m_texture = new QOpenGLTexture(QOpenGLTexture::TargetRectangle);
// set bilinear filtering mode for texture magnification and minification
m_texture->setMinificationFilter(QOpenGLTexture::Nearest);
m_texture->setMagnificationFilter(QOpenGLTexture::Nearest);
// set the wrap mode
m_texture->setWrapMode(QOpenGLTexture::ClampToEdge);
m_texture->setData(m_image.mirrored(), QOpenGLTexture::MipMapGeneration::DontGenerateMipMaps);
int imgWidth = m_image.width();
int imgHeight = m_image.height();
m_projection_matrix.setToIdentity();
m_projection_matrix.ortho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
// m_projection_matrix.ortho(0.0, (float) width(), (float) height(), 0.0f, -1.0f, 1.0f);
m_model_view_matrix.setToIdentity();
glViewport(0, 0, width(), height());
m_program = new QOpenGLShaderProgram();
m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vshader.glsl");
m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fshader.glsl");
m_program->link();
m_program->bind();
// texture coordinates
static const QVector2D textureData[] = {
QVector2D(0.0f, 0.0f),
QVector2D((float) imgWidth, 0.0f),
QVector2D((float) imgWidth, (float) imgHeight),
QVector2D(0.0f, (float) imgHeight)
};
// create Vertex Array Object (VAO)
m_object.create();
m_object.bind();
// create position VBO
m_vbo_position.create();
m_vbo_position.bind();
m_vbo_position.setUsagePattern(QOpenGLBuffer::StaticDraw);
m_vbo_position.allocate(vertextData, 4 * sizeof(QVector3D));
// create texture coordinates VBO
m_vbo_tex_coord.create();
m_vbo_tex_coord.bind();
m_vbo_tex_coord.setUsagePattern(QOpenGLBuffer::StaticDraw);
m_vbo_tex_coord.allocate(textureData, 4 * sizeof(QVector2D));
// create the index buffer
m_vbo_index.create();
m_vbo_index.bind();
m_vbo_index.setUsagePattern(QOpenGLBuffer::StaticDraw);
m_vbo_index.allocate(indices, 6 * sizeof(GLushort));
// enable the two attributes that we have and set their buffers
m_program->enableAttributeArray(0);
m_program->enableAttributeArray(1);
m_program->setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(QVector3D));
m_program->setAttributeBuffer(1, GL_FLOAT, 0, 2, sizeof(QVector2D));
// Set modelview-projection matrix
m_program->setUniformValue("projectionMatrix", m_projection_matrix);
m_program->setUniformValue("modelViewMatrix", m_model_view_matrix);
// use texture unit 0 which contains our frame
m_program->setUniformValue("texRGB", 0);
// release (unbind) all
m_object.release();
m_vbo_position.release();
m_vbo_tex_coord.release();
m_vbo_index.release();
m_program->release();
}
void OGLWindow::resizeGL(int width, int height)
{
qDebug() << "resizeGL(): width =" << width << ", height=" << height;
if (isInitialized) {
// avoid division by zero
if (height == 0) {
height = 1;
}
m_projection_matrix.setToIdentity();
m_projection_matrix.perspective(60.0, (float) width / (float) height, -1, 1);
glViewport(0, 0, width, height);
}
}
void OGLWindow::paintGL()
{
// clear
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// render using our shader
m_program->bind();
{
m_texture->bind();
m_object.bind();
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0) );
m_object.release();
}
m_program->release();
}
void OGLWindow::teardownGL()
{
// actually destroy our OpenGL information
m_object.destroy();
m_vbo_position.destroy();
m_vbo_color.destroy();
delete m_program;
}
EDIT-2: I'm creating the context as follows:
QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setVersion(3,3);
This line in your fragment shader code is invalid:
vec3 rgb = texture2DRect(texRGB, vTexCoord.st).rgb;
texture2DRect() is not a built-in function.
Since you're using the GLSL 3.30 core profile (core is the default for the version unless compatibility is specified), you should be using the overloaded texture() function, which replaces the older type specific functions like texture2D() in the core profile.
Functions like texture2D() are still supported in GLSL 3.30 core unless a forward compatible core profile context is used. So depending on how the context is created, you can still use those functions.
However, sampler2DRect was only added as a sampler type in GLSL 1.40 as part of adding rectangular textures to the standard in OpenGL 3.1. At the time, the legacy sampling functions were already marked as deprecated, and only the new texture() function was defined for rectangular textures. This means that texture2DRect() does not exist in any GLSL version.
The correct call is:
vec3 rgb = texture(texRGB, vTexCoord.st).rgb;
Another part of your code that can prevent it from rendering anything is this projection matrix:
m_projection_matrix.perspective(60.0, (float) width / (float) height, -1, 1);
The near and far planes for a standard projection matrix both need to be positive. This call will set up a projection transformation with a "camera" on the origin, looking down the negative z-axis. The near and far values are distances from the origin. A valid call could look like this:
m_projection_matrix.perspective(60.0, (float) width / (float) height, 1.0f, 10.0f);
You will then also need to set the model matrix to transform the coordinates of the object into this range on the negative z-axis. You could for example apply a translation by (0.0f, 0.0f, -5.0f).
Or, if you just want to see something, the quad should also become visible if you simply use the identity matrix for the projection.

OpenGL Texture Mapping memory leak

I am writing a video content analysis application which analyses recorded and live videos.
I use opengl to display the videos on a qt interface (using qglwidgets). I am using texture mapping with picture buffer objects if the graphics card supports it(here's the reference: http://www.songho.ca/opengl/gl_pbo.html )to display the video(loaded from OpenCV's IPLImage).
The problem is that, the memory for the application keeps on increasing over time. Approx. 4-8KB per second. I am using the task manager to verify this.
I have narrowed down the issue with the rendering of the video because I saw a lot of posts about textures not being freed which leads to memory usage but I haven't been able to find a solution for my problem.
I am only using glGenTextures in initializeGL() so the texture is being generated only once and reused.
Here's the code wherein the problem lies:
void paintGL(){
static int index = 0;
int nextIndex = 0; // pbo index used for next frame
if(paintFlag){
if(pboMode > 0) {
// "index" is used to copy pixels from a PBO to a texture object "nextIndex" is used to update pixels in a PBO
if(pboMode == 1){
// In single PBO mode, the index and nextIndex are set to 0
index = nextIndex = 0;
}
else if(pboMode == 2)
{
// In dual PBO mode, increment current index first then get the next index
index = (index + 1) % 2;
nextIndex = (index + 1) % 2;
}
// start to copy from PBO to texture object ///////
// bind the texture and PBO
glBindTexture(GL_TEXTURE_2D, texture);
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[index]);
// copy pixels from PBO to texture object
// Use offset instead of ponter.
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, WIDTH, HEIGHT, GL_BGR, GL_UNSIGNED_BYTE, 0);
// measure the time copying data from PBO to texture object
//t1.stop();
//copyTime = t1.getElapsedTimeInMilliSec();
///////////////////////////////////////////////////
// start to modify pixel values ///////////////////
// t1.start();
// bind PBO to update pixel values
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[nextIndex]);
// map the buffer object into client's memory
// Note that glMapBufferARB() causes sync issue.
// If GPU is working with this buffer, glMapBufferARB() will wait(stall)
// for GPU to finish its job. To avoid waiting (stall), you can call
// first glBufferDataARB() with NULL pointer before glMapBufferARB().
// If you do that, the previous data in PBO will be discarded and
// glMapBufferARB() returns a new allocated pointer immediately
// even if GPU is still working with the previous data.
glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_DRAW_ARB);
GLubyte* ptr = (GLubyte*)glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY_ARB);
if(ptr)
{
// update data directly on the mapped buffer
//updatePixels(ptr, DATA_SIZE);
memcpy(ptr,original->imageData,DATA_SIZE);
glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB); // release pointer to mapping buffer
}
// measure the time modifying the mapped buffer
//t1.stop();
//updateTime = t1.getElapsedTimeInMilliSec();
///////////////////////////////////////////////////
// it is good idea to release PBOs with ID 0 after use.
// Once bound with 0, all pixel operations behave normal ways.
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
}
else
{
///////////////////////////////////////////////////
// start to copy pixels from system memory to textrure object
//t1.start();
glBindTexture(GL_TEXTURE_2D, texture);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, WIDTH, HEIGHT, GL_BGR, GL_UNSIGNED_BYTE, (GLvoid*)original->imageData);
//t1.stop();
//copyTime = t1.getElapsedTimeInMilliSec();
}
paintFlag=false;
}
// clear buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glBegin(GL_QUADS);
glTexCoord2i(0,1); glVertex2i(0,HEIGHT);
glTexCoord2i(0,0); glVertex2i(0,0);
glTexCoord2i(1,0); glVertex2i(WIDTH,0);
glTexCoord2i(1,1); glVertex2i(WIDTH,HEIGHT);
glEnd();
glFlush();
glBindTexture(GL_TEXTURE_2D, 0);
swapBuffers();
glDeleteBuffers(1,&texture);
updateGL();
}
The code is pretty much the same as in the tutorial. However, my texture data comes from an IplImage structure which is continuously updated by a separate thread. I am also using boost's lock_guard for synchronization purposes.
Is there anything wrong that I am doing here?
EDIT: I am adding the remaining code:
//Constructor, this is where all the allocation happens
const int DATA_SIZE = WIDTH * HEIGHT * 3;
QGLCanvas::QGLCanvas(QWidget* parent,QString caption)
: QGLWidget(parent)
{
imageFormat=QImage::Format_RGB888;
this->name=caption;
original=cvCreateImage(cvSize(WIDTH,HEIGHT),IPL_DEPTH_8U,3);
if(this->name=="Background")
bgFrameBackup=cvCreateImage(cvSize(WIDTH,HEIGHT),IPL_DEPTH_8U,3);
cvZero(original);
//cvShowImage("w",original);
//cvWaitKey(0);
switch(original->nChannels) {
case 1:
format = GL_LUMINANCE;
break;
case 2:
format = GL_LUMINANCE_ALPHA;
break;
case 3:
format = GL_BGR;
break;
default:
return;
}
drawing=false;
setMouseTracking(true);
mouseX=0;mouseY=0;
startX=0; endX=0;
startY=0; endY=0;
dialog=new EntryExitRuleDialog();
makeCurrent();
GLenum result=glewInit();
if(result){
qDebug()<<(const char*)(glewGetErrorString(result));
}
//qDebug()<<"Open GL Version: "<<(const char*)glGetString(GL_VERSION);
bgColor=QColor::fromRgb(100,100,100);
initializeGL();
qglClearColor(bgColor);
glInfo glInfo;
glInfo.getInfo();
#ifdef _WIN32
// check PBO is supported by your video card
if(glInfo.isExtensionSupported("GL_ARB_pixel_buffer_object"))
{
// get pointers to GL functions
glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress("glGenBuffersARB");
glBindBufferARB = (PFNGLBINDBUFFERARBPROC)wglGetProcAddress("glBindBufferARB");
glBufferDataARB = (PFNGLBUFFERDATAARBPROC)wglGetProcAddress("glBufferDataARB");
glBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC)wglGetProcAddress("glBufferSubDataARB");
glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)wglGetProcAddress("glDeleteBuffersARB");
glGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC)wglGetProcAddress("glGetBufferParameterivARB");
glMapBufferARB = (PFNGLMAPBUFFERARBPROC)wglGetProcAddress("glMapBufferARB");
glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)wglGetProcAddress("glUnmapBufferARB");
// check once again PBO extension
if(glGenBuffersARB && glBindBufferARB && glBufferDataARB && glBufferSubDataARB &&
glMapBufferARB && glUnmapBufferARB && glDeleteBuffersARB && glGetBufferParameterivARB)
{
pboSupported = true;
cout << "Video card supports GL_ARB_pixel_buffer_object." << endl;
}
else
{
pboSupported = false;
cout << "Video card does NOT support GL_ARB_pixel_buffer_object." << endl;
}
}
#else // for linux, do not need to get function pointers, it is up-to-date
if(glInfo.isExtensionSupported("GL_ARB_pixel_buffer_object"))
{
pboSupported = pboUsed = true;
cout << "Video card supports GL_ARB_pixel_buffer_object." << endl;
}
else
{
pboSupported = pboUsed = false;
cout << "Video card does NOT support GL_ARB_pixel_buffer_object." << endl;
}
#endif
if(pboSupported){
glGenBuffersARB(2, pboIds);
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[0]);
glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_DRAW_ARB);
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[1]);
glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_DRAW_ARB);
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
//Note: pboMode=2 somehow does not work while calibration. Fix this later.
pboMode=1;
}
else{
pboMode=0;
}
paintFlag=false;
}
void QGLCanvas::setImage(IplImage image){
if(QString(this->name)=="Background"){
cvCopyImage(&image,bgFrameBackup);
}
//cvShowImage(name,&image);
// Display a rectangle between startX ,startY and endX,endY if we are in calibration mode
//and drawing flag is set.(typically, by a mouse click)
if(QString(this->name)=="Calibrate" && calibrating ){
if(drawing)
cvRectangle(&image,cvPoint(startX,startY),cvPoint(endX,endY),cvScalarAll(0xee));
if(select_object) //During calibration
cvRectangle(&image,cvPoint(selection.x,selection.y),cvPoint(selection.x+selection.width,selection.y+selection.height),cvScalarAll(0xee));
//Draw existing calibration rectangles
for (list<CvRect>::iterator it=calibration_rect_list->begin(); it!=calibration_rect_list->end(); ++it)
{
cvRectangle(&image, cvPoint((*it).x, (*it).y), cvPoint((*it).x + (*it).width, (*it).y + (*it).height), CV_RGB(100,255,0), 2, 8, 0);
}
}
//Only draw on the video widget with the name "Final"
if(QString(this->name)=="Final")
{
if(calibrating && drawing)
cvRectangle(&image,cvPoint(startX,startY),cvPoint(endX,endY),cvScalarAll(0xee));
//If we are adding a rule, the corresponding rule shape must be drawn on the widget.
if(addingRule && drawing){
if(currentShape==RULE_SHAPE_RECT){
cvRectangle(&image,cvPoint(startX,startY),cvPoint(endX,endY),cvScalarAll(0xee));
}
else if(currentShape==RULE_SHAPE_POLY){
int linecolor=0xee;
if(points.count()>0){
//Draw polygon...
for(int i=1;i<points.count();i++){
cvLine(&image,cvPoint(points[i-1]->x(),points[i-1]->y()),cvPoint(points[i]->x(),points[i]->y()),cvScalarAll(linecolor));
}
cvLine(&image,cvPoint(startX,startY),cvPoint(endX,endY),cvScalarAll(0xee));
cvLine(&image,cvPoint(endX,endY),cvPoint(points[0]->x(),points[0]->y()),cvScalarAll(linecolor));
}
}
else if(currentShape==RULE_SHAPE_TRIPLINE){
for(int i=1;i<points.count();i++){
cvLine(&image,cvPoint(points[i-1]->x(),points[i-1]->y()),cvPoint(points[i]->x(),points[i]->y()),cvScalarAll(0xee));
}
cvLine(&image,cvPoint(startX,startY),cvPoint(endX,endY),cvScalarAll(0xee));
}
}
if(entryExitRuleCreated && currentZoneType==RULE_ZONE_TYPE_ENTRY_EXIT ){
//Highlight appropriate sides of the currentRule to mark them as Entry/Exit Zone
for(int i=0;i<currentRule->points.count();i++){
QPoint* P1=currentRule->points[i];
QPoint* P2;
//Implement cyclic nature of polygon
if(i<currentRule->points.count()-1)
P2=currentRule->points[i+1];
else P2=currentRule->points[0];
int deltax=mouseX-P1->x();
int deltax1=P2->x()-P1->x();
float m,m1;
if(deltax!=0)
m= (float)(mouseY-P1->y())/deltax;
if(deltax1!=0 && deltax!=0){
m1=(float)(P2->y()-P1->y())/deltax1;
if(round(m,1)==round(m1,1))//Mouse pointer lies on the line whose slope is same as the polygon edge
{
//Mouse pointer is on the edge of a polygon, highlight the edge
if(abs(P1->y()-P2->y()) >= abs(mouseY-P2->y()) && abs(P1->y()-P2->y()) >= abs(mouseY-P1->y())
&& abs(P1->x()-P2->x()) >= abs(mouseX-P2->x()) && abs(P1->x()-P2->x()) >= abs(mouseX-P1->x())
){
edgeHighlighted=true;
highlightedEdge[0]=P1;
highlightedEdge[1]=P2;
currentEdgeNumber=i;
break;
}
}
else{
edgeHighlighted=false;
}
}
else{
//Vertical edge of a polygon.
if(abs(mouseX-P1->x())<4) { //Same vertical line
if(abs(P1->y()-P2->y()) > abs(mouseY-P2->y()) && abs(P1->y()-P2->y()) > abs(mouseY-P1->y())){
//Current y lies between the two vertices of an edge
//Mouse pointer is on the edge of polygon,highlight the edge
//qDebug()<<"P1="<<P1->x()<<","<<P1->y()<<", P2="<<P2->x()<<","<<P2->y();
edgeHighlighted=true;
highlightedEdge[0]=P1;
highlightedEdge[1]=P2;
currentEdgeNumber=i;
break;
}
else
edgeHighlighted=false;
}
}
}
if(edgeHighlighted || edgeHighlightedFromButton){
cvLine(&image,cvPoint(highlightedEdge[0]->x(),highlightedEdge[0]->y()),cvPoint(highlightedEdge[1]->x(),highlightedEdge[1]->y()),cvScalar(0xff,0x00,0x00),3);
}
}
}
{
//qDebug()<<name<<":Saving original image";
ExclusiveLock xlock(globalXMutex);
this->original=ℑ
paintFlag=true;
}
updateGL();
/*if(this->name=="Final"){
cvShowImage("Final",original);
cvWaitKey(1);
}*/
}
//Texture is generated here
void QGLCanvas::initializeGL(){
glDisable(GL_LIGHTING);
glEnable(GL_TEXTURE_2D);
glClearColor(0, 0, 0, 0); // background color
glClearStencil(0); // clear stencil buffer
glClearDepth(1.0f); // 0 is near, 1 is far
glDepthFunc(GL_LEQUAL);
glEnable(GL_TEXTURE_2D);
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);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glBindTexture(GL_TEXTURE_2D,texture);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,WIDTH,HEIGHT,0,GL_BGR,GL_UNSIGNED_BYTE,NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glClearStencil(0); // clear stencil buffer
glClearDepth(1.0f); // 0 is near, 1 is far
glDepthFunc(GL_LEQUAL);
setAutoBufferSwap(false);
}
void QGLCanvas::resizeGL(int width,int height){
if (height==0) // Prevent A Divide By Zero By
{
height=1; // Making Height Equal One
}
glViewport(0,0,WIDTH,HEIGHT); // Reset The Current Viewport
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
glOrtho(0.0f,WIDTH,HEIGHT,0.0f,0.0f,1.0f);
glEnable(GL_TEXTURE_2D);
glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glLoadIdentity(); // Reset The Modelview Matrix
}
You're calling glDeleteBuffers() on a texture object (should be buffer object), or rather, should not be here at all I think. Like other GL objects, only glDelete() once for every glGen() call.
You're calling glFlush() and swapBuffers(), I believe Qt takes care of that for you.
The OpenGL driver could have a memory leak. Try it without PBO.
Try glGetError() after each GL call to see if you've made a mistake elsewhere.

Using QImage with OpenGL

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

Resources