2013-06-20 123 views
5

使用openGL做一些图像处理,第一个实验是 将彩色图像转换成灰色,一切都很好,除了我不想 显示小部件。用Qt5做屏幕外渲染(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; 
} 

我尽量简化代码,但它仍然是相当冗长

+0

你的'#版本'指令在哪里? – genpfault

+0

我很疑惑应该使用哪个版本,但是我发现即使我没有指定#version,只要我坚持使用opengl 2.0,Qt和openGL都可以理解glsl。如果我错了,请纠正我,由一个发现openGL非常复杂的newb。 – StereoMatching

回答

9

对离屏渲染,渲染尝试进入QOpenGLFramebufferObject ,它可以是converted into a QImage,这反过来可以很容易地保存到磁盘。

为此但是,你仍然需要一个表面渲染到(所要求的QOpenGLContext::makeCurrent()),所以你唯一的选择确实是使用QWindowQGLWidget得到这样的表面。

在Qt 5.1中,将会有一个新的类叫做QOffscreenSurface,它可能最适合你的用例。您将使用QOffscreenSurface为您的OpenGL context获取曲面,然后使用QOpenGLFramebufferObject将其渲染为FBO,然后调用QOpenGLFramebufferObject::toImage()访问像素。

+0

谢谢,我正在学习“OpenGL窗口示例”,openGL上下文,并尝试理解什么是FBO,相当复杂的东西(我是唯一一个认为openGL非常复杂的东西?)。如果我能弄清楚如何发布答案做我想做的事。即使qml2很容易使用,当你需要更多的图形功能时,openGL的知识看起来是必须的。 – StereoMatching