How to get framebuffer image and save it to texture in Qt? - qt

I am trying to get front-face and back-face of cube and save them to texture for calculating difference of them.
This is what I want to get.
Vertices of cube is passed to pipeline for getting front-face of cube and save it to texture
Vertices of cube is passed to pipeline for getting back-face of cube and save it to texture
Using texture of both front-face and back-face of cube, calculate the difference of them
I have paintGL() like this,
void GLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glShadeModel(GL_SMOOTH);
QMatrix4x4 mMatrix;
QMatrix4x4 vMatrix;
QMatrix4x4 cameraTransformation;
cameraTransformation.rotate(alpha, 0, 1, 0);
cameraTransformation.rotate(beta, 1, 0, 0);
QVector3D cameraPosition = cameraTransformation * QVector3D(0, 0, distance);
QVector3D cameraUpDirection = cameraTransformation * QVector3D(0, 1, 0);
QVector3D eye = QVector3D(0, 0, 0);
vMatrix.lookAt(cameraPosition, eye, cameraUpDirection);
shaderProgram.bind();
shaderProgram.setUniformValue("mvpMatrix", pMatrix*vMatrix*mMatrix);
//vertex array enable
shaderProgram.setAttributeArray("vertex", vertices.constData());
shaderProgram.enableAttributeArray("vertex");
//color array enable
shaderProgram.setAttributeArray("color", colors.constData());
shaderProgram.enableAttributeArray("color");
/ /glDisable(GL_DEPTH_TEST);
glDrawArrays(GL_QUADS, 0, vertices.size());
//vertex&color array disable
shaderProgram.disableAttributeArray("vertex");
shaderProgram.disableAttributeArray("color");
shaderProgram.release();
}

Related

Combining Vertex Array Object with Vertex Buffer & Index Buffer

I am trying to combine a vertex array object with a vertex buffer & an index buffer using Qt.
The following code combines a VAO and a VBO, working perfectly:
// Create buffers
void Tile::setupBuffers() {
vbo.create();
vbo.bind();
vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
vbo.allocate(vertices.constData(), vertices.size() * sizeof(QVector3D));
vbo.release();
vao.create();
shaderProgram->bind();
vao.bind();
vbo.bind();
shaderProgram->enableAttributeArray(0);
shaderProgram->setAttributeBuffer(positionLocation, GL_FLOAT, 0, 3, 0);
vao.release();
shaderProgram->disableAttributeArray(0);
vbo.release();
shaderProgram->release();
}
// Draw triangle
void Tile::draw() {
shaderProgram->bind();
vao.bind();
glDrawArrays(GL_TRIANGLES, 0, 3);
vao.release();
shaderProgram->release();
}
When I'm now trying to add an index buffer nothing gets drawn except for the clear color. Here's the code:
// Create buffers
void Tile::setupBuffers() {
vbo.create();
vbo.bind();
vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
vbo.allocate(vertices.constData(), vertices.size() * sizeof(QVector3D));
vbo.release();
ibo.create();
ibo.bind();
ibo.setUsagePattern(QOpenGLBuffer::StaticDraw);
ibo.allocate(indices.constData(), indices.size() * sizeof(GLushort));
ibo.release();
vao.create();
shaderProgram->bind();
vao.bind();
vbo.bind();
shaderProgram->enableAttributeArray(0);
shaderProgram->setAttributeBuffer(positionLocation, GL_FLOAT, 0, 3, 0);
ibo.bind();
vao.release();
shaderProgram->disableAttributeArray(0);
vbo.release();
ibo.release();
shaderProgram->release();
}
void Tile::draw() {
shaderProgram->bind();
vao.bind();
glDrawElements(GL_TRIANGLES, 3, GL_FLOAT, 0);
vao.release();
shaderProgram->release();
}
Here's the content of my vertex vector & index vector:
vertices = {
QVector3D(1.0f, -1.0f, 0.0f),
QVector3D(1.0f, 1.0f, 0.0f),
QVector3D(-1.0f, -1.0f, 0.0f)
};
indices = {
0, 1, 2
};
I assume the order of binding the VAO/VBO/IBO in the second code block is wrong, I just don't know how it's done right. Any help is appreciated.
glDrawElements(GL_TRIANGLES, 3, GL_FLOAT, 0);
Your indices are GLushorts, not floats. You should have gotten a GL_INVALID_ENUM error; you should always check for GL errors.
Also, shaderProgram->disableAttributeArray(0); is completely unnecessary. The enable/disable state is part of the VAO (Qt's idiotic wrapper to the contrary). Disabling the attribute won't change the VAO, since you unbound it. And you don't want to disable it anyway.
Lastly, there's this:
shaderProgram->enableAttributeArray(0);
shaderProgram->setAttributeBuffer(positionLocation, GL_FLOAT, 0, 3, 0);
The first parameter of both of these functions should be the same. They probably are, so this is more of a benign bug. But it's still wrong.

Displaying heatmap with OpenGL using shaders

I am trying to display a heatmap with OpenGL using shaders.
Here is my vertex shader:
# version 130
void main (void)
{
vec4 vertex = gl_Vertex;
gl_Position = gl_ModelViewProjectionMatrix * vertex;
gl_TexCoord[0] = gl_MultiTexCoord0;
}
And here is my fragment shader:
# version 130
uniform sampler2D heatmap;
uniform sampler1D colormap;
void main (void)
{
float temp = texture2D(heatmap, gl_TexCoord[1].st).r; // [0 - 50] degrees celcius
float r = temp/50.0f;
r = clamp(r, 0.0f, 1.0f);
gl_FragColor = texture1D(colormap, r);
}
Here is the code I call once to send the textures to GPU memory:
glGenTextures(2, textures);
GLenum errc = glGetError();
if (errc != GL_NO_ERROR)
{
const char* errmsg = (const char*)gluErrorString(errc);
std::cerr << errmsg;
}
...
glEnable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_1D);
glBindTexture(GL_TEXTURE_2D, textures[0]); // makes the texture with id texture active
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, 100, 100, 0, GL_RED, GL_FLOAT, &data[0]);
glBindTexture(GL_TEXTURE_1D, textures[1]); // makes the texture with id texture active
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage1D(GL_TEXTURE_1D, 0, 3, 256, 0, GL_RGB, GL_FLOAT, &rgb[0]);
Here data is a std::vector of 100x100 floats and rgb is a std::vector of 3x256 floats.
Here is my drawing code:
glBegin(GL_QUADS); // Draw A Quad
glTexCoord2f(0.0, 1.0);
glVertex3f(0.0, 1.0, 0.0);
glTexCoord2f(1.0, 1.0);
glVertex3f(1.0, 1.0, 0.0)
glTexCoord2f(1.0, 0.0);
glVertex3f(1.0, 0.0, 0.0);
glTexCoord2f(0.0, 0.0);
glVertex3f(0.0, 0.0, 0.0);
glEnd();
Do I need to call glTexCoord1f() for each vertex? These values are not used.
I am using Qt and QGLWidget in particular.
I am not seing anything. What could be wrong?
Some observations:
If instead set gl_FragColor = texture2D(heatmap, gl_TexCoord[1].st); inside the fragment shader I see the red component correctly.
In the code above glGenTextures fails, but I still can see the red component as described above.
If I move this call to just before glBindTexture it does not fail, but then I do not see anything!?

How to set the colour of an alpha blended GL_TEXTURE_2D in a QGLWidget

I'm a newbie with blending and textures in Opengl. My render_text() method uses the drawText method in QPainter with a QImage as its device. The QImage becomes a GL_TEXTURE_2D and then is attached to a GL_QUADS. The text appears correctly over the 2d scene as black. I would like to know how to arbitrarily set the colour. If anyone can recommend a good tutorial on how the source to destination blending works would also benefit.
void GLView::render_text(char *txt, quint8 height)
{
QImage image;
qint32 font_height, width;
font_height = height * VIEW_SIZE / (ZOOM * OBJ_HEIGHT);
QFont font("Helvetica", font_height);
QFontMetrics fm(font);
// render text in QImage
QImage img(fm.width(txt), fm.height(), QImage::Format_ARGB32);
img.fill(0);
QPainter pixPaint(&img);
pixPaint.setFont(font);
pixPaint.drawText(0, font_height, txt);
// push to gl
image = QGLWidget::convertToGLFormat(img);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//glBlendColor(1, 0, 0, 0.5);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
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);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image.width(), image.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.bits() );
width = image.width() * ZOOM * OBJ_WIDTH / VIEW_SIZE; //calc object width from image width
glBegin(GL_QUADS);
glTexCoord2i(0,0); glVertex2f(0, 0);
glTexCoord2i(0,1); glVertex2f(0, height);
glTexCoord2i(1,1); glVertex2f(width, height);
glTexCoord2i(1,0); glVertex2f(width, 0);
glEnd();
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glDeleteTextures(1, &texture);
}
You can paint in the right color in the first place -- set the color on the QPainter object / QPen!
May I suggest you use QGLFrameBufferObject? You can draw into it with the QPainter and then draw it as texture with a Qt call within your GL code. Advantages:
Qt will use OpenGL for rendering the text
No copy to the GPU needed.
=> Much faster!

How to make original (0,0) coordinate to be on the top left of QGLWIdget?

I want to display an image using QGLWidget, it doesn't show in the correct way, one of the problems is the original coordinate is on the bottom left of the widget.
I would like to know how to make the original coordinate to be on the top left and flip the y axis.
here's my code:
header
#ifndef _GLImageDisplay_H_
#define _GLImageDisplay_H_
#include "stdafx.h"
class GLImageDisplay : public QGLWidget
{
Q_OBJECT
public:
GLImageDisplay(QWidget *parent = 0);
void DisplayImage(QString img);
protected:
void initializeGL();
void resizeGL(int w, int h);
void paintGL();
private:
QImage svgImage;
GLubyte* gluImage;
};
#endif
cpp
#include "stdafx.h"
#include "GLImageDisplay.h"
GLImageDisplay::GLImageDisplay(QWidget *parent) : QGLWidget (parent)
{
}
void GLImageDisplay::DisplayImage(QString img)
{
svgImage.load(img);
resize(svgImage.size());
gluImage = new GLubyte[svgImage.height() * svgImage.width() * 3];
for (int a = 0; a < svgImage.width(); ++a)
{
for (int b = 0; b < svgImage.height(); ++b)
{
QColor color = svgImage.pixel(a, b);
gluImage[3 * (a + b * svgImage.width()) + 0] = (GLubyte) color.red();
gluImage[3 * (a + b * svgImage.width()) + 1] = (GLubyte) color.green();
gluImage[3 * (a + b * svgImage.width()) + 2] = (GLubyte) color.blue();
}
}
this->setMinimumWidth(svgImage.width());
this->setMinimumHeight(svgImage.height());
}
void GLImageDisplay::initializeGL()
{
glClearColor(0.5, 0.5, 0.5, 1.0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}
void GLImageDisplay::resizeGL(int w, int h)
{
glViewport(0, 0, svgImage.width(), svgImage.height());
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, svgImage.width(), 0, svgImage.height(), 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void GLImageDisplay::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT);
glRasterPos2i(0, 0);
glDrawPixels(svgImage.width(), svgImage.height(), GL_RGB, GL_UNSIGNED_BYTE, gluImage);
}
OpenGL coordinates start at the bottom. To fix this, you can do a -1 scaling in the y axis in your projection matrix. The offset is related.
To fix this, alter your call to glOrtho or apply a scaling + translation just after the call.
Btw. you could also use QPainter and use beginNativePainting() wherever you really need GL. QPainter will already use GL itself and perform very well.
glDrawPixels is a very inefficient way of drawing an image onto screen. You should rather load it into a texture and draw a quad with it. (Yet again, QPainter can do that for you, too, much more easily.)
Thanks ypnos!
in the end i flip the projection matrix gluOrtho2D(0, width, height, 0);
and flip the text coord.
here's my code:
#include "stdafx.h"
#include "GLImageDisplay.h"
GLImageDisplay::GLImageDisplay(QWidget *parent) : QGLWidget (parent)
{
}
void GLImageDisplay::DisplayImage(QString img)
{
myImage.load(img);
// calculating power-of-two (pow) size
int xpow = (int) std::pow(2.0, std::ceil( std::log10((double)myImage.width())/std::log10(2.0) ) );
int ypow = (int) std::pow(2.0, std::ceil( std::log10((double)myImage.height())/std::log10(2.0) ) );
// the texture should be square too
xpow = std::max(xpow, ypow);
ypow = xpow;
// shrink if the size is too big
if(xpow > 1024)
{
xpow = 1024;
ypow = 1024;
}
// transform the image to square pow size
scaledImage = myImage.scaled(xpow, ypow, Qt::IgnoreAspectRatio);
glImage = QGLWidget::convertToGLFormat(scaledImage);
this->setMinimumWidth(myImage.width());
this->setMinimumHeight(myImage.height());
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &imageID);
glBindTexture( GL_TEXTURE_2D, imageID );
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, scaledImage.width(), scaledImage.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, glImage.bits());
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glDisable(GL_TEXTURE_2D);
}
void GLImageDisplay::initializeGL()
{
glClearColor(0.5, 0.5, 0.5, 1.0);
}
void GLImageDisplay::resizeGL(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, width, height, 0); // flip the y axis
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void GLImageDisplay::paintGL()
{
int width = myImage.width();
int height = myImage.height();
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1,0,0);
glBegin(GL_POLYGON);
glVertex2f(10,10);
glVertex2f(10,600);
glVertex2f(300,10);
glEnd();
glEnable(GL_TEXTURE_2D);
glColor3f(1,1,1);
glBindTexture( GL_TEXTURE_2D, imageID );
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glImage.width(), glImage.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, glImage.bits());
glBegin(GL_QUADS);
// text coord is flipped
glTexCoord2d(0,1); glVertex3d(0, 0, 0);
glTexCoord2d(1,1); glVertex3d(width, 0, 0);
glTexCoord2d(1,0); glVertex3d(width, height, 0);
glTexCoord2d(0,0); glVertex3d(0, height, 0);
glEnd();
glDisable(GL_TEXTURE_2D);
}
but i'm encountering another issue when i try to display a large image where the bottom of the widget is cut and displays black area. It seems that the widget only is rendered at most the height of my LCD screen (?) I'm wondering QGLWidget can't be easily put in the QScrollArea (?). It's a different issue though.

How can I draw my opengl background keeping good performance?

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

Resources