[OpenGL]帧缓冲

文章目录[x]
  1. 1:设置帧缓冲
  2. 2:使用帧缓冲
  3. 3:反相(Inversion)
  4. 4:灰度(Grayscale)
  5. 5:核效果
  6. 6:高斯模糊
  7. 7:边缘检测
  8. 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);
}

FrameBuffer

可以看到,虽然看起来效果没差。但是当开启线框绘制后,明显看到这其实是一张Texture而已。(类似Unity中的RenderTexture)

利用帧缓冲,我们就可以干很多事,比如后处理(post-procoessing)

 

反相(Inversion)


// Fragment Shader
void main()
{
    FragColor = vec4(1 - vec3(texture(screenTexture, iTexCoords)), 1.0);
}

 

image inversion

 

灰度(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);
}

 

image grayscale

 

核效果


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);
}

image kernel

 

高斯模糊


// 使用核即可
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
        );

blur

 

边缘检测


// 使用核即可
float kernel[9] = float[](
        1, 1, 1,
        1, -8, 1,
        1, 1, 1
        );

edge-detection

 

 

更多的核


更多的核可以看这里 http://setosa.io/ev/image-kernels/

 

 

点赞

发表评论

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像