OpenGL framebuffer object

Framebuffer는 rendering의 최종 결과가 기록되는 buffer들의 집합이다.
OpenGL context가 생성되면서 기본적으로 하나의 framebuffer를 생성하여 사용하는데,
이러한 default framebufferwindow-system-provided framebuffer라고 부른다.
반면, 필요 시 임의의 framebuffer를 동적으로 생성하여 사용하는 것이 가능한데,
이렇게 생성된 framebuffer를 application-created framebuffer라고 하며,
일반적으로 framebuffer object (FBO)라고 한다.
FBO는 display 외의 목적으로도 많이 사용된다.
Buffer란 OpenGL이 관리하는 memory 영역을 말한다. (buffer=array=image)
이러한 의미로 보면 FBO는 사실 엄밀히 말해서 buffer는 아니다.
(사실 aggregator의 개념에 더 가깝다.)

FBO는 하나 또는 다수의 buffer들이 attach되어 있는 형태로 존재한다.
(여러 개의 array들에 대한 pointer들을 member로 가지는 구조체(struct)와 같은 개념.)
Default framebuffer에는 color, depth, stencil, accumulation의 용도로 사용되는 buffer들이 attach될 수 있지만, FBO에는 accumulation을 제외한 color, depth, stencil의 용도로 사용되는 buffer들이 attach될 수 있다.
하나의 FBO에는 다수의 attachment point들이 존재하며, 각각의 attachment point마다 하나의 buffer가 attach될 수 있다.
하나의 FBO는 n개(system 마다 다름)의 color attachment points와 각각 한 개씩의 depth attachment point, stencil attachment point를 가진다.
(GL_COLOR_ATTACHMENT0, …, GL_COLOR_ATTACHMENTn, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT)
하나의 FBO에는 최소한 한 개의 color buffer가 attach되어야 한다.
여러 개의 color attachment points를 가지고 있는 이유는 동시에 여러 개의 color buffer에 rendering이 가능하기 때문인데, 이러한 기법을 multiple render targets (MRT)라고 부른다.
Framebuffer attachable buffer는 크게 두 가지 type이 있다.
1. texture object  2. renderbuffer object
Texture object가 render target으로 설정되어 rendering이 수행되는 것을 render-to-texture,
RBO가 render target으로 설정되어 rendering 작업이 수행되는 것을 offscreen rendering이라고 한다.
기본적으로 RBO에 rendering 작업이 완료되었다고 해도 GPU memory 상에만 존재할 뿐 화면에는 보여지지 않기 때문이다.
Texture란 보통의 경우 3D model의 표면을 장식해주는 image의 역할을 수행하지만, 이 외에도 다양한 data를 저장하기 위해 사용될 수 있다.
즉, texture는 GPU에서 사용되는 bulk data를 저장하는 용도로 쓰인다.
(bulk data란 여러 가지 용도로 사용되는 data를 말함.)
Texture는 1D, 2D, 3D 등의 형태로 사용이 가능하며, shader 내에서 값을 읽어서 사용하는 것이 가능하다.
Texture는 GPU memory 상에 독립적으로 동적 할당되어 자유롭게 사용이 가능하지만, RBO는 반드시 FBO에 attach되어 사용되어야 한다.
RBO는 texture와는 달리 반드시 2D 형태로만 사용되어야 하고, 단 한 개의 image 만을 가질 수 있다. (texture에서처럼 mipmaps 사용이 불가능함.)
RBO에 저장된 rendering 결과는 glReadPixels(), glDrawPixels(), glCopyPixels()와 같은 함수들을 이용해서 읽고 쓰는 것이 가능하긴 하지만, shader 내에서는 값을 읽어오는 것이 불가능하므로 texture source 등의 용도로 사용할 수 없다. 만약 shader 내에서 특정 image의 값을 읽어올 필요가 있다면 texture object로 만들어서 사용해야 한다.
만약 shader에서 값을 읽어서 사용하지 않는다면 성능상 texture object 보다는 RBO type을 사용하는 것이 더 좋다.
보통의 경우 depth test가 필요할 경우 depth buffer를 RBO type으로 생한한 후 해당 FBO의 GL_DEPTH_ATTACHMENT에 attach해서 사용하지만, 만약 fragment shader에서 post processing을 수행할 때 depth 값이 필요하다면 texture object type으로 생성한 후 해당 FBO의 GL_DEPTH_ATTACHMENT에 attach해서 사용해야 한다.
Texture object를 생성하는 방법은 일반적으로 texture를 사용하는 경우와 동일하다.
다만 image data를 load하여 연결해줄 필요가 없으므로 glTexImage2D() 함수에서 맨 마지막 인자에는 NULL을 써주면 된다.

다음은 FBO를 한 개 생성한 후, depth buffer로 사용될 texture object를 생성하여 attach하는 예제이다.


// framebuffer object
GLuint fboId;
glGenFramebuffers( 1, &fboId );
glBindFramebuffer( GL_FRAMEBUFFER, fboId );

// texture buffer object to be used for depth buffer
GLuint depthTexId;
glGenTextures( 1, &depthTexId );
glBindTexture( GL_TEXTURE_2D, depthTexId );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width, height, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );

// attachment
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE2D, depthTexId, 0 );

다음은 FBO를 한 개 생성한 후, depth buffer로 사용될 renderbuffer object를 생성하여 attach하는 예제이다.


// framebuffer object
GLuint fboId;
glGenFramebuffers( 1, &fboId );
glBindFramebuffer( GL_FRAMEBUFFER, fboId );

// renderbuffer object to be used for depth buffer
GLuint depthRboId;
glGenRenderbuffers( 1, &depthRboId );
glBindRenderBuffer( GL_RENDERBUFFER, depthRboId );
glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height );

// attachment
glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRboId );

답글 남기기