PDA

View Full Version : Do offscreen render(openGL) with Qt5



stereoMatching
23rd June 2013, 02:06
I want to do offscreen rendering, but have no luck
These are the codes, I have no any idea why the FBO(frame buffer object)
always give me a blank image:confused:

What I want to do is, the texture after render will write to the FBO
after that, I could transfer the data of fbo into QImage by toImage()

.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
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");
}

//if(glCheckFramebufferStatus(fbo.handle()) != GL_FRAMEBUFFER_COMPLETE){
// qDebug()<<"frame buffer error";
//}
qDebug()<<"has opengl fbo : "<<QOpenGLFramebufferObject::hasOpenGLFramebufferObje cts();

//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::F ragment,
"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);

//bind and create fbo
QOpenGLFramebufferObject fbo(img.size());
qDebug()<<"bind success? : "<<fbo.bind();

glDrawArrays(GL_TRIANGLES, 0, buffer.size());

program.disableAttributeArray("qt_Vertex");
program.release();
glActiveTexture(0);
glBindTexture(GL_TEXTURE_2D, 0);
buffer.release();

QImage result = fbo.toImage();
fbo.release();
context_->doneCurrent();

return result;
}

saman_artorious
23rd June 2013, 11:45
your fbo implementation is correct. But, may I ask why you are using 0 here:

glBindTexture(GL_TEXTURE_2D, 0);

stereoMatching
23rd June 2013, 15:20
Thanks for your reply, I unbind the texture too early.



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

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


I change the order of the line(the original can't edit), but I still get a blank image

saman_artorious
25th June 2013, 05:54
can't you test your fbo instructions separately in another program? You may also have a look at my own fbo implementation sample (ignore the lighting part of the program):



void GlWidget::paintGL()
{
BakeDynamicTexture();

// RenderShapes();

glClearColor(0, 0, 0.0f, 1.0f);
// //! [2]
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, width(),height());

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);
vMatrix.lookAt(cameraPosition, QVector3D(0, 0, 0), cameraUpDirection);

QMatrix4x4 mvMatrix;
mMatrix.setToIdentity();

mvMatrix = vMatrix * mMatrix;

QMatrix3x3 normalMatrix;
normalMatrix = mvMatrix.normalMatrix();

QMatrix4x4 lightTransformation;
lightTransformation.rotate(lightAngle, 0, 1, 0);

QVector3D lightPosition = lightTransformation * QVector3D(0, 1, 1);

//! [3]
//##########################
// lightingShaderProgram.setUniformValue("mvpMatrix", pMatrix * vMatrix * mMatrix);
// shpFill->RenderCube (&lightingShaderProgram, dynamicTexture);
// lightingShaderProgram.release();
//##########################

//! [5]
lightingShaderProgram.bind();

lightingShaderProgram.setUniformValue("mvpMatrix", pMatrix * mvMatrix);
lightingShaderProgram.setUniformValue("mvMatrix", mvMatrix);
lightingShaderProgram.setUniformValue("normalMatrix", normalMatrix);
lightingShaderProgram.setUniformValue("lightPosition", vMatrix * lightPosition);

shpFill->RenderLightingCube(&lightingShaderProgram, dynamicTexture);

mMatrix.setToIdentity();
mMatrix.translate(lightPosition);
mMatrix.rotate(lightAngle, 0, 1, 0);
mMatrix.rotate(45, 1, 0, 0);
mMatrix.scale(0.1);

coloringShaderProgram.bind();
coloringShaderProgram.setUniformValue("mvpMatrix", pMatrix * vMatrix * mMatrix);

shpFill->colorLightingCube(&coloringShaderProgram);
}



Baking part:


void GlWidget::BakeDynamicTexture()
{
fbo->bind();

RenderShapes();

fbo->release();

dynamicTexture = fbo->texture();
}


RenderShapes (instead of drawing a shape file here, simply draw something else, check if it draws successfully)


void GlWidget::RenderShapes()
{
QMatrix4x4 sMatrix;

glClearColor(0.1f, 0.5f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, 256,256);

shaderProgram.bind();

sMatrix.setToIdentity();

shaderProgram.setUniformValue("mvpMatrix", sMatrix);

shpFill->Render(&shaderProgram);

shaderProgram.release();
}


RenderLightingCube:


void FillShape::RenderLightingCube(QGLShaderProgram* lightingShaderProgram, GLuint cubeTexture)
{
lightingShaderProgram->setUniformValue("ambientColor", QColor(32, 32, 32));
lightingShaderProgram->setUniformValue("diffuseColor", QColor(128, 128, 128));
lightingShaderProgram->setUniformValue("specularColor", QColor(255, 255, 255));
lightingShaderProgram->setUniformValue("ambientReflection", (GLfloat) 1.0);
lightingShaderProgram->setUniformValue("diffuseReflection", (GLfloat) 1.0);
lightingShaderProgram->setUniformValue("specularReflection", (GLfloat) 1.0);
lightingShaderProgram->setUniformValue("shininess", (GLfloat) 100.0);
lightingShaderProgram->setUniformValue("texture", 0);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, cubeTexture);
glActiveTexture(0);

lightingShaderProgram->setAttributeArray("vertex", cubeVertices.constData());
lightingShaderProgram->enableAttributeArray("vertex");
lightingShaderProgram->setAttributeArray("normal", cubeNormals.constData());
lightingShaderProgram->enableAttributeArray("normal");
lightingShaderProgram->setAttributeArray("textureCoordinate", cubeTextureCoordinates.constData());
lightingShaderProgram->enableAttributeArray("textureCoordinate");

glDrawArrays(GL_TRIANGLES, 0, cubeVertices.size());

lightingShaderProgram->disableAttributeArray("vertex");
lightingShaderProgram->disableAttributeArray("normal");
lightingShaderProgram->disableAttributeArray("textureCoordinate");

lightingShaderProgram->release();
}

stereoMatching
26th June 2013, 11:34
can't you test your fbo instructions separately in another program?
Sorry but I don't get it, do you mean test it with different shader codes?