Frame Buffer
카테고리: OpenGL
태그: Graphics
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 |
---|---|
// fragment shader
uniform float gamma;
void main() {
vec4 pixel = texture(tex, texCoord);
fragColor = vec4(pow(pixel.rgb, vec3(gamma)), 1.0);
}
- 또한 전체적인 밝기를 조정해주는 gamma 후처리는, frame buffer를 이용한 대표적인 post process
댓글 남기기