目录
[1 FBO应用场景(AI)](#1 FBO应用场景(AI))
[1.1 核心场景(按你Qt项目的相关性排序)](#1.1 核心场景(按你Qt项目的相关性排序))
[1. 屏幕后期特效(最常用)](#1. 屏幕后期特效(最常用))
[2. 渲染到纹理(Render To Texture)](#2. 渲染到纹理(Render To Texture))
[3. 阴影贴图 ShadowMap(3D渲染)](#3. 阴影贴图 ShadowMap(3D渲染))
[4. MRT 多渲染目标(Multiple Render Targets)](#4. MRT 多渲染目标(Multiple Render Targets))
[5. 离屏导出图片(不显示窗口,直接保存图片)](#5. 离屏导出图片(不显示窗口,直接保存图片))
[6. 低分辨率渲染(提升性能)](#6. 低分辨率渲染(提升性能))
[1.2 区分两个附件类型(写代码必懂)](#1.2 区分两个附件类型(写代码必懂))
[1.3 使用流程](#1.3 使用流程)
[1.3.1 极简流程示例](#1.3.1 极简流程示例)
[2 Demo 源码](#2 Demo 源码)
[2.1 效果](#2.1 效果)
[2.2 myopengl.h文件](#2.2 myopengl.h文件)
[2.3 myopengl.cpp](#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_H
2.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();
// }