Frame Buffer

Date:     Updated:

카테고리:

태그:

Main Reference
- Learn OpenGL
- Rinthel Kwon - OpenGL Lecture


Frame Buffer

지금까지 배운 내용들을 종합해보면, 물체를 렌더링 하기 위해 color 값을 작성하는 color buffer, depth/stencil 테스트를 위한 depth stencil buffer 등을 결합해 최종 화면을 렌더링 했다. 사실 스크린에 그려지는 frame buffer 이외에도 개발자가 직접 frame buffer를 생성하고 활용할 수 있다. 화면에 그려지지 않는 frame buffer라고 하여 off-screen buffer라고도 불리는데 포스트 프로세싱, 그림자 매핑, 환경 매핑, 다중 패스 렌더링 등 여러 방면에서 활용된다.


Example

class Framebuffer {
public:
    static FramebufferUPtr Create(const TexturePtr colorAttachment);
    static void BindToDefault();
    ~Framebuffer();

    const uint32_t Get() const { return m_framebuffer; }
    void Bind() const;
    const TexturePtr GetColorAttachment() const { return m_colorAttachment; }

private:
    Framebuffer() {}
    bool InitWithColorAttachment(const TexturePtr colorAttachment);

    uint32_t m_framebuffer { 0 };
    uint32_t m_depthStencilBuffer { 0 };
    TexturePtr m_colorAttachment;
};

#endif // __FRAMEBUFFER_H__
void Framebuffer::BindToDefault() {
    glBindFramebuffer(GL_FRAMEBUFFER, 0);   
}

void Framebuffer::Bind() const {
    glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
}

bool Framebuffer::InitWithColorAttachment(const TexturePtr colorAttachment) {
    m_colorAttachment = colorAttachment;
    glGenFramebuffers(1, &m_framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); 

    glFramebufferTexture2D(GL_FRAMEBUFFER,             
        GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,        
        colorAttachment->Get(), 0);                     

    glGenRenderbuffers(1, &m_depthStencilBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer);
    glRenderbufferStorage(                                       
        GL_RENDERBUFFER, GL_DEPTH24_STENCIL8,       
        colorAttachment->GetWidth(), colorAttachment->GetHeight());
    glBindRenderbuffer(GL_RENDERBUFFER, 0); 

    glFramebufferRenderbuffer(
        GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
        GL_RENDERBUFFER, m_depthStencilBuffer);

    auto result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (result != GL_FRAMEBUFFER_COMPLETE) {
        SPDLOG_ERROR("failed to create framebuffer: {}", result);
        return false;
    }

    BindToDefault();

    return true;
}

이번 포스팅에서는 가장 간단한 프레임 버퍼를 만들어 볼 것이다. Frame Buffer에 Texture객체를 연동시켜 frame buffer에 렌더링된 결과를 사용하는 예제이다. 프레임 버퍼를 사용하는 과정은 다음과 같다.

  • 프레임 버퍼 객체 생성
  • 렌더 버퍼(color/depth/stencil) 생성
  • 렌더 버퍼들을 프레임 버퍼에 연결
  • 해당 렌더 버퍼의 조합이 프레임 버퍼로 사용 가능한지 확인


프레임 버퍼 객체 생성

glGenFramebuffers(1, &m_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);

glFramebufferTexture2D(GL_FRAMEBUFFER,
    GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 
    colorAttachment->Get(), 0);
  • 다른 버퍼들과 마찬가지로 GenFrameBuffers 로 생성
    • 인자로는 생성할 버퍼 개수, 버퍼 주소를 가리키는 포인터
  • Bind를 해줘야 스크린 버퍼가 아닌 내가 지정한 프레임 버퍼에 렌더링
  • glFramebufferTexture2D : 텍스처를 프레임 버퍼 객체에 붙이기(attachment)
    • GL_FRAMEBUFFER : 바인딩한 프레임 버퍼
    • GL_COLOR_ATTACHMENT0 : color/depth/stencil 등 여러 버퍼 중 color값 붙임
    • Texture 포맷, 포인터, Mipmap level


void Texture::SetTextureFormat(int width, int height, uint32_t format) {
    m_width = width;
    m_height = height;
    m_format = format;
    
    glTexImage2D(GL_TEXTURE_2D, 0, m_format,
        m_width, m_height, 0,
        m_format, GL_UNSIGNED_BYTE,
        nullptr);
}
  • 참고로 프레임 버퍼에 붙이는 텍스처는 데이터가 null로 채워진 빈 텍스처 사용
  • Warp나 Filter에 관한 설정도 필요 없음


렌더 버퍼 생성

glGenRenderbuffers(1, &m_depthStencilBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer);
glRenderbufferStorage(                                      
    GL_RENDERBUFFER, GL_DEPTH24_STENCIL8,       
    colorAttachment->GetWidth(), colorAttachment->GetHeight());
glBindRenderbuffer(GL_RENDERBUFFER, 0);
  • 프레임 버퍼에서 사용할 렌더 버퍼 생성
  • 마찬가지로 버퍼 생성 & 바인딩은 기본
  • glRenderbufferStorage : 렌더 버퍼 포맷 세팅
    • GL_DEPTH24_STENCIL8 : 32bit중 24bit는 depth, 8bit는 stencil
  • glBindRenderbuffer(GL_RENDERBUFFER, 0); : 바인딩된 렌더 버퍼 언바인딩
    • 0은 디폴트 버퍼로 다시 바인딩한다는 의미인데, 일반적으로 스크린 버퍼
    • 원래 렌더링 할 때마다 바인딩을 해줘서 없어도 될 것 같은데..
    • 렌더 버퍼는 이상한 값이 추가되어도 눈으로 확인이 불가능하니 안전성을 높이기 위해 언바인딩 해주는듯..


프레임 버퍼에 연결


glFramebufferRenderbuffer(
    GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
    GL_RENDERBUFFER, m_depthStencilBuffer);

// definition
void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
  • glFramebufferRenderbuffer : 프레임 버퍼 객체에 렌더 버퍼를 첨부(attach)


결과 체크

auto result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (result != GL_FRAMEBUFFER_COMPLETE) {
    SPDLOG_ERROR("failed to create framebuffer: {}", result);
    return false;
}
  • 프레임 버퍼는 마음대로 만들 수 없고, 아래 요구사항을 만족해야 함 (버퍼들의 사이즈 같은건 기본)
    • 최소한 하나의 렌더 버퍼가 첨부
    • 최소한 하나의 color가 첨부
    • 첨부하는 버퍼들은 모두 메모리 할당이 완료되어 잇어야 함
    • 각 버퍼들은 샘플의 갯수가 같아야 함


주의

void Context::Reshape(int width, int height) {
m_width = width;
m_height = height;
glViewport(0, 0, m_width, m_height);

m_framebuffer = Framebuffer::Create(Texture::Create(width, height, GL_RGBA));
}
  • 참고로 window의 크기가 resize되면 프레임 버퍼도 새로 만들어 줘야 한다


Example

// fragment shader
void main() {
    vec4 pixel = texture(tex, texCoord);
    fragColor = vec4(1.0 - pixel.rgb, 1.0);
}


// context.cpp
void Context::Render() {
    // ImGui setting...

    m_framebuffer->Bind();
    
    // rendering...

    Framebuffer::BindToDefault(); // 프레임버퍼 언바인딩

    // 위에 까지는 프레임 버퍼 렌더링
    // 여기서 부터가 진짜 스크린에 렌더링 하는 부분.
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    m_postProgram->Use();
    m_postProgram->SetUniform("transform",
        glm::scale(glm::mat4(1.0f), glm::vec3(2.0f, 2.0f, 1.0f)));
    m_framebuffer->GetColorAttachment()->Bind();
    m_postProgram->SetUniform("tex", 0);
    m_plane->Draw(m_postProgram.get());
}
  • m_postProgram : 색을 반전시키는 shader program
  • 프레임 버퍼에 이것 저것 다 렌더링 하고
  • color/depth/stencil 버퍼 초기화
  • 프레임 버퍼를 텍스처로 갖는 m_plane Draw
  • 즉, 3D처럼 보여도 사실은 2D plane에 texture 씌운거임..


Origin Post Process
011516 011428


// fragment shader
uniform float gamma;

void main() {
  vec4 pixel = texture(tex, texCoord);
  fragColor = vec4(pow(pixel.rgb, vec3(gamma)), 1.0);
}

012622

  • 또한 전체적인 밝기를 조정해주는 gamma 후처리는, frame buffer를 이용한 대표적인 post process



맨 위로 이동하기

OpenGL 카테고리 내 다른 글 보러가기

댓글 남기기