2013-06-20 117 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