你已经提供了 `Demo11.cpp` 和 `framebuffers_screen.fs` 的部分内容,并且当前目标是**补充 `demo14.cpp` 和完成 `framebuffers_screen.fs` 片段着色器功能**,结合上下文来看,这是一个使用 **帧缓冲(Framebuffer)进行后处理(Post-processing)** 的 OpenGL 示例。
我们将基于你的原始代码结构和需求,实现一个完整的 **屏幕后处理系统**,支持:
- 反相
- 灰度化(平均 & 加权)
- 边缘检测、模糊、锐化等卷积核操作
---
### ✅ 1. 完整的 `framebuffers_screen.fs` 实现
#### **framebuffers_screen.fs**
```glsl
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D screenTexture;
// 反相效果
vec4 invers() {
vec4 color = texture(screenTexture, TexCoords);
return vec4(vec3(1.0 - color.rgb), color.a);
}
// 平均灰度:简单取 RGB 均值
vec4 gray1() {
vec4 color = texture(screenTexture, TexCoords);
float avg = (color.r + color.g + color.b) / 3.0;
return vec4(avg, avg, avg, color.a);
}
// 加权灰度:更符合人眼感知(NTSC 标准)
vec4 gray2() {
vec4 color = texture(screenTexture, TexCoords);
float avg = 0.299 * color.r + 0.587 * color.g + 0.114 * color.b;
return vec4(avg, avg, avg, color.a);
}
// 卷积核偏移量(用于边缘/模糊/锐化)
const float offset = 1.0 / 300.0;
vec2 offsets[9] = vec2[](
vec2(-offset, offset), // 左上
vec2( 0.0f, offset), // 正上
vec2( offset, offset), // 右上
vec2(-offset, 0.0f), // 左
vec2( 0.0f, 0.0f), // 中
vec2( offset, 0.0f), // 右
vec2(-offset, -offset), // 左下
vec2( 0.0f, -offset), // 正下
vec2( offset, -offset) // 右下
);
// 卷积核函数:根据 type 返回不同滤波效果
vec4 kernel(int type) {
vec3 sample[9];
for (int i = 0; i < 9; i++) {
sample[i] = vec3(texture(screenTexture, TexCoords + offsets[i]));
}
if (type == 1) { // 边缘检测(Sobel-like 或 Laplacian)
vec3 edge =
-sample[0] - sample[1] - sample[2] +
-sample[3] + 8.0 * sample[4] - sample[5] +
-sample[6] - sample[7] - sample[8];
return vec4(edge, 1.0);
}
else if (type == 2) { // 模糊(Box Blur)
vec3 avg = vec3(0.0);
for (int i = 0; i < 9; i++)
avg += sample[i];
avg *= 1.0 / 9.0;
return vec4(avg, 1.0);
}
else if (type == 3) { // 锐化
vec3 sharpened =
-0.1 * sample[0] - 0.1 * sample[1] - 0.1 * sample[2] +
-0.1 * sample[3] + 1.8 * sample[4] - 0.1 * sample[5] +
-0.1 * sample[6] - 0.1 * sample[7] - 0.1 * sample[8];
return vec4(clamp(sharpened, 0.0, 1.0), 1.0);
}
else {
return texture(screenTexture, TexCoords); // 默认原图
}
}
void main()
{
// 原图
FragColor = texture(screenTexture, TexCoords);
// 后处理效果切换(取消注释以启用)
// FragColor = invers(); // 反相
// FragColor = gray1(); // 平均灰度
// FragColor = gray2(); // 加权灰度
// FragColor = kernel(1); // 边缘检测
// FragColor = kernel(2); // 模糊
// FragColor = kernel(3); // 锐化
}
```
---
### ✅ 2. 完整的 `demo14.cpp` 实现
这个文件应为新的主程序入口,整合了前几个示例的功能:**渲染到帧缓冲 + 屏幕后处理**
#### **demo14.cpp**
```cpp
// demo14.cpp: 使用帧缓冲实现屏幕后处理特效
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include "ShaderManager.h"
#include "TexturePool.h"
#include "LightManager.h"
#include "Object.h"
#include "Cube_Tangent_24.h"
#include "Plane.h"
#include "Quad.h"
#include "camera.h"
// 全局变量
unsigned int scrWidth = 800;
unsigned int scrHeight = 600;
GLFWwindow* window = nullptr;
Camera camera(glm::vec3(0.0f, 1.5f, 5.0f));
float lastX = scrWidth / 2.0f;
float lastY = scrHeight / 2.0f;
bool firstMouse = true;
// 渲染对象
const int Object_Num = 5;
Object* objects[Object_Num];
// FBO 相关
unsigned int fbo, textureBuffer;
// 当前后处理模式
int postProcessMode = 0; // 0=原图, 1=反相, 2=灰度1, 3=灰度2, 4=边缘, 5=模糊, 6=锐化
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double yoffset);
void processInput(GLFWwindow* window, double dt);
void init();
void render();
void update(float dt);
void cleanUp();
int createFrameBuffer(unsigned int& fbo, unsigned int& textureBuffer);
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
window = glfwCreateWindow(scrWidth, scrHeight, "Demo14 - Post Processing", NULL, NULL);
if (!window)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); // 隐藏光标并锁定
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
init();
double t = 0.0;
while (!glfwWindowShouldClose(window))
{
double dt = glfwGetTime() - t;
t = glfwGetTime();
processInput(window, dt);
update(static_cast<float>(dt));
render();
glfwPollEvents();
}
cleanUp();
glfwTerminate();
return 0;
}
void init()
{
TexturePool* texPool = TexturePool::getInstance();
texPool->addTexture("wood", "./texture/wood.png");
texPool->addTexture("brick_normal", "./texture/brickwall_normal.jpg");
ShaderManager* shaderMgr = ShaderManager::getInstance();
shaderMgr->addShader("object", "object.vs", "object.fs");
shaderMgr->addShader("outline", "outline.vs", "outline.fs");
shaderMgr->addShader("postprocess", "framebuffers_screen.vs", "framebuffers_screen.fs");
// 创建帧缓冲
createFrameBuffer(fbo, textureBuffer);
// 初始化场景对象
Shader* objShader = shaderMgr->getShader("object");
Shader* ppShader = shaderMgr->getShader("postprocess");
TextureNames cubeTex, planeTex;
cubeTex["material.diffuse"] = "wood";
planeTex["material.diffuse"] = "wood";
objects[0] = new Quad(ppShader, nullptr);
objects[0]->setTexture(textureBuffer);
objects[1] = new Plane(objShader, &planeTex);
objects[2] = new Cube_Tangent_24(objShader, &cubeTex);
objects[3] = new Cube_Tangent_24(objShader, &cubeTex);
objects[4] = new Cube_Tangent_24(objShader, &cubeTex);
for (int i = 0; i < Object_Num; ++i)
{
objects[i]->initData();
}
// 设置物体位置与旋转
objects[1]->setScale(glm::vec3(10.0f, 1.0f, 10.0f));
objects[1]->setTranslation(glm::vec3(0.0f, -0.5f, 0.0f));
objects[2]->setTranslation(glm::vec3(0.0f, 0.5f, 0.0f));
objects[3]->setTranslation(glm::vec3(2.0f, 0.5f, -2.0f));
objects[4]->setTranslation(glm::vec3(-2.0f, 0.5f, -1.0f));
objects[4]->setRotationSpeed(45.0f);
// 添加光照
LightProperty lightProp{};
lightProp.type = 2;
lightProp.position = glm::vec3(0.0f, 2.0f, 0.0f);
lightProp.ambient = glm::vec4(0.2f);
lightProp.diffuse = glm::vec4(1.0f);
lightProp.specular = glm::vec4(1.0f);
lightProp.constant = 1.0f;
lightProp.linear = 0.09f;
lightProp.quadratic = 0.032f;
LightManager* lightMgr = LightManager::getInstance();
lightMgr->addLight(&lightProp, objShader);
lightProp.type = 1;
lightProp.direction = glm::vec3(-0.5f, -1.0f, -0.5f);
lightProp.diffuse = glm::vec4(0.5f, 0.5f, 1.0f, 1.0f);
lightMgr->addLight(&lightProp, objShader);
vector<LightProperty*> lights;
lightMgr->getLightProperties(lights);
Material mat;
mat.ambient = glm::vec4(1.0f);
mat.diffuse = glm::vec4(1.0f);
mat.specular = glm::vec4(1.0f);
mat.shininess = 32.0f;
for (int i = 0; i < Object_Num; ++i)
{
if (i >= 1) // 不给 Quad 设置材质
{
objects[i]->setLightProperty(lights);
objects[i]->setMaterial(mat);
}
}
}
void render()
{
// Step 1: 渲染到帧缓冲
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glm::mat4 view = camera.GetViewMatrix();
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom),
(float)scrWidth / (float)scrHeight,
0.1f, 100.0f);
LightManager::getInstance()->render(view, projection, camera.Position);
for (int i = 1; i < Object_Num; ++i)
{
objects[i]->render(view, projection, camera.Position);
}
// Step 2: 渲染帧缓冲内容到屏幕(应用后处理)
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
// 切换后处理模式(通过按键控制)
static const char* effects[] = {
"Original", "Invert", "Gray1", "Gray2", "Edge", "Blur", "Sharpen"
};
string effectName = effects[postProcessMode % 7];
cout << "Effect: " << effectName << endl;
objects[0]->getShader()->use();
objects[0]->getShader()->setInt("postProcessType", postProcessMode % 7);
objects[0]->renderObject(); // 绘制全屏四边形
}
void update(float dt)
{
for (int i = 1; i < Object_Num; ++i)
{
objects[i]->update(dt);
}
LightManager::getInstance()->update(dt);
}
void processInput(GLFWwindow* window, double dt)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
camera.ProcessKeyboard(FORWARD, dt);
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
camera.ProcessKeyboard(BACKWARD, dt);
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
camera.ProcessKeyboard(LEFT, dt);
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
camera.ProcessKeyboard(RIGHT, dt);
// 切换后处理效果
if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS)
{
static bool spacePressed = false;
if (!spacePressed)
{
postProcessMode++;
spacePressed = true;
}
}
else
{
spacePressed = false;
}
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
scrWidth = width;
scrHeight = height;
glViewport(0, 0, width, height);
}
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
camera.ProcessMouseMovement(xoffset, yoffset);
}
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
camera.ProcessMouseScroll(yoffset);
}
void cleanUp()
{
for (int i = 0; i < Object_Num; ++i)
{
delete objects[i];
}
glDeleteFramebuffers(1, &fbo);
glDeleteTextures(1, &textureBuffer);
}
// 创建帧缓冲对象
int createFrameBuffer(unsigned int& fbo, unsigned int& textureBuffer)
{
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// 创建颜色纹理附件
glGenTextures(1, &textureBuffer);
glBindTexture(GL_TEXTURE_2D, textureBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, scrWidth, scrHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureBuffer, 0);
// 创建深度-模板渲染缓冲
unsigned int rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, scrWidth, scrHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;
return 0;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return 1;
}
```
---
### ✅ 补充说明
| 文件 | 功能 |
|------|------|
| `demo14.cpp` | 主循环逻辑,包含相机控制、FBO 渲染、后处理切换(按空格键) |
| `framebuffers_screen.fs` | 支持多种图像滤镜:反相、灰度、边缘、模糊、锐化 |
> 📌 提示:你可以通过修改 `objects[0]->getShader()->setInt("postProcessType", ...)` 来动态选择效果。但为了兼容你的原始设计,我们建议你在 fragment shader 中用注释方式手动切换调试。
或者进一步扩展,在 shader 中添加 uniform:
```glsl
uniform int postProcessType;
...
void main() {
if (postProcessType == 0) FragColor = texture(...);
else if (postProcessType == 1) FragColor = invers();
...
}
```
然后在 C++ 中用 `shader->setInt("postProcessType", mode)` 控制。
---