使用Qt5进行屏幕外渲染(openGL)

时间:2013-06-20 19:04:01

标签: c++ qt opengl

使用openGL进行一些图像处理,第一个实验是 将彩色图像转换为灰色,除了我不想要的一切都很好 显示小部件。

如果我不调用“show()”,QGLWidget将不会开始渲染纹理 我可以在不显示小部件的情况下渲染纹理吗? QGLWidget是一个正确的工具吗?

部分代码

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

顶点着色器

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

片段着色器

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

编辑:使用QWindow FBO进行离屏渲染,但结果是空白图像

代码可以编译,但QOpenGLFrameBufferObject返回的QImage为空。

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

    return result;
}

我尽力简化代码,但它仍然非常详细

1 个答案:

答案 0 :(得分:9)

对于屏幕外渲染,请尝试渲染QOpenGLFramebufferObjectconverted into a QImageQOpenGLContext::makeCurrent(),然后可以轻松保存到磁盘。

然而,对于那个,你仍然需要一个表面来渲染(根据QOffscreenSurface的要求),所以你唯一的选择确实是使用QWindowQGLWidget来获得这样的表面上。

在Qt 5.1中,将会有一个名为QOffscreenSurface的新类,它可能最适合您的用例。您可以使用OpenGL context获取QOpenGLFramebufferObject的表面,然后使用QOpenGLFramebufferObject::toImage()渲染到FBO,然后调用{{3}}以访问像素。