文章目录[x]
- 1:设置帧缓冲
- 2:使用帧缓冲
- 3:反相(Inversion)
- 4:灰度(Grayscale)
- 5:核效果
- 6:高斯模糊
- 7:边缘检测
- 8:更多的核
设置帧缓冲
// 生成帧缓冲
glGenFramebuffers(1, &m_FBO);
glBindFramebuffer(GL_FRAMEBUFFER, m_FBO);
// 纹理作为附件,这个用来装颜色
glGenTextures(1, &texColorBuffer);
glBindTexture(GL_TEXTURE_2D, texColorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCREEN_WIDTH, SCREEN_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// GL_COLOR_ATTACHMENT0 附件类型
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texColorBuffer, 0);
glBindTexture(GL_TEXTURE, 0);
// 下面代码可以让深度和模板缓冲也写入到一张纹理中,其中深度24位,模板8位
//glTexImage2D(
// GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 800, 600, 0,
// GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL
//);
//glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texture, 0);
// 使用渲染缓冲对象作为附件,这个用来装深度缓冲和模板缓冲
glGenRenderbuffers(1, &m_RBO);
glBindRenderbuffer(GL_RENDERBUFFER, m_RBO);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, SCREEN_WIDTH, SCREEN_HEIGHT);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_RBO);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
std::cout << "[ERROR::FRAMEBUFFER] Framebuffer is not complete" << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
使用帧缓冲
View Code
virtual void Setup() override
{
// 上述设置 ...略
float quadVertices[] = { // vertex attributes for a quad that fills the entire screen in Normalized Device Coordinates.
// positions // texCoords
-1.0f, -1.0f, 0.0f, 0.0f,
+1.0f, -1.0f, 1.0f, 0.0f,
+1.0f, +1.0f, 1.0f, 1.0f,
-1.0f, +1.0f, 0.0f, 1.0f
};
unsigned int quadIndices[] = {
0, 1, 2,
2, 3, 0
};
m_VA[1] = new VertexArray();
m_VB[1] = new VertexBuffer(quadVertices, sizeof(quadVertices));
VertexBufferLayout layout;
layout.Push<float>(2);
layout.Push<float>(2);
m_VA[1]->AddBuffer(*m_VB[1], layout);
m_IB[1] = new IndexBuffer(quadIndices, 6);
m_Shader[1] = new Shader(m_ShaderFile);
DepthTestAndStencilTest::Basic::Setup();
}
virtual void Render() override
{
// 第一阶段 绑定帧缓冲 绘制场景
glBindFramebuffer(GL_FRAMEBUFFER, m_FBO);
glEnable(GL_DEPTH_TEST);
m_Renderer.Clear(m_ClearColor, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
DepthTestAndStencilTest::Basic::Render();
// 第二阶段 解绑帧缓冲, 使用默认的帧缓冲绘制自定义的帧缓冲
glBindFramebuffer(GL_FRAMEBUFFER, 0); // 这里把帧缓冲设置回默认
glDisable(GL_DEPTH_TEST);
m_Renderer.Clear(glm::vec4(1.0f), GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texColorBuffer);
m_Shader[1]->Bind();
m_Shader[1]->SetInt("screenTexture", 0);
m_Renderer.Draw(m_VA[1], m_IB[1], m_Shader[1]);
}
#Shader Vertex
#version 330 core
layout(location = 0) in vec2 a_position;
layout(location = 1) in vec2 a_texCoords;
out vec2 iTexCoords;
void main()
{
gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0);
iTexCoords = a_texCoords;
}
#Shader Fragment
#version 330 core
out vec4 FragColor;
in vec2 iTexCoords;
uniform sampler2D screenTexture;
void main()
{
FragColor = texture(screenTexture, iTexCoords);
}
可以看到,虽然看起来效果没差。但是当开启线框绘制后,明显看到这其实是一张Texture而已。(类似Unity中的RenderTexture)
利用帧缓冲,我们就可以干很多事,比如后处理(post-procoessing)
反相(Inversion)
// Fragment Shader
void main()
{
FragColor = vec4(1 - vec3(texture(screenTexture, iTexCoords)), 1.0);
}
灰度(Grayscale)
void main()
{
fragColor = texture(screenTexture, iTexCoords);
// float average = (fragColor.r + fragColor.g + fragColor.b) / 3.0;
// 由于人眼对绿色更敏感,对蓝色不敏感,所以一般会让绿色的权重高点
float average = fragColor.r * 0.2126 + fragColor.g * 0.7152 + fragColor.b * 0.0722;
fragColor = vec4(average, average, average, 1.0);
}
核效果
uniform int kernelType;
void main()
{
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) // 右下
);
float kernel[9];
if (kernelType == 1)
{
kernel = float[](
1, 1, 1,
1, -9, 1,
1, 1, 1
);
}
else if (kernelType == 2)
{
kernel = float[](
-1, -1, -1,
-1, +9, -1,
-1, -1, -1
);
}
// outline
else if (kernelType == 3)
{
kernel = float[](
-1, -1, -1,
-1, +8, -1,
-1, -1, -1
);
}
vec3 sampleTex[9];
for (int i = 0; i < 9; i++)
sampleTex[i] = vec3(texture(screenTexture, iTexCoords.st + offsets[i]));
vec3 col = vec3(0.0);
for (int i = 0; i < 9; i++)
col += sampleTex[i] * kernel[i];
FragColor = vec4(col, 1.0);
}
高斯模糊
// 使用核即可
float kernel[9] = float[](
1 / 16.0, 2 / 16.0, 1 / 16.0,
2 / 16.0, 4 / 16.0, 2 / 16.0,
1 / 16.0, 2 / 16.0, 1 / 16.0
);
边缘检测
// 使用核即可
float kernel[9] = float[](
1, 1, 1,
1, -8, 1,
1, 1, 1
);
更多的核
更多的核可以看这里 http://setosa.io/ev/image-kernels/