Do offscreen render(openGL) with Qt5
-
Using openGL to do some image processing, the first experiment is
convert the color image to gray, everything are fine, but I don't want
to show the widget.If I don't call "show()" the QGLWidget would not begin to render the texture
Could I render the texture without showing the widget?
Is QGLWidget a right tool to do that?part of the codes
@
#include <QDebug>#include "toGray.hpp"
toGray::toGray(std::string const &vertex_file, std::string const &fragment_file, QWidget *parent)
:basicGLWidget(vertex_file, fragment_file, parent) //read shaders, compile them, allocate VBO
{
}void toGray::initializeVertexBuffer()
{
std::vector<GLfloat> const vertex{
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 1.0f,
};initializeVertexBufferImpl(vertex); //copy the data into QOpenGLBuffer QImage img(":/simpleGPGPU/images/emili.jpg"); texture_addr_ = bindTexture(img); resize(img.width(), img.height()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
void toGray::paintGL()
{
qglClearColor(Qt::white);
glClear(GL_COLOR_BUFFER_BIT);program_.bind(); bind_buffer(); program_.enableAttributeArray("qt_Vertex"); program_.setAttributeBuffer( "qt_Vertex", GL_FLOAT, 0, 4); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture_addr_); glDrawArrays(GL_TRIANGLES, 0, get_buffer(0).size()); program_.disableAttributeArray("qt_Vertex"); program_.release(); glActiveTexture(0); release_buffer();
}
@
vertex shader
@
attribute highp vec4 qt_Vertex;
varying highp vec2 qt_TexCoord0;void main(void)
{
gl_Position = qt_Vertex;
qt_TexCoord0 = (qt_Vertex.xy + vec2(1.0)) * 0.5;
}@
fragment shader
@
uniform sampler2D source;
varying highp vec2 qt_TexCoord0;vec3 toGray(vec3 rgb)
{
return vec3(rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114);
}void main(void)
{
vec3 gray = toGray(texture2D(source, qt_TexCoord0).rgb);
gl_FragColor = vec4(gray, 0.0);
}
@ -
You have to work directly on OpenGL context.
https://www.opengl.org/wiki/Creating_an_OpenGL_Context_(WGL)
https://qt-project.org/doc/qt-4.8/qglcontext.htmlDo render frame you have to call "makeCurrent()":https://qt-project.org/doc/qt-4.8/qglcontext.html#makeCurrent at the beginning and "doneCurrent()":https://qt-project.org/doc/qt-4.8/qglcontext.html#doneCurrent in the end.
Hope i'm right.
-
Thanks, could you introduce me some examples?
I want to process an image by openGL and save the image after process
without showing the widget.For me, the threshold of openGL is pretty high, the rules are complicated
-
Sorry that I could not give you straight example of that but there are many tutorials about rendering frame to texture on the web:
http://lmgtfy.com/?q=opengl+render+to+texture
One good and easy example:
http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-14-render-to-texture/
To get texture pixels back from GPU to RAM (so that you can create QImage) you have to use "glReadPixels":https://www.opengl.org/sdk/docs/man/xhtml/glReadPixels.xml (read pixels from currently bound frame buffer) or "glGetTexImage":https://www.opengl.org/sdk/docs/man/xhtml/glGetTexImage.xml (to read pixels from currently bound texture). Don't be surprised when obtained image is flipped upside down (you have to fix it manually ). Pixels might have mismatched channels, QImage does not support GL_RGBA (maybe combination of GL_RGB and QImage::Format_RGB888 could work).
bq. For me, the threshold of openGL is pretty high, the rules are complicated
Probably not. Only conversion from texture to QImage might course a little trouble. I strongly recommend you to use scanLine instead of setPixel.
Rules are sometimes complicated yes, you sometimes cannot read pixels from GPU because you graphics card does not support given format (or do software processing instead). -
Using QWindow to do some offscreen rendering, but the function
"initializeOpenGLFunctions();" always make the program crash.hpp
@
#ifndef OFFSCREENEXP_HPP
#define OFFSCREENEXP_HPP#include <QOpenGLFunctions>
#include <QWindow>class QOpenGLContext;
class offScreenExp : public QWindow, protected QOpenGLFunctions
{
public:
explicit offScreenExp(QWindow *parent = 0);private:
QOpenGLContext *context_;
};#endif // OFFSCREENEXP_HPP
@
.cpp
@
#include <QOpenGLContext>#include <QDebug>
#include "offScreenExp.hpp"
offScreenExp::offScreenExp(QWindow *parent) :
QWindow(parent),
context_(nullptr)
{
setSurfaceType(QWindow::OpenGLSurface);if (!context_) { context_ = new QOpenGLContext(this); context_->setFormat(requestedFormat()); if (!context_->create()) qFatal("Cannot create the requested OpenGL context!"); qDebug()<<"create context"; } context_->makeCurrent(this); qDebug()<<"initialize gl func"; initializeOpenGLFunctions(); //crash at here qDebug()<<"after initialize gl func";
}
@
-
Looks like the problem is I haven't called "create()" in my codes
Now the codes can compile, but the QImage return by the QOpenGLFrameBufferObject is empty..hpp
@
#ifndef OFFSCREENEXP_HPP
#define OFFSCREENEXP_HPP#include <QOpenGLFunctions>
#include <QWindow>class QImage;
class QOpenGLContext;class offScreenExp : public QWindow, protected QOpenGLFunctions
{
public:
explicit offScreenExp(QWindow *parent = 0);QImage render();
private:
QOpenGLContext *context_;
};#endif // OFFSCREENEXP_HPP
@
.cpp
@
#include <QImage>
#include <QOpenGLBuffer>
#include <QOpenGLContext>
#include <QOpenGLFramebufferObject>
#include <QOpenGLShaderProgram>
#include <QString>
#include <QWidget>#include <QDebug>
#include "offScreenExp.hpp"
offScreenExp::offScreenExp(QWindow *parent) :
QWindow(parent),
context_(nullptr)
{
setSurfaceType(QWindow::OpenGLSurface);
setFormat(QSurfaceFormat());
create();
}QImage offScreenExp::render()
{
//create the context
if (!context_) {
context_ = new QOpenGLContext(this);
QSurfaceFormat format;
context_->setFormat(format);if (!context_->create()) qFatal("Cannot create the requested OpenGL context!"); } context_->makeCurrent(this); initializeOpenGLFunctions(); //load image and create fbo QString const prefix("/Users/Qt/program/experiment_apps_and_libs/openGLTest/simpleGPGPU/"); QImage img(prefix + "images/emili.jpg"); if(img.isNull()){ qFatal("image is null"); } QOpenGLFramebufferObject fbo(img.size()); qDebug()<<"bind success? : "<<fbo.bind(); //if(glCheckFramebufferStatus(fbo.handle()) != GL_FRAMEBUFFER_COMPLETE){ // qDebug()<<"frame buffer error"; //} qDebug()<<"has opengl fbo : "<<QOpenGLFramebufferObject::hasOpenGLFramebufferObjects(); //use two triangles two cover whole screen std::vector<GLfloat> const vertex{ -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, }; //initialize vbo QOpenGLBuffer buffer(QOpenGLBuffer::VertexBuffer); buffer.create(); buffer.setUsagePattern(QOpenGLBuffer::StaticDraw); buffer.bind(); buffer.allocate(&vertex[0], vertex.size() * sizeof(GLfloat) ); buffer.release(); //create texture GLuint rendered_texture; glGenTextures(1, &rendered_texture); // "Bind" the newly created texture : all future texture functions will modify this texture glBindTexture(GL_TEXTURE_2D, rendered_texture); //naive solution, better encapsulate the format in a function if(img.format() == QImage::Format_Indexed8){ glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, img.width(), img.height(), 0, GL_RED, GL_UNSIGNED_BYTE, img.scanLine(0)); }else if(img.format() == QImage::Format_RGB888){ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img.width(), img.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, img.scanLine(0)); }else{ QImage temp = img.convertToFormat(QImage::Format_RGB888); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img.width(), img.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, temp.scanLine(0)); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(GL_TEXTURE_2D, 0); //compile and link program QOpenGLShaderProgram program; if(!program.addShaderFromSourceCode(QOpenGLShader::Vertex, "attribute highp vec4 qt_Vertex;" "varying highp vec2 qt_TexCoord0;" "void main(void)" "{" " gl_Position = qt_Vertex;" " qt_TexCoord0 = (qt_Vertex.xy + vec2(1.0)) * 0.5;" "}")){ qDebug()<<"QOpenGLShader::Vertex error : " + program.log(); } // Compile fragment shader if (!program.addShaderFromSourceCode(QOpenGLShader::Fragment, "uniform sampler2D source;" "varying highp vec2 qt_TexCoord0;" "vec3 toGray(vec3 rgb)" "{" " return vec3(rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114);" "}" "void main(void)" "{" "vec3 gray = toGray(texture2D(source, qt_TexCoord0).rgb);" "gl_FragColor = vec4(gray, 0.0);" "}" )){ qDebug()<<"QOpenGLShader::Fragment error : " + program.log(); } // Link shader pipeline if (!program.link()){ qDebug()<<"link error : " + program.log(); } //render the texture as usual glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, img.width(), img.height()); program.bind(); buffer.bind(); program.enableAttributeArray("qt_Vertex"); program.setAttributeBuffer( "qt_Vertex", GL_FLOAT, 0, 4); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, rendered_texture); glDrawArrays(GL_TRIANGLES, 0, buffer.size()); program.disableAttributeArray("qt_Vertex"); program.release(); glActiveTexture(0); glBindTexture(GL_TEXTURE_2D, 0); buffer.release(); return fbo.toImage();
}
@
-
remove line 67 and add doneCurrent at the end
@
glActiveTexture(0);
glBindTexture(GL_TEXTURE_2D, 0);
buffer.release();context_->doneCurrent(); return fbo.toImage();
@
Still get a blank image
-
Edit the codes of offScreenExp::render part, begin from line 132
change the order of cleaning resource@
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, img.width(), img.height());program.bind();
buffer.bind();
program.enableAttributeArray("qt_Vertex");
program.setAttributeBuffer( "qt_Vertex", GL_FLOAT, 0, 4);glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, rendered_texture);//bind and create fbo
QOpenGLFramebufferObject fbo(img.size());
qDebug()<<"bind success? : "<<fbo.bind();glDrawArrays(GL_TRIANGLES, 0, buffer.size());
QImage result = fbo.toImage();program.disableAttributeArray("qt_Vertex");
program.release();
buffer.release();fbo.release();
glActiveTexture(0);
glBindTexture(GL_TEXTURE_2D, 0);
context_->doneCurrent();return result;
@But I am still get a blank image