目录
1 FBO应用场景(AI)
1.1 核心场景(按你Qt项目的相关性排序)
1. 屏幕后期特效(最常用)
2. 渲染到纹理(Render To Texture)
3. 阴影贴图 ShadowMap(3D渲染)
4. MRT 多渲染目标(Multiple Render Targets)
5. 离屏导出图片(不显示窗口,直接保存图片)
6. 低分辨率渲染(提升性能)
1.2 区分两个附件类型(写代码必懂)
1.3 使用流程
1.3.1 极简流程示例
2 Demo 源码
2.1 效果
2.2 myopengl.h文件
2.3 myopengl.cpp
1 FBO应用场景(AI)
默认渲染是画到窗口屏幕(默认帧缓冲);FBO是自定义的离屏渲染目标,可以把画面渲染到一张纹理或者RenderBuffer,不直接显示在窗口上,渲染结果后续可以作为纹理继续采样处理,非常适合Qt+OpenGL做图像、3D特效开发。
1.1 核心场景(按你Qt项目的相关性排序)
1. 屏幕后期特效(最常用)
先把整个场景渲染到FBO的纹理,再拿这张纹理画全屏四边形,shader做图像处理。
- 模糊、锐化、灰度、反相、LUT调色
- 景深、运动模糊、胶片颗粒
流程:绑定FBO → 渲染场景到纹理 → 切回默认窗口帧缓冲 → 使用刚才生成的纹理绘制全屏矩形做后处理。
2. 渲染到纹理(Render To Texture)
你可以动态渲染内容生成一张GPU纹理,再贴到模型/UI上:
- 镜子反射、水面反射(从镜子视角渲染场景到FBO纹理,贴到水面平面)
- 动态看板:3D模型上的显示屏,实时渲染文字/视频画面
- 数字孪生、大屏可视化里面动态贴图
3. 阴影贴图 ShadowMap(3D渲染)
从光源视角渲染场景,把深度值渲染到FBO深度纹理,主渲染pass采样这张深度图做阴影判断。
4. MRT 多渲染目标(Multiple Render Targets)
FBO可以一次性绑定多个颜色附件,一次drawcall同时输出多张纹理。
- G-Buffer(延迟渲染):一次渲染输出 颜色、法线、位置、金属粗糙度多张贴图,后面做光照计算,游戏/数字孪生常用。
5. 离屏导出图片(不显示窗口,直接保存图片)
不弹出窗口,直接渲染画面到FBO,然后glReadPixels读像素到内存,保存图片。 适合:批量渲染截图、生成缩略图、服务端无窗口渲染。
6. 低分辨率渲染(提升性能)
用更小分辨率FBO渲染场景,再放大绘制到窗口,降低GPU开销;比如VR、性能弱的嵌入式Qt设备。
1.2 区分两个附件类型(写代码必懂)
- 纹理附件 Texture:FBO绑定纹理 → 渲染结果后续可以在shader里采样(上面绝大多数场景都用它)
- RenderBuffer渲染缓冲:不能当纹理采样,只做深度/模板缓冲,配合FBO使用,只用于深度测试存储。
1.3 使用流程
- FBO需要完整性检查
glCheckFramebufferStatus(GL_FRAMEBUFFER),附件格式/尺寸不匹配会渲染失败黑屏。 - 绑定FBO后,记得修改
glViewport,尺寸要和FBO纹理分辨率一致。 - 绘制完成要解绑FBO,切回默认帧缓冲(0),不然继续往FBO绘制,窗口黑屏。
- FBO本身不存储像素,像素存储在绑定的纹理或RenderBuffer。
1.3.1 极简流程示例
// 1. 创建FBO、创建颜色纹理附件、深度RenderBuffer,绑定到FBO glBindFramebuffer(GL_FRAMEBUFFER, fboId); // 2. 渲染场景,输出到FBO绑定的纹理 renderScene(); //3. 切回默认屏幕帧缓冲 glBindFramebuffer(GL_FRAMEBUFFER,0); //4. 使用FBO生成的纹理,绘制到屏幕做后处理 drawQuadWithTexture(fboTexture);2 Demo 源码
2.1 效果
2.2 myopengl.h文件
#ifndef MYOPENGL_H #define MYOPENGL_H // 继承字OpenGLWidget #include <QOpenGLWidget> // 使用3.3的版本 #include <QOpenGLFunctions_3_3_Core> // Qt的shaderProgram对象 #include <QOpenGLShaderProgram> class MyOpenGL : public QOpenGLWidget,protected QOpenGLFunctions_3_3_Core { Q_OBJECT public: explicit MyOpenGL(QWidget* parent = nullptr); ~MyOpenGL(); protected: virtual void initializeGL(); virtual void resizeGL(int w, int h); virtual void paintGL(); private: // 输入的图像纹理 GLuint m_texture; // FBO帧缓冲对象,fbo渲染输出纹理 GLuint fbo_VAO,fbo_VBO,fbo_EBO; GLuint FBO, fboTex, loc_fboTex;//fboTex 为输出纹理 GLuint m_fboProgram; // shader 用于渲染的纹理位置 GLuint screen_VAO,screen_VBO,screen_EBO; GLuint fbo_screen, loc_screenTex; GLuint m_screenProgram; // shader程序对象 void initFBOShader(); void initScreenShader(); void createFBO(); void setupFBOGeometry(); void setupScreenGeometry(); bool loadTextureFromQImage(const QString &path, GLuint &outTextureId); }; #endif // MYOPENGL_H #ifndef MYOPENGL_H #define MYOPENGL_H // 继承字OpenGLWidget #include <QOpenGLWidget> // 使用3.3的版本 #include <QOpenGLFunctions_3_3_Core> // Qt的shaderProgram对象 #include <QOpenGLShaderProgram> class MyOpenGL : public QOpenGLWidget,protected QOpenGLFunctions_3_3_Core { Q_OBJECT public: explicit MyOpenGL(QWidget* parent = nullptr); ~MyOpenGL(); protected: virtual void initializeGL(); virtual void resizeGL(int w, int h); virtual void paintGL(); private: // 输入的图像纹理 GLuint m_texture; // FBO帧缓冲对象,fbo渲染输出纹理 GLuint fbo_VAO,fbo_VBO,fbo_EBO; GLuint FBO, fboTex, loc_fboTex;//fboTex 为输出纹理 GLuint m_fboProgram; // shader 用于渲染的纹理位置 GLuint screen_VAO,screen_VBO,screen_EBO; GLuint fbo_screen, loc_screenTex; GLuint m_screenProgram; // shader程序对象 void initFBOShader(); void initScreenShader(); void createFBO(); void setupFBOGeometry(); void setupScreenGeometry(); bool loadTextureFromQImage(const QString &path, GLuint &outTextureId); }; #endif // MYOPENGL_H2.3 myopengl.cpp
#include "myopengl.h" MyOpenGL::MyOpenGL(QWidget *parent) :QOpenGLWidget(parent) { qDebug()<< "inited"; } MyOpenGL::~MyOpenGL() { // 切换到当前widget的上下文 makeCurrent(); glDeleteVertexArrays(1, &screen_VAO); glDeleteBuffers(1,&screen_VBO); glDeleteBuffers(1, &screen_EBO); glDeleteProgram(m_screenProgram); doneCurrent(); } void MyOpenGL::initializeGL() { // 获取OpenGL的函数指针 this->initializeOpenGLFunctions(); // 获取屏幕的默认fbo // 1 opengl原生方法初始化Shader程序 initScreenShader(); // 2 初始化物体信息 setupScreenGeometry(); // fbo 帧缓冲 createFBO(); initFBOShader(); setupFBOGeometry(); // 加载纹理 loadTextureFromQImage(":/res/2.png",m_texture); } void MyOpenGL::resizeGL(int w, int h) { // 好像内部会重建,不用自己写 // glBindTexture(GL_TEXTURE_2D,fboTex); // glTexImage2D(GL_TEXTURE_2D,0,GL_BGRA,w,h,0,GL_BGRA,GL_UNSIGNED_BYTE,nullptr); fbo_screen = defaultFramebufferObject(); } void MyOpenGL::paintGL() { // 1 进入到FBO缓冲 glBindFramebuffer(GL_FRAMEBUFFER,FBO); glClearColor(1,0,0,1); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(m_fboProgram); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D,m_texture); glUniform1i(loc_fboTex,0); // 将纹理索引链接到0号 glBindVertexArray(fbo_VAO); glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,nullptr); glBindVertexArray(0); glBindTexture(GL_TEXTURE_2D,0); glUseProgram(0); // 2 进入到默认的屏幕渲染缓冲 glBindFramebuffer(GL_FRAMEBUFFER, fbo_screen); // 清理屏幕 glClearColor(0,1,0,1); glClear(GL_COLOR_BUFFER_BIT); // 使用shader程序 glUseProgram(m_screenProgram); //原生写法 // m_shaderProgram->bind(); // qt 的写法 // 激活 shader 中的纹理 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, fboTex); glUniform1i(loc_screenTex,0); glBindVertexArray(screen_VAO); glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,nullptr); glBindVertexArray(0); glUseProgram(0); } void MyOpenGL::setupScreenGeometry(){ float vertices[24] = { // 位置 纹理坐标 -0.5f, -0.5f, 0.0f, 0.0f,1.0f, // QImage的原点坐标在左上,而OpenGL在左下角 -0.5f, 0.5f, 0.0f, 0.0f,0.0f, 0.5f, -0.5f, 0.0f, 1.0f,1.0f, 0.5f, 0.5f, 0.0f, 1.0f,0.0f }; // 每3个点读一次 unsigned int indexes[6] = {0,1,2,1,2,3}; glGenVertexArrays(1,&screen_VAO); glGenBuffers(1, &screen_VBO); glGenBuffers(1, &screen_EBO); // 激活定点数组配置,开始记录定点属性 glBindVertexArray(screen_VAO); // 绑定VBO 包含数据 glBindBuffer(GL_ARRAY_BUFFER, screen_VBO); glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW); // EBO 缓冲 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, screen_EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indexes),indexes,GL_STATIC_DRAW); // 参数1:索引 // 参数2:几个数据 // 参数3:数据类型 // 参数4:是否归一化,如果是0-255需要转为0-1 // 参数5:步长 // 参数6:偏移量 glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,5 * sizeof(float),(void*)0); glEnableVertexAttribArray(0); // 指定为程序的0号layout glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,5 * sizeof(float),(void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); // 解绑VBO glBindBuffer(GL_ARRAY_BUFFER,0); // 解绑VAO 不需要解绑EBO,当VAO解绑时自动解绑 glBindVertexArray(0); } bool MyOpenGL::loadTextureFromQImage(const QString &path, GLuint &outTextureId) { QImage img(path); if(img.isNull()){ qWarning()<< "加载失败"; return false; } // 1 opengl 创建纹理 glGenTextures(1, &outTextureId); // 2 opengl 绑定纹理类型 glBindTexture(GL_TEXTURE_2D,outTextureId); // 3 opengl 上传数据 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.width(), img.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); 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); return true; } void MyOpenGL::initFBOShader() { const char* vSrc = R"( #version 330 core layout(location=0) in vec3 aPos; layout(location=1) in vec2 aTex; out vec2 texCoord; void main(){ gl_Position = vec4(aPos,1.0); texCoord = aTex; } )"; const char* fSrc = R"( #version 330 core in vec2 texCoord; out vec4 FragColor; uniform sampler2D texture1; void main(){ //FragColor = vec4(ourColor,1.0); // 加一点蓝色 FragColor = texture(texture1,texCoord) + vec4(0,0,0.5,0); } )"; GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader,1,&vSrc,nullptr); glCompileShader(vertexShader); GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader,1,&fSrc,nullptr); glCompileShader(fragmentShader); m_fboProgram = glCreateProgram(); glAttachShader(m_fboProgram,vertexShader); glAttachShader(m_fboProgram,fragmentShader); glLinkProgram(m_fboProgram); glDeleteShader(vertexShader); glDeleteShader(fragmentShader); loc_fboTex = glGetUniformLocation(m_fboProgram,"texture1"); } void MyOpenGL::initScreenShader(){ const char* vSrc = R"( #version 330 core layout(location=0) in vec3 aPos; layout(location=1) in vec2 aTex; out vec2 texCoord; void main(){ gl_Position = vec4(aPos,1.0); texCoord = aTex; } )"; const char* fSrc = R"( #version 330 core in vec2 texCoord; out vec4 FragColor; uniform sampler2D texture1; void main(){ //FragColor = vec4(ourColor,1.0); FragColor = texture(texture1,texCoord); } )"; // 创建shader,加载源码,编译 GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader,1,&vSrc,nullptr); glCompileShader(vertexShader); GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader,1,&fSrc,nullptr); glCompileShader(fragmentShader); // 创建程序,绑定shader,链接为GPU程序 m_screenProgram = glCreateProgram(); glAttachShader(m_screenProgram,vertexShader); glAttachShader(m_screenProgram,fragmentShader); glLinkProgram(m_screenProgram); glDeleteShader(vertexShader); glDeleteShader(fragmentShader); loc_screenTex = glGetUniformLocation(m_screenProgram,"texture1"); } void MyOpenGL::createFBO() { glGenFramebuffers(1, &FBO); glBindFramebuffer(GL_FRAMEBUFFER,FBO); glGenTextures(1,&fboTex); glBindTexture(GL_TEXTURE_2D,fboTex); // 创建一个空纹理 glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA, 800, 800, 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr); 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); // 指定Framebuffer的纹理id glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,fboTex,0); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if(status != GL_FRAMEBUFFER_COMPLETE) { qDebug() << "FBO创建失败 status:" << status; }else{ qDebug() << "FBO创建成功 status:" << status; } // 清空 glBindFramebuffer(GL_FRAMEBUFFER,0); } void MyOpenGL::setupFBOGeometry() { float vertices[24] = { // 位置 纹理坐标 -0.5f, -0.5f, 0.0f, 0.0f,0.0f, -0.5f, 0.5f, 0.0f, 0.0f,1.0f, 0.5f, -0.5f, 0.0f, 1.0f,0.0f, 0.5f, 0.5f, 0.0f, 1.0f,1.0f }; // 每3个点读一次 unsigned int indexes[6] = {0,1,2,1,2,3}; glGenVertexArrays(1,&fbo_VAO); glGenBuffers(1,&fbo_VBO); glGenBuffers(1,&fbo_EBO); glBindVertexArray(fbo_VAO); glBindBuffer(GL_ARRAY_BUFFER,fbo_VBO); glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,fbo_EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indexes),indexes,GL_STATIC_DRAW); glVertexAttribPointer(0,3,GL_FLOAT,false,5 * sizeof(GLfloat),(void*)0); glVertexAttribPointer(1,2,GL_FLOAT,false,5 * sizeof(GLfloat),(void*)(3 * sizeof(GLfloat))); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glBindVertexArray(0); // vao ebo glBindBuffer(GL_ARRAY_BUFFER,0); //vbo } // void MyOpenGL::initQtShader() // { // const char* vSrc = R"( // #version 330 core // layout(location=0) in vec3 aPos; // layout(location=1) in vec3 aColor; // out vec3 ourColor; // void main(){ // gl_Position = vec4(aPos,1.0); // ourColor = aColor; // } // )"; // const char* fSrc = R"( // #version 330 core // in vec3 ourColor; // out vec4 FragColor; // void main(){ // FragColor = vec4(ourColor,1.0); // } // )"; // m_shaderProgram = new QOpenGLShaderProgram(); // QOpenGLShader * vertexShader = new QOpenGLShader(QOpenGLShader::Vertex); // vertexShader->compileSourceCode(vSrc); // QOpenGLShader * fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment); // fragmentShader->compileSourceCode(fSrc); // // 对应原生的attach // m_shaderProgram->addShader(vertexShader); // m_shaderProgram->addShader(fragmentShader); // // 将编译完成的着色器目标对象进行链接,生成完整GPU着色器程序 // m_shaderProgram->link(); // }