使用 Qt5 进行屏幕外渲染(openGL)
Posted
技术标签:
【中文标题】使用 Qt5 进行屏幕外渲染(openGL)【英文标题】:Do offscreen render(openGL) with Qt5 【发布时间】:2013-06-20 19:04:00 【问题描述】:使用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;
我尽量简化代码,但还是很冗长
【问题讨论】:
你的#version
指令在哪里?
我对我应该使用哪个版本感到困惑,但我发现即使我没有指定#version,只要我坚持使用opengl es 2.0,带有openGL的Qt也可以理解glsl。请如果我错了,请纠正我,一个发现 openGL 非常复杂的新手。
【参考方案1】:
对于离屏渲染,尝试渲染到QOpenGLFramebufferObject,可以是converted into a QImage
,然后可以很容易地保存到磁盘。
然而,为此,您仍然需要一个要渲染的表面(根据QOpenGLContext::makeCurrent()
的要求),因此您唯一的选择确实是使用QWindow
或QGLWidget
来获得这样的表面。
在 Qt 5.1 中,将有一个名为 QOffscreenSurface 的新类,它可能最适合您的用例。您将使用QOffscreenSurface
为您的OpenGL context 获取表面,然后使用QOpenGLFramebufferObject
渲染到FBO,然后调用QOpenGLFramebufferObject::toImage()
以访问像素。
【讨论】:
谢谢,我正在研究“OpenGL Window 示例”,openGL 上下文并尝试了解什么是 FBO,相当复杂的东西(我是唯一认为 openGL 非常复杂的人吗?)。将发布如果我能弄清楚如何做我想做的事,就会回答。即使qml2好用,当你需要更多的图形能力时,openGL的知识看起来是必须的。以上是关于使用 Qt5 进行屏幕外渲染(openGL)的主要内容,如果未能解决你的问题,请参考以下文章
使用 Docker 使用 QOffscreenSurface 进行屏幕外渲染
使用带有 FBO 的 OpenGL 3.2+ 的 Linux 屏幕外渲染
使用 QOpenGLWidget 进行屏幕外渲染的最简单方法