상세 컨텐츠

본문 제목

OpenGL FBO (FrameBuffer Object) 사용하기

프로그래밍 관련/3D,2D DRAW 관련

by AlrepondTech 2017. 8. 18. 16:22

본문

반응형

 

 

 

=======================

=======================

=======================

 

 

 

OpenGL을 쓰고 항시 API를 쓰고 다시 해제해주어야 하는 API들은 는 해제 해주는것을 잊지 마세요 

예로들어 wglMakeCurrent(...)로 들자면 

 

HDC   hdc    = getHDC();

HGLRC hglrc = getHGLRCRES_GL();

 

::wglMakeCurrent(hdc, hglrc); //사용

 
//-----------------------------------
//{랜더링 코드들
 
..................................................................
 
//}
//-----------------------------------

::wglMakeCurrent(NULL, NULL);  //::wglMakeCurrent(hdc, NULL);  //해제

 

 

위와같이 wglMakeCurrent(...) 랜더링 부분에 Api 사용과 해제 부분을 제대로 하지 않으면 다른 

위치의 wglMakeCurrent(...) 를 사용시 문제가 생길수 있습니다.

 

 

 

 

 

=======================

=======================

=======================

 

 

 

 

출처: http://iskim3068.tistory.com/21

 

 

FBO에 대해 알아봅시다~

 

 

 

우선 버퍼(buffer)가 무엇인지 알아보자

컴퓨팅에서, 버퍼(buffer, 문화어: 완충기억기)는 데이터를 한 곳에서 다른 한 곳으로 전송하는 동안 일시적으로 그 데이터를 보관하는 메모리의 영역이다. 버퍼링(buffering)이란 버퍼를 활용하는 방식 또는 버퍼를 채우는 동작을 말한다. 다른 말로 '큐(Queue)'라고도 표현한다.

보통 데이터는 키보드와 같은 입력 장치로부터 받거나 프린터와 같은 출력 장치로 내보낼 때 버퍼 안에 저장된다. 그러나 버퍼는 컴퓨터 안의 프로세스 사이에서 데이터를 이동시킬 때 사용된다. 이는 전자 통신의 버퍼와 비유할 수 있다. 버퍼는 하드웨어나 소프트웨어에 추가될 수 있지만 버퍼는 상당수가 소프트웨어에 추가된다. 버퍼는 보통 속도가 계속 바뀔 수 있으므로 데이터 수신, 처리 속도에 차이가 있다. (예: 프린터 스풀러)

버퍼는 네트워크 상에서 자료를 주고 받을 때나 스피커에 소리를 재생할 때, 또는 디스크 드라이브와 같은 하드웨어의 입출력을 결합하는 데에 자주 이용된다. 버퍼는 또한 순서대로 데이터를 출력하는 FIFO 방식에서 보통 사용된다.

 

-위키백과-

 

 

프레임 버퍼(frame buffer) 란

 

래스터 주사 방식에서 화면에 나타날 영상 정보를 일시적으로 저장하는 기억 장치.

그래픽 프로세서가 중앙 처리 장치(CPU)로부터 도형을 표현하는 디스플레이 리스트를 받아 변환하여 프레임 버퍼에 기록한다. 프레임 버퍼의 각 기억 단위는 화면의 하나의 화소에 하나씩 대응하여 화면에 그대로 반영된다. 즉 화면의 각 점의 온(on)/오프(off)나 색깔을 비트 맵으로 기억하고 있으며 이 기억 장치에 어떤 내용을 써넣으면 그것이 화면에 표시된다. 이는 대개 시스템의 주기억 장치와는 별도로 분리되어 있으며, 특별한 기술을 사용하여 속도를 빠르게 하기도 한다.

 

 

 

FBO 란?( frame buffer object )

OpenGL 렌더링 파이프라인에서 geometry 데이터 및 질감 변화 몇 가지 테스트를 통과 그리고 마지막으로 2D 픽셀 화면에 렌더링. 

OpenGL 파이프라인의 최종 렌더링 대상 프레임 버퍼를라고 합니다. 프레임 버퍼는 2D 배열 또는 스토리지 OpenGL;에 의해 활용 모음 색상 버퍼, 깊이 버퍼, 스텐실 버퍼와 누적 버퍼. 기본적으로 OpenGL 프레임 버퍼를 사용 하 여 생성 되 고 전적으로 윈도우 시스템에 의해 관리 되는 렌더링 대상으로. 이 기본 프레임 버퍼 창 시스템 제공 버퍼를 라고 합니다.OpenGL 확장 GL_ARB_framebuffer_object 추가 비 표시할 수 있는 프레임 버퍼 개체 (FBO)를 만들 수 있는 인터페이스를 제공 합니다. 이 버퍼 기본 윈도우 시스템 제공 버퍼에서 구분 하기 위해 응용 프로그램 생성 프레임 버퍼를 라고 합니다. 프레임 버퍼 개체 (FBO)를 사용 하 여 OpenGL 응용 프로그램 전통적인 창 시스템 제공 버퍼 이외 응용 프로그램 만든 프레임 버퍼 개체 (FBO) 렌더링 출력을 리디렉션할 수 있습니다. 그리고, 그것은 완전히 OpenGL에 의해 제어 됩니다.창 시스템 제공 버퍼와 마찬가지로 FBO 목적지; 렌더링의 컬렉션을 포함 색상, 깊이 스텐실 버퍼입니다. (누적 버퍼 FBO에서 정의 되지 않은 참고.) 이러한 논리는 FBO 버퍼는 프레임 버퍼 개체를 연결할 수 있는 픽셀의 2 차원 배열을 프레임 버퍼를 연결할 수 있는 이미지를 라고 합니다.거기 두 가지 유형의 프레임 버퍼 연결 가능한 이미지; 텍스처 이미지와 renderbuffer 이미지입니다. 텍스처 개체의 이미지를 프레임 버퍼에 연결 하는 경우 OpenGL "텍스처 렌더링"을수행 합니다. Renderbuffer 개체의 이미지를 프레임 버퍼에 연결, OpenGL 수행 "오프 스크린 렌더링"

.

그건 그렇고, renderbuffer 개체 는 GL_ARB_framebuffer_object 확장에 정의 된 저장소 개체의 새로운 유형입니다. 렌더링 프로세스 동안 단일 2D 이미지 렌더링 대상으로 사용 됩니다.


FBO, 텍스처와 Renderbuffer 사이의 연결

다음 다이어그램에서는 프레임 버퍼 개체, 텍스처 개체 renderbuffer 개체 간의 연결을 보여 줍니다. 여러 질감 개체 또는 renderbuffer 개체는 첨부 파일 포인트를 통해 프레임 개체에 첨부할 수 있습니다.여러 색상 첨부 파일 포인트 (GL_COLOR_ATTACHMENT0,..., GL_COLOR_ATTACHMENTn), 하나의 깊이 첨부 파일 점 (GL_DEPTH_ATTACHMENT), 및 1 개의 스텐실 첨부 파일 (GL_STENCIL_ATTACHMENT) 프레임 버퍼 개체에. 컬러 첨부 파일 포인트 수 구현 종속, 하지만 각 FBO 적어도 하나의 색상 부속품 지점이 있어야 합니다. 그래픽 카드에서 지 원하는 GL_MAX_COLOR_ATTACHMENTS와 컬러 부속품 포인트의 최대 수를 쿼리할 수 있습니다. 이유는 FBO 여러 컬러 부속품 포인트 동시에 여러 대상 색상 버퍼를 렌더링 하는 데 허용 됩니다. GL_ARB_draw_buffers 확장에 의해이 "여러 렌더링 대상" (MRT)을 수행할 수 있습니다. 프레임 버퍼 개체 자체는, 그러나, 그것은 어떤 이미지 storage(array) 통지만 여러 첨부 파일 포인트를 했다.프레임 버퍼 개체 (FBO)를 효율적인 스위칭 메커니즘; 이전 프레임 버퍼를 연결할 수 있는 이미지는 FBO에서 분리 하 고 FBO를 새로운 프레임 버퍼를 연결할 수 있는 이미지를 첨부. FBOs 사이 전환 보다 훨씬 빠릅니다 프레임 버퍼를 연결할 수 있는 이미지를 전환 합니다. FBO glFramebufferTexture2D() 2D 텍스처 개체 및 glFramebufferRenderbuffer() renderbuffer 개체를 전환 스위치를 제공 합니다.

 

 

 

 

참조 : http://www.songho.ca

 



출처: http://iskim3068.tistory.com/21 [LausdeoF]

 

 

 

 

=======================

=======================

=======================

 

 

 

출처: http://diehard98.tistory.com/m/entry/FBO-FrameBuffer-Object-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

 

 

 

FBO는 OpenGL에서 사용하는 '외부에 그리기' 기술중 하나로써 나도 종종 사용한다. FBO를 사용함으로써 사용자는 다양한 작업이 가능해 진다. 

 
대표적인 FBO 사용예로 그려질 scene 내부에 또 다른 scene을 그려야 할 때다. 집안에 있는 TV를 표현할 때 종종 이 방법이 사용된다. FBO의 장점으로는 간단한 셋업과 사용 그리고 context switching이 필요없다는 점 (오버헤드 적음) 각종 버퍼 (depth buffer, stencil buffer, accumulation buffer) 등을 사용할 수 있어 유용하다는 점 등이다. 
 
 
1. 셋업
 
우선 다른 OpenGL 객체와 마찬가지로 FBO 핸들을 받을 변수를 선언해야 하고 그 변수에 핸들을 생성해서 대입해야 한다. glGenFramebuffersEXT() 함수의 첫번째 인자는 glGenTextures() 함수의 첫번째 인자처럼 몇개나 만들건지 지정해주는 인자다.
GLuint fbo; glGenFramebuffersEXT(1, &fbo);
명령을 수행하려면 바인딩 부터 해야 한다. 바인딩을 함으로써 다음에 이어지는 명령들이 바인딩한 객체에 대해 수행되도록 한다.
 
 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
첫번째 인자가 Target 인자 인데 GL_FRAMEBUFFER_EXT는 고정된 것이라고 보면 된다. 물론 나중에 확장이 되면 다른 형태가 추가 될수 있지만 현재로서는 저 인자가 고정이라고 보면 된다. 두번째 인자로 앞서 정의한 핸들을 넘겨주면 오케이.
 
2. 깊이 버퍼 (Depth buffer) 추가
 
FBO 객체 자체로는 할 수 있는게 없다. 왜냐면 걍 객체(식별표)이니까... 이제 이 식별표에 실제적인 저장 공간(버퍼)들을 추가해 줘야 한다. 알다시피 OpenGL이 기본적으로 제공하는 Frame buffer도 깊이 버퍼, 스텐실 버퍼 등등으로 구성되어 있다. 고로 FBO 객체에도 Renderable (렌더링 가능한) 버퍼를 추가 시켜 주어야 한다. 이 버퍼로 사용가능한 것이 텍스쳐 또는 렌더버퍼 중 하나다. 
 
렌더버퍼는 FBO의 offscreen rendering을 지원하기 위한 객체이다. 적절한 텍스쳐 포멧을 지원하지 않는 스텐실 버퍼나 깊이 버퍼를 표현하기 위해서 사용하는 것이 바로 렌더버퍼 객체다. 이 예제에서는 렌더버퍼를 FBO의 깊이 버퍼로 사용할 것이다. 
 
그러니까 요약하자면, FBO는 OpenGL이 기본적으로 가지고 있는 깊이 버퍼, 스텐실 버퍼, 색상 버퍼등을 '연결'할 수 있는 객체인데 이때 이 연결 가능한 놈들은 두 종류로써 하나는 일반 텍스쳐이고 다른 하나는 '렌더버퍼(Renderbuffer)'이다. 일반 텍스쳐의 경우 색상 버퍼로 사용할 수 있으나 깊이 버퍼나 스텐실 버퍼와 같이 텍스쳐로 표현할 수 없는 버퍼들은 렌더버퍼를 이용해서 FBO에 연결한다는 말인듯 하다. 
 
아무튼, 일단 렌더버퍼를 생성하는 방법은 다음과 같다.
GLuint depthbuffer; glGenRenderbuffersEXT(1, &depthbuffer);
depthbuffer라는 변수 (핸들을 받기위한)를 선언하고 generation 함수를 통해 렌더버퍼를 생성해 낸다. (그래서 함수 이름도 glGenRenderbuffersEXT() 이다. 
 
이제 이렇게 만든 렌더버퍼를 바인딩 시킴으로써 다음에 이어지는 일련의 명령이 depthbuffer 렌더버퍼에 대해 실행되도록 한다.
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);
앞서 본 것과 마찬가지로 GL_RENDERBUFFER_EXT는 고정이라고 보면 된다. 그리고 두번째 인자로 앞서 만든 렌더버퍼의 핸들을 넘겨준다. 
 
자, 이제 해야 할 일은 이 깊이 버퍼의 크기를 지정해 주는 일이다. (폭과 높이 지정하기)
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT,                           width, height);
이 과정은 일종의 메모리 할당이라고 보면 되겠네. 한가지 알아둘 것은 이 렌더버퍼는 기본 RGB/RGBA 저장용으로 사용가능하며 스텐실 버퍼로써 연결도 가능하단다. RGB/RGBA 저장 공간으로 사용될 수 있다는 점은 조금 신선하긴 하다만 텍스쳐를 사용하는 것이 더 유연할 것이라 생각된다.
 
다음은 이렇게 실제적으로 메모리를 할당한 깊이 렌더버퍼를 우리가 앞서 만든 fbo 객체에 가져다 붙이는 것이다. 
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,                               GL_RENDERBUFFER_EXT, depthbuffer);
위 명령이 좀 복잡해 보여도 실은 단순하다. 현재 바인딩 되어 있는 FBO 객체 (fbo)에 렌더버퍼 (depthbuffer)를 바인딩 하라.. 라는 말이다.
 
 
3. 렌더링 텍스쳐 추가하기
 
 
앞서 두 단계를 수행했지만 아직까지 FBO에 색상 버퍼를 추가하지 않았다. 이제 색상 버퍼를 추가 해야 하는데 이 색상 버퍼가 사실상 '내 눈에 보여질' 놈이다. 
 
이 색상 버퍼를 추가하는 방법은 두가지가 있다.
1. 색상 렌더버퍼를 FBO의 색상버퍼로 연결
2. 일반 텍스쳐를 FBO의 색상버퍼로 연결
 
첫번째 방법도 사용하기는 하지만 우리는 두번째 방법을 사용할 것이다. (앞서 2번에서 설명했듯이 렌더버퍼는 기본 RGB/RGBA를 저장하기 위한 버퍼로도 사용가능하다고 했다. 하지만 분명 일반 텍스쳐 보다는 범용성이 떨어질 듯하다.)
 
텍스쳐를 FBO에 연결하기 위해서는 당연히 텍스쳐 생성부터 해야 한다.
GLuint img; glGenTextures(1, &img); glBindTexture(GL_TEXTURE_2D, img); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  width, height, 0, GL_RGBA,               GL_UNSIGNED_BYTE, NULL);
이 과정은 일반적인 텍스쳐 생성 과정이다. 이때 주의 해야 할 한 가지 포인트는 폭과 넓이가 앞서 만든 깊이 렌더버퍼의 크기와 같아야 한다는 매우 상식적인 것이다. 
 
자, 이제 색상 버퍼로 사용될 텍스쳐를 만들었으므로 우리가 앞서 만든 FBO 객체에 연결하자.
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,                            GL_TEXTURE_2D, img, 0);
이 명령이 어렵게 보여도 실제로는 그닥 어렵지 않다. 우선 굵은 글자로 표시된 GL_COLOR_ATTACHMENT0_EXT가 좀 어렵게 보일지 모르나 이것은 OpenGL에게 어떤 color attachment point에 연결할 지를 명시해주는 인자일 뿐이다. FBO는 동시에 여러개의 색상 버퍼를 가질 수 있기 때문에 어떤 포인트에 연결할 지를 명시해야 한다. 하나의 색상 버퍼만 사용한다면 저 숫자 0은 당신에게 아무런 중요성이 없는 단순한 표식일 뿐이다. 그리고 GL_TEXTURE_2D는 우리가 2D 텍스쳐를 사용한다는 말이고 img는 사용할 텍스쳐의 핸들, 그리고 마지막 0은 mipmap level 이다. 
 
이제 FBO 완성을 위해 마지막으로 해야 할 일은 FBO가 잘 만들어 졌는지 체크하는 일이다. 다음 함수로 한방에 체크하자.
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
모든 셋팅이 제대로 통과하였다면 GL_FRAMEBUFFER_COMPLETE_EXT를 리턴할 것이고 만약 에러가 있다면 다른 값을 리턴한다. 
 
4. 텍스쳐로 렌더링하기
 
이제 색상버퍼로 연결시켜 놓은 텍스쳐 (img)로 렌더링을 수행하는 방법을 알아볼 차례. 이건 상당히 쉬운데 걍 glBindFramebufferEXT() 함수만 호출하면 땡이다. 셋업이 조금 어려워서 그렇지 사용 자체는 굉장히 쉽다.
 
FBO로 렌더링 하기 위해서는 glBindFrameBufferEXT()를 호출하면 되고 렌더링을 멈추기 위해서는 같은 함수를 호출하되 인자값을 0으로 주면 된다.
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); // FBO 바인딩 glPushAttrib(GL_VIEWPORT_BIT);                       glViewport(0,0,width, height);                 // 여기서 부터는 텍스쳐에 렌더링한다.
   // Render as normal here // output goes to the FBO and it’s attached buffers  glPopAttrib(); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);   // 언바인딩. FBO에 렌더링하기 중지
자, FBO를 바인딩함으로써 차후 수행되는 모든 그리기 명령은 우리가 설정한 FBO의 색상 버퍼, 깊이 버퍼, 스텐실 버퍼에 적용된다. 그리고 glPushAttrib() 와 glPopAttrib() 함수를 이용해 이전의 3차원 매트릭스 값들을 신속히 복구 및 적용할 수 있게 했다. 
 
중요한 것은, 다시한번 강조하지만, glBindFramebufferEXT() 함수 하나로 간단하게 FBO로 렌더링을 할 수 있다는 점이다. 앞서 셋업에서 수행했던 몇몇 버퍼 바인딩 및 연결과정은 더이상 필요없다. 
 
5. 렌더링 된 FBO의 색상 버퍼를 텍스쳐로 사용하기
 
이제 우리의 궁극적인 목표에 가까워 졌다. 왜 우리는 FBO에 렌더링을 수행했던가? 바로 이 단계를 위해서다. FBO 색상 버퍼에 그려진 그림은 곧바로 텍스쳐로 사용 가능하다. 즉, 집안에 있는 TV 속의 화면을 우리는 방금 FBO를 이용해서 그려낸 것이다. 이제 TV 안의 화면은 그렸으니 집을 그릴때 만들어 놓은 TV 속의 화면을 텍스쳐로 입히자.
 
알다시피 img 핸들에 FBO의 색상 버퍼가 연결되어 있으므로 이 텍스쳐를 사용하기 위해서는 우선 바인딩 부터 한다.
glBindTexture(GL_TEXTURE_2D, img);
그리고 FBO 밉맵 자동 생성 함수인 glGenerateMipmapEXT() 함수를 호출하자.
glGenerateMipmapEXT(GL_TEXTURE_2D);
한가지 주의할 것은 만약 내가 임의의 밉맵 필터 (GL_LINEAR_MIPMAP_LINEAR와 같은)를 사용할 것이라면 FBO에 연결될 색상 버퍼 생성시 반드시 glGenerateMipmapEXT() 함수를 호출해서 가능한지 여부를 체크해야 한다. 
 
이 경우, 아래의 명령대로 FBO의 색상버퍼를 생성하면 된다.
glGenTextures(1, &img); glBindTexture(GL_TEXTURE_2D, img); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  width, height, 0, GL_RGBA,               GL_UNSIGNED_BYTE, NULL); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glGenerateMipmapEXT(GL_TEXTURE_2D);
img를 바인딩 했다면 이제 할 일은 바인딩한 텍스쳐를 원하는 곳에 그려넣기인데 소스 코드를 보면서 설명해 보면...
 
 
void display(void)   
{
// FBO 바인딩 - FBO에 렌더링 하겠다고 알려줌
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
 
// 각종 화면 관련 속성 저장해 놓기 - FBO에 렌더링 끝나면 이전값으로 복귀하기 위해서
glPushAttrib(GL_VIEWPORT_BIT);
glViewport(0,0,width,height);
 
// 이제 부터 회전하는 작은 큐브를 FBO에 그린다.
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
 
glTranslatef(0.0f,0.0f,-2.0f);
glRotatef(xrot,1.0f,0.0f,0.0f);
glRotatef(yrot,0.0f,1.0f,0.0f);
 
glBegin(GL_QUADS);
// Front Face
glColor4f(0.0f,1.0f,0.0f,1.0f);
glVertex3f(-0.5f, -0.5,  0.5);
glVertex3f( 0.5, -0.5,  0.5);
glVertex3f( 0.5,  0.5,  0.5);
glVertex3f(-0.5,  0.5,  0.5);
// Back Face
glColor4f(1.0f,0.0f,0.0f,1.0f);
glVertex3f(-0.5, -0.5, -0.5);
glVertex3f(-0.5,  0.5, -0.5);
glVertex3f( 0.5,  0.5, -0.5);
glVertex3f( 0.5, -0.5, -0.5);
// Top Face
glColor4f(0.0f,0.0f,1.0f,1.0f);
glVertex3f(-0.5,  0.5, -0.5);
glVertex3f(-0.5,  0.5,  0.5);
glVertex3f( 0.5,  0.5,  0.5);
glVertex3f( 0.5,  0.5, -0.5);
// Bottom Face
glColor4f(0.0f,1.0f,1.0f,1.0f);
glVertex3f(-0.5, -0.5, -0.5);
glVertex3f( 0.5, -0.5, -0.5);
glVertex3f( 0.5, -0.5,  0.5);
glVertex3f(-0.5, -0.5,  0.5);
// Right face
glColor4f(1.0f,1.0f,0.0f,1.0f);
glVertex3f( 0.5, -0.5, -0.5);
glVertex3f( 0.5,  0.5, -0.5);
glVertex3f( 0.5,  0.5,  0.5);
glVertex3f( 0.5, -0.5,  0.5);
// Left Face
glColor4f(1.0f,1.0f,1.0f,1.0f);
glVertex3f(-0.5, -0.5, -0.5);
glVertex3f(-0.5, -0.5,  0.5);
glVertex3f(-0.5,  0.5,  0.5);
glVertex3f(-0.5,  0.5, -0.5);
glEnd();
 
// 앞서 저장했던 화면 관련 값을 복구하고 FBO 사용 해제
glPopAttrib();
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

        // 이제부터 그리는 것들은 스크린에 보여질 직접적인 것들
glClearColor(0.0f, 0.0f, 0.2f, 0.5f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// 여기서 FBO에 그려진 회전하는 규브 (색상 버퍼)를 바인딩한다
glBindTexture(GL_TEXTURE_2D, img);
// 여기서는 밉맵 옵션이 사용되지 않지만 만약 당신이 밉맵을 사용한다면 아래의 주석을 제거하고 텍스쳐 생성시에 
// glGenerateMipmapEXT() 함수를 호출하라.
// glGenerateMipmapEXT(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_2D);
 
glTranslatef(0.0f,0.0f,-2.0f);
glRotatef(-xrot,1.0f,0.0f,0.0f);
glRotatef(-yrot,0.0f,1.0f,0.0f);
 
glColor4f(1.0f,1.0f,1.0f,1.0f);
 
// 이제 FBO의 색상 버퍼를 텍스쳐로 사용하는 또다른 규브 그리기
glBegin(GL_QUADS);
// Front Face
glNormal3f( 0.0f, 0.0f, 1.0);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.5f, -0.5,  0.5);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 0.5, -0.5,  0.5);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 0.5,  0.5,  0.5);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5,  0.5,  0.5);
// Back Face
glNormal3f( 0.0f, 0.0f,-1.0);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-0.5, -0.5, -0.5);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-0.5,  0.5, -0.5);
glTexCoord2f(0.0f, 1.0f); glVertex3f( 0.5,  0.5, -0.5);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 0.5, -0.5, -0.5);
// Top Face
glNormal3f( 0.0f, 1.0, 0.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.5,  0.5, -0.5);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5,  0.5,  0.5);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 0.5,  0.5,  0.5);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 0.5,  0.5, -0.5);
// Bottom Face
glNormal3f( 0.0f,-1.0, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-0.5, -0.5, -0.5);
glTexCoord2f(0.0f, 1.0f); glVertex3f( 0.5, -0.5, -0.5);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 0.5, -0.5,  0.5);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-0.5, -0.5,  0.5);
// Right face
glNormal3f( 1.0, 0.0f, 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 0.5, -0.5, -0.5);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 0.5,  0.5, -0.5);
glTexCoord2f(0.0f, 1.0f); glVertex3f( 0.5,  0.5,  0.5);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 0.5, -0.5,  0.5);
// Left Face
glNormal3f(-1.0, 0.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5, -0.5, -0.5);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-0.5, -0.5,  0.5);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-0.5,  0.5,  0.5);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.5,  0.5, -0.5);
glEnd();
 
glDisable(GL_TEXTURE_2D);
 
xrot+=xspeed;
yrot+=yspeed;
 
glutSwapBuffers ( );
// 버퍼 스왑 (출력하기)
}
 
 
위 프로그램을 돌려보면 일단 두개의 큐브가 나오는데 바깥쪽의 큰 큐브의 각 면은 FBO에 그려진 또 다른 회전하는 작은 큐브를 끊임없이 보여준다. (아래 그림 처럼)
 

 

6. 치우기
 
다 사용했다면 프로그램 종료전에 치우기를 수행해야 한다. (메모리 해제) 뭐 이것도 간단하다.
glDeleteFramebuffersEXT(1, &fbo);
위 명령은 fbo 를 삭제하라는 명령이고 앞서 만든 렌더버퍼를 날려보내기 위해서는 다음 명령을 수행한다.
glDeleteRenderbuffersEXT(1, &depthbuffer);
뭐 가운데 한 단어만 다를뿐 같은 명령이다.
 
마지막으로 글쓴이는 몇가지 당부를 하고 있는데, FBO를 생성했다 지웠다를 프로그램 실행중 반복하지 말라는 부탁과 렌더링된 FBO의 색상 버퍼를 glTexImage 함수를 이용해서 변경하거나 추가하는 행위를 피하는 것이 좋다고 한다. 이는 당연한 이야기로 이렇게 하면 Performance가 나빠질 것은 불을 보듯 뻔하다.
 
그리고 ATI를 사용하는 사람들에게는 위 예제가 제대로 동작하지 않을 수도 있단다. ATI 드라이버를 사용할때  FBO의 깊이 버퍼를 렌더버퍼로 연결하는 과정에 버그가 있다고 하는데 아마도 지금이 다 고쳐 졌겠지? 
 
소스 파일을 글에 첨부하였는데 Visual Studio 솔루션 파일이고 추가로 필요한 프로그램은 FreeGLUT이다. 이 FreeGLUT는 http://freeglut.sourceforge.net/에 가면 다운 받을수 있고 압축을 푼후 VisualStudio2008 폴더에 있는 프로젝트 파일을 열어서 컴파일 하게 되면 freeglut.lib와 freeglut.dll 파일이 생성된다. freeglut.dll은 윈도우즈 폴더에 System32 폴더안에 던져 넣어 버리고 lib 파일은 FBO_Example 프로젝트 폴더 안에 넣거나 FBO_Example/Release 폴더 및 FBO_Example/Debug 폴더에 던져 넣어버리면 문제 없이 컴파일 될것이다. 

 

 

 

 

=======================

=======================

=======================

 

 

 

출처: https://www.khronos.org/opengl/wiki/Framebuffer_Object_Extension_Examples

 

 

This page describes old functionality. You should use the core Framebuffer Object functionality if at all possible.RTT = render_to_textureThis page shows a few examples on how to setup a RTT and how to cleanup.The extension specification is at http://www.opengl.org/registry/specs/EXT/framebuffer_object.txtNote that this extension became core in GL 3.0 and at the same time they released the ARB version of the extensionhttp://www.opengl.org/registry/specs/ARB/framebuffer_object.txtGL_ARB_framebuffer_object brings together GL_EXT_framebuffer_object, GL_EXT_framebuffer_blit, GL_EXT_framebuffer_multisample, GL_EXT_packed_depth_stencil which are all folded into the core of GL 3.0.

 

 

 

 

Quick example, render_to_texture (2D)

Let's assume we want to render to a texture and we also want depth testing to take place. We need to create a color texture and we need to attach it to the FBO. We need a depth buffer RenderBuffer and attach it to the FBO. Once you are done rendering to this texture, you can use it like any other texture. In this case, we don't care what happens to the depth values. If you want to access the depth (for example, from within your shader), you need to make a depth texture instead of a depth buffer RenderBuffer. Please look at the other examples. Also, keep in mind we are using the GL_RGBA8 format here which is a format supported by all GPUs.

   //RGBA8 2D texture, 24 bit depth texture, 256x256    glGenTextures(1, &color_tex);    glBindTexture(GL_TEXTURE_2D, color_tex);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);    //NULL means reserve texture memory, but texels are undefined    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);    //-------------------------    glGenFramebuffersEXT(1, &fb);    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);    //Attach 2D texture to this FBO    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, color_tex, 0);    //-------------------------    glGenRenderbuffersEXT(1, &depth_rb);    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_rb);    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, 256, 256);    //-------------------------    //Attach depth buffer to FBO    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_rb);    //-------------------------    //Does the GPU support current FBO configuration?    GLenum status;    status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);    switch(status)    {       case GL_FRAMEBUFFER_COMPLETE_EXT:       cout<<"good";    default:       HANDLE_THE_ERROR;    }    //-------------------------    //and now you can render to GL_TEXTURE_2D    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);    glClearColor(0.0, 0.0, 0.0, 0.0);    glClearDepth(1.0f);    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    //-------------------------    glViewport(0, 0, 256, 256);    glMatrixMode(GL_PROJECTION);    glLoadIdentity();    glOrtho(0.0, 256.0, 0.0, 256.0, -1.0, 1.0);     glMatrixMode(GL_MODELVIEW);    glLoadIdentity();    //-------------------------    glDisable(GL_TEXTURE_2D);    glDisable(GL_BLEND);    glEnable(GL_DEPTH_TEST);    //-------------------------    //**************************    //RenderATriangle, {0.0, 0.0}, {256.0, 0.0}, {256.0, 256.0}    //Read http://www.opengl.org/wiki/VBO_-_just_examples    RenderATriangle();    //-------------------------    GLubyte pixels[4*4*4];    glReadPixels(0, 0, 4, 4, GL_BGRA, GL_UNSIGNED_BYTE, pixels);    //pixels 0, 1, 2 should be white    //pixel 4 should be black    //----------------    //Bind 0, which means render to back buffer    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 

And in the end, cleanup

   //Delete resources    glDeleteTextures(1, &color_tex);    glDeleteRenderbuffersEXT(1, &depth_rb);    //Bind 0, which means render to back buffer, as a result, fb is unbound    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);    glDeleteFramebuffersEXT(1, &fb); 

Quick example, render_to_texture (2D), mipmaps

This example is nearly identical to the above sample code with one difference : glGenerateMipmapEXT is used to generate the mipmaps. You can use it to generate mipmaps whenever you want. Generally, you render to the texture, then unbind the FBO with glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0), then bind the texture with glBindTexture, then call glGenerateMipmapEXT. ALSO, notice that glGenerateMipmapEXT doesn't have an "s".

   //RGBA8 2D texture, 24 bit depth texture, 256x256    glGenTextures(1, &color_tex);    glBindTexture(GL_TEXTURE_2D, color_tex);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);    //NULL means reserve texture memory, but texels are undefined    //**** Tell OpenGL to reserve level 0    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);    //You must reserve memory for other mipmaps levels as well either by making a series of calls to    //glTexImage2D or use glGenerateMipmapEXT(GL_TEXTURE_2D).    //Here, we'll use :    glGenerateMipmapEXT(GL_TEXTURE_2D)    //-------------------------    glGenFramebuffersEXT(1, &fb);    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);    //Attach 2D texture to this FBO    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, color_tex, 0);    //-------------------------    glGenRenderbuffersEXT(1, &depth_rb);    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_rb);    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, 256, 256);    //-------------------------    //Attach depth buffer to FBO    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_rb);    //-------------------------    //Does the GPU support current FBO configuration?    GLenum status;    status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);    switch(status)    {       case GL_FRAMEBUFFER_COMPLETE_EXT:       cout<<"good";    default:       HANDLE_THE_ERROR;    }    //-------------------------    //and now you can render to GL_TEXTURE_2D    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);    glClearColor(0.0, 0.0, 0.0, 0.0);    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    //-------------------------    glViewport(0, 0, 256, 256);    glMatrixMode(GL_PROJECTION);    glLoadIdentity();    glOrtho(0.0, 256.0, 0.0, 256.0, -1.0, 1.0);     glMatrixMode(GL_MODELVIEW);    glLoadIdentity();    //-------------------------    glDisable(GL_TEXTURE_2D);    glDisable(GL_BLEND);    glEnable(GL_DEPTH_TEST);    //-------------------------    //**************************    //RenderATriangle, {0.0, 0.0}, {256.0, 0.0}, {256.0, 256.0}    //Read http://www.opengl.org/wiki/VBO_-_just_examples    RenderATriangle();    //-------------------------    GLubyte pixels[4*4*4];    glReadPixels(0, 0, 4, 4, GL_BGRA, GL_UNSIGNED_BYTE, pixels);    //pixels 0, 1, 2 should be white    //pixel 4 should be black    //----------------    //Bind 0, which means render to back buffer    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);    //----------------    //**** Now that we rendered to level 0 of the texture, we must generate the mipmaps.    //This should be quick since it is done on the GPU.    glBindTexture(GL_TEXTURE_2D, color_tex);    glGenerateMipmapEXT(GL_TEXTURE_2D) 

And in the end, cleanup

   //Delete resources    glDeleteTextures(1, &color_tex);    glDeleteRenderbuffersEXT(1, &depth_rb);    //Bind 0, which means render to back buffer, as a result, fb is unbound    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);    glDeleteFramebuffersEXT(1, &fb); 

Quick example, render_to_texture (Cubemap)

In case you want to have dynamic reflections on a shiny object, you would want to do render to a cubemap.

The concept behind rendering to a cubemap is the following. Bind a cubemap face, then render to it. Bind another cubemap face, then render to it. There are 6 faces in total. You may think that rendering 6 times your scene will drag down performance and you are right. Don't update the cubemap often. You can update every 2 frames. Make your cubemap small, for example 256x256.

  //RGBA8 Cubemap texture, 24 bit depth texture, 256x256   glGenTextures(1, &color_tex);   glBindTexture(GL_TEXTURE_CUBE_MAP, color_tex);   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);   //NULL means reserve texture memory, but texels are undefined   glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+0, 0, GL_RGBA8, 256, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);   glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+1, 0, GL_RGBA8, 256, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);   glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+2, 0, GL_RGBA8, 256, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);   glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+3, 0, GL_RGBA8, 256, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);   glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+4, 0, GL_RGBA8, 256, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);   glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+5, 0, GL_RGBA8, 256, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);   //-------------------------   glGenFramebuffersEXT(1, &fb);   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);   //Attach one of the faces of the Cubemap texture to this FBO   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X, color_tex, 0);   //-------------------------   glGenRenderbuffersEXT(1, &depth_rb);   glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_rb);   glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, 256, 256);   //-------------------------   //Attach depth buffer to FBO   glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_rb);   //-------------------------   //Does the GPU support current FBO configuration?   GLenum status;   status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);   switch(status)   {      case GL_FRAMEBUFFER_COMPLETE_EXT:      cout<<"good";   default:      HANDLE_THE_ERROR;   }   //-------------------------   //and now you can render to GL_TEXTURE_CUBE_MAP_POSITIVE_X   //In order to render to the other faces, do this :   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, color_tex, 0);   //... now render   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, color_tex, 0);   //... now render   //... and so on 


And in the end, cleanup

   //Delete resources    glDeleteTextures(1, &color_tex);    glDeleteRenderbuffersEXT(1, &depth_rb);    //Bind 0, which means render to back buffer, as a result, fb is unbound    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);    glDeleteFramebuffersEXT(1, &fb); 

Quick example, render_to_texture (2D Depth texture ONLY)

In this example, notice glDrawBuffer(GL_NONE) and glReadBuffer(GL_NONE). We don't need a color output so that is why we set them to GL_NONE. The important call is glDrawBuffer(GL_NONE). We do not want to render to a color buffer.

  //32 bit depth texture, 256x256   glGenTextures(1, &depth_tex);   glBindTexture(GL_TEXTURE_2D, depth_tex);   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_NEAREST);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);   glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);   //NULL means reserve texture memory, but texels are undefined   //You can also try GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24 for the internal format.   //If GL_DEPTH24_STENCIL8_EXT, go ahead and use it (GL_EXT_packed_depth_stencil)   glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, 256, 256, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);   //-------------------------   glGenFramebuffersEXT(1, &fb);   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);   //Attach   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depth_tex, 0);   //-------------------------   //Does the GPU support current FBO configuration?   //Before checking the configuration, you should call these 2 according to the spec.   //At the very least, you need to call glDrawBuffer(GL_NONE)   glDrawBuffer(GL_NONE);   glReadBuffer(GL_NONE);   GLenum status;   status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);   switch(status)   {      case GL_FRAMEBUFFER_COMPLETE_EXT:      cout<<"good";   default:      HANDLE_THE_ERROR;   }   //-------------------------   //----and to render to it, don't forget to call   //At the very least, you need to call glDrawBuffer(GL_NONE)   glDrawBuffer(GL_NONE);   glReadBuffer(GL_NONE);   //-------------------------   //If you want to render to the back buffer again, you must bind 0 AND THEN CALL glDrawBuffer(GL_BACK)   //else GL_INVALID_OPERATION will be raised   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);   glDrawBuffer(GL_BACK);   glReadBuffer(GL_BACK); 

And in the end, cleanup

   //Delete resources    glDeleteTextures(1, &depth_tex);    //Bind 0, which means render to back buffer, as a result, fb is unbound    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);    glDeleteFramebuffersEXT(1, &fb); 

Quick example, render_to_texture (2D), mipmaps, depth_stencil

If GL_EXT_packed_depth_stencil is present, use it. Also called a D24S8 format. All common GPUs support this format.

http://www.opengl.org/registry/specs/EXT/packed_depth_stencil.txt

  //RGBA8 2D texture, D24S8 depth/stencil texture, 256x256   glGenTextures(1, &color_tex);   glBindTexture(GL_TEXTURE_2D, color_tex);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);   //NULL means reserve texture memory, but texels are undefined   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);   //You must reserve memory for other mipmaps levels as well either by making a series of calls to   //glTexImage2D or use glGenerateMipmapEXT(GL_TEXTURE_2D).   //Here, we'll use :   glGenerateMipmapEXT(GL_TEXTURE_2D);   //-------------------------   glGenFramebuffersEXT(1, &fb);   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);   //Attach 2D texture to this FBO   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, color_tex, 0);   //-------------------------   glGenRenderbuffersEXT(1, &depth_stencil_rb);   glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_stencil_rb);   glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, 256, 256);   //-------------------------   //Attach depth buffer to FBO   glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_stencil_rb);   //Also attach as a stencil   glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_stencil_rb);   //-------------------------   //Does the GPU support current FBO configuration?   GLenum status;   status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);   switch(status)   {      case GL_FRAMEBUFFER_COMPLETE_EXT:      cout<<"good";   default:      HANDLE_THE_ERROR;   }   //-------------------------   //and now you can render to GL_TEXTURE_2D   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);   glClearColor(0.0, 0.0, 0.0, 0.0);   //It's always a good idea to clear the stencil at the same time as the depth when the format is D24S8.   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |  GL_STENCIL_BUFFER_BIT);   //-------------------------   glViewport(0, 0, 256, 256);   glMatrixMode(GL_PROJECTION);   glLoadIdentity();   glOrtho(0.0, 256.0, 0.0, 256.0, -1.0, 1.0);    glMatrixMode(GL_MODELVIEW);   glLoadIdentity();   //-------------------------   glDisable(GL_TEXTURE_2D);   glDisable(GL_BLEND);   glEnable(GL_DEPTH_TEST);   //-------------------------   //**************************   //RenderATriangle, {0.0, 0.0}, {256.0, 0.0}, {256.0, 256.0}   //Read http://www.opengl.org/wiki/VBO_-_just_examples   RenderATriangle();   //-------------------------   GLubyte pixels[4*4*4];   glReadPixels(0, 0, 4, 4, GL_BGRA, GL_UNSIGNED_BYTE, pixels);   //pixels 0, 1, 2 should be white   //pixel 4 should be black   //----------------   //Bind 0, which means render to back buffer   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 

And in the end, cleanup

  //Delete resources   glDeleteTextures(1, &color_tex);   glDeleteRenderbuffersEXT(1, &depth_stencil_rb);   //Bind 0, which means render to back buffer, as a result, fb is unbound   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);   glDeleteFramebuffersEXT(1, &fb); 

Quick example, render_to_buffer (p-buffer replacement)

Around 2000, the p-buffer extension was released which was used to do offscreen rendering. These days, it is best to use GL_EXT_framebuffer_object. This extension is much easier to use compared to p-buffer and best of all, it is cross platform. This example creates a RenderBuffer using 2 calls to glRenderbufferStorageEXT. The first call is for creating a color buffer and the second is used to create a depth buffer.

   //RGBA8 RenderBuffer, 24 bit depth RenderBuffer, 256x256    glGenFramebuffersEXT(1, &fb);    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);    //Create and attach a color buffer    glGenRenderbuffersEXT(1, &color_rb);    //We must bind color_rb before we call glRenderbufferStorageEXT    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, color_rb);    //The storage format is RGBA8    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, 256, 256);    //Attach color buffer to FBO    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, color_rb);    //-------------------------    glGenRenderbuffersEXT(1, &depth_rb);    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_rb);    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, 256, 256);    //-------------------------    //Attach depth buffer to FBO    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_rb);    //-------------------------    //Does the GPU support current FBO configuration?    GLenum status;    status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);    switch(status)    {       case GL_FRAMEBUFFER_COMPLETE_EXT:       cout<<"good";    default:       HANDLE_THE_ERROR;    }    //-------------------------    //and now you can render to the FBO (also called RenderBuffer)    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);    glClearColor(0.0, 0.0, 0.0, 0.0);    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    //-------------------------    glViewport(0, 0, 256, 256);    glMatrixMode(GL_PROJECTION);    glLoadIdentity();    glOrtho(0.0, 256.0, 0.0, 256.0, -1.0, 1.0);     glMatrixMode(GL_MODELVIEW);    glLoadIdentity();    //-------------------------    glDisable(GL_TEXTURE_2D);    glDisable(GL_BLEND);    glEnable(GL_DEPTH_TEST);    //-------------------------    //**************************    //RenderATriangle, {0.0, 0.0}, {256.0, 0.0}, {256.0, 256.0}    //Read http://www.opengl.org/wiki/VBO_-_just_examples    RenderATriangle();    //-------------------------    GLubyte pixels[4*4*4];    glReadPixels(0, 0, 4, 4, GL_BGRA, GL_UNSIGNED_BYTE, pixels);    //pixels 0, 1, 2 should be white    //pixel 4 should be black    //----------------    //Bind 0, which means render to back buffer    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 

And in the end, cleanup

   //Delete resources    glDeleteRenderbuffersEXT(1, &color_rb);    glDeleteRenderbuffersEXT(1, &depth_rb);    //Bind 0, which means render to back buffer, as a result, fb is unbound    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);    glDeleteFramebuffersEXT(1, &fb); 

Limitations of GL_EXT_framebuffer_object

One of the limitations of GL_EXT_framebuffer_object is that when you bind a color buffer and then you bind a depth buffer, both must have the same width and height or else the state of the FBO is considered invalid (incomplete). This means if you have 1 FBO that is 64x64, another which is 512x64, another that is 1024x1024, for each of those you have to allocate a separate depth buffer (if you need depth testing of course). This obviously wastes memory.
In GL 3.0, FBO became core and that limitation was removed.
You can create 1 depth buffer that is 1024x1024 and bind them to all 3 FBOs. Notice that the depth buffer is large enough for even the smaller textures like 64x64.

1 FBO or more

Is it better to make 1 FBO and bind your texture to it each time you need to render to the texture?
An FBO itself doesn't use much memory. It is a state vector object. In terms of performance, each time you bind, the driver needs to validate the state which costs CPU time. Logically, it would be better to have 1 FBO per Render_To_Texture (RTT).
However, it has been found that you get a speed boost if your textures is the same size and you use 1 FBO for them.
If you have 10 textures that are 64x64 and 10 textures that are 512x64, make 2 FBOs. One FBO for each group.

The main framebuffer

Can you bind the main framebuffer's depth buffer as a depth buffer for your FBO? No. You must create a depth texture or a depth Render Buffer.

Does GL 3.0 allow using the main depth buffer? No.

Can you do MRT (multiple render targets) and have the main color framebuffer as one of the targets? No, you can only target a texture or a Render Buffer. GL 3.0 doesn't support it either.

 

MSAA

Are multisample Render_To_Texture (RTT) supported?

Not directly. You need GL_EXT_framebuffer_multisample and you would have to copy the contents of the AA-FBO to a standard RTT.

Note that GL_EXT_framebuffer_multisample also became core in GL 3.0

See also http://www.opengl.org/wiki/GL_EXT_framebuffer_multisample

 

Color texture, Depth texture

In this example, we are attaching a color texture and also a depth texture and we'll render to both of them.

  //RGBA8 2D texture, 24 bit depth texture, 256x256   glGenTextures(1, &color_tex);   glBindTexture(GL_TEXTURE_2D, color_tex);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);   //NULL means reserve texture memory, but texels are undefined   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);   glGenTextures(1, &depth_tex);   glBindTexture(GL_TEXTURE_2D, depth_tex);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);   glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);   //NULL means reserve texture memory, but texels are undefined   glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, 256, 256, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);   //-------------------------   glGenFramebuffersEXT(1, &fb);   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);   //Attach 2D texture to this FBO   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, color_tex, 0/*mipmap level*/);   //-------------------------   //Attach depth texture to FBO   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depth_tex, 0/*mipmap level*/);   //-------------------------   //Does the GPU support current FBO configuration?   GLenum status;   status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);   switch(status)   {      case GL_FRAMEBUFFER_COMPLETE_EXT:      cout<<"good";   default:      HANDLE_THE_ERROR;   } 

And in the end, cleanup

  //Delete resources   glDeleteTextures(1, &color_tex);   glDeleteTextures(1, &depth_tex);   //Bind 0, which means render to back buffer, as a result, fb is unbound   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);   glDeleteFramebuffersEXT(1, &fb); 

Depth only

This is similar to the case above (Color texture, Depth texture) except that since there is no color buffer, call glDrawBuffer(GL_NONE) before or after calling glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb) and then render. When you are done, call glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0) to render to the main framebuffer. This is important, call glDrawBuffer(GL_BACK) after. If you call before glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0), a GL error will be raised.

As for your fragment shader, you should write to gl_FragColor or whatever your color output it. The GPU will automatically generate the depth value and write to gl_FragDepth. The color value is obviously dropped automatically by the GPU. Example

  //[FRAGMENT SHADER]   #version 110   void main()   {     gl_FragColor = vec4(1.0);   } 

Color only

Simply disable depth testing (glDisable(GL_DEPTH_TEST) and set the depth mask to FALSE (glDepthMask(GL_FALSE)) before you render to your RTT.

Stencil

NEVER EVER MAKE A STENCIL buffer. All GPUs and all drivers do not support an independent stencil buffer. If you need a stencil buffer, then you need to make a Depth=24, Stencil=8 buffer, also called D24S8. Please search for the example about GL_EXT_packed_depth_stencil on this page.

MRT

Talk about MRT

MRT and cubemaps

Talk about MRT and cubemaps

glReadPixels

Yes, you can bind a FBO and then render to it and then read with with a call to glReadPixels. It doesn't matter if what you have attached to the FBO is a RenderBuffer or a texture, glReadPixels will still read it and it will return the results.

For RTT (Render To Texture), if you will be using glGetTexImage, it is recommended that you unbind the FBO, make the texture current with a call to glActiveTexture and glBindTexture and use glGetTexImage. It is recommended that you avoid glGetTexImage and use the glReadPixels method since certain drivers don't do anything when you call glGetTexImage.

Sampling and Rendering to the Same Texture

You should read

http://www.opengl.org/wiki/Framebuffer_Objects#Feedback_Loops

http://www.opengl.org/wiki/GLSL_:_common_mistakes#Sampling_and_Rendering_to_the_Same_Texture

 

 

 

 

반응형

 

728x90

 

 

 

 

=======================

=======================

=======================

 

 

 

출처: http://yeomhyeonseop.blogspot.kr/2014/11/frame-buffer-object.html

 

Frame Buffer Object + Render to Texture

 
OpenGL의 렌더링 파이프라인에서 지오메트리데이터와 택스쳐는 변형되어 몇가지 테스트를 거치게 되고, 최후에 스크린에 2D픽셀로써 랜더링되게 된다.
OpenGL의 최종 랜더링타깃은 FrameBuffer라고 불린다.
프레임버퍼에는 2D배열의 모음이나 OpenGL에서 사용되는것들이 저장된다.
(색상버퍼, 뎁스버퍼, 스텐실버퍼, etc)
기본적으로 OpenGL은  완전히 윈도우시스템이 만들고 관리하는 렌더링 타겟으로써 프레임버퍼를 사용한다.  이러한 디폴트 프레임버퍼를 Window System Privided FrameBuffer라 한다.

OpenGL의 glFrameBufferObject는 화면출력용이아닌 프레임버퍼 오브젝트를 생성할 수 있는 인터페이스이다.
이렇게 만들어진 프레임버퍼를 Application-created Framebuffer이라 불리운다.
Application created FBO를 사용함으로써 OpenGL프로그램은 랜더링 결과에 redirect 할 수 있게 된다.

window-system-provided framebuffer와 비슷하게, FBO는 색상, 뎁스, 스텐실버퍼와 같은 렌더링 결과물의 collection을 저장한다.
그와같이 FBO안의 논리버퍼들을 framebuffer-attachable images라고 하고 이것은 크게 텍스쳐이미지와 RenerBuffer Image가 있다.
만일 택스쳐이미지가 FBO에 붙어있을 경우 OpenGL은 render to texure를 수행한다.
또한 렌더버퍼오브젝트가 붙어있을경우 OpenGL은 offscreen rendering을 수행한다

FBO -  GL_COLOR_ATTACHMENT0 : Texture Object(Image)
           GL_COLOR_ATTACHMENT1 : null
            .
            .
            .
           GL_DEPTH_ATTACHMENT : Renderbuffer Object(Image)            
           GL_STENCIL_ATTACHMENT : null


위 그림과같이 Texture Object나 Renderbuffer Object는 어태치먼트포인트를 통하여 FBO와 연관지을 수 있다.
통상 n개의 색상 어태치먼트포인트와 1개의 뎁스, 1개의 스텐실어태치먼트포인트로 이루어져있다.
COLOR_ATTACHMENT의 갯수는 시스템의존적이기에 GL_MAX_COLOR_ATTACHMENTS를 이용하여 그 최대치를 알 수 있다.

FBO가 여러개의 COLOR_ATTACHMENT를 가지고있는것은 컬러버퍼를 여러 목적지에 동시에 랜더링하는것을 가능하게 해준다.
이러한 multiple render targets(MRT)는 glDrawBuffers 익스텐션으로 구현가능하다.

FBO 그 자신은 그자신은 아무런 배열(저장소)를 가지고있지않고 오로지 어태치먼트 포인트만들 가지고 있다.

FBO는유능한 스위칭 매커니즘을 가지고있다. (이전 framebuffer-attachable image를 FBO에서 디태치하고 다른 image를 FBO에 어태치.)
이는 FBO간에 스위칭하는것보다 빠르게 작동된다.
또한 이는 불필요한 데이터복사본으로인한 메모리소비를 아껴준다.
예를들어 하나의 택스쳐를 여러 FBO에 어태치하므로써 이 texture image는 여러 FBO에서 공요될 수 있다. 
FBO는 glFramebufferTexture2D() 를이용하여 2D texture object를 스위칭할 수 있고 glFramebufferRenderbuffer()를 이용하여 렌더버퍼오브젝트를 스위칭 할 수 있게끔 해준다.

FBO는
void glGenFramebuffers(GLsizei n, GLuint* ids)
메소르도 생성되며 
void glDeleteFramebuffers(GLsizei n, const GLuint* ids)
함수를 이용하여 삭제할 수 있다.
또한 
void glBindFramebuffer(GLenum target, GLuint id)
의 target을 GL_FRAMEBUFFER로 함으로써 바인딩 할 수 있다.
한번 FBO를 바인딩하면 모든 OpenGL 명령은 현재 바운딩되어있는 프레임버퍼에 적용된다.
id 0은 이폴트 윈도우시스템 프레임버퍼를 사용하게된다.

RenderBuffer Object는 offscreen rendering을 위해 새로 채용되었다.
이것은 씬을 텍스쳐오브젝트에 렌더하는 대신에 다이렉트로 렌더버퍼오브젝트에 랜더링 할 수 있게 만들어준다.
렌더버퍼는 단순히 하나의 렌더링가능한 내부포멧 이미지를 포함한 데이터 저장 오브젝트이다.
이것은 스텐실버퍼나 뎁스버퍼와 같은 서로 일치하는 텍스쳐포멧을 가지 않는 OpenGL의 논리버퍼들을 저장하기 위해 사용된다.

void glGenRenderbuffers(GLsizei n, GLuint* ids)
 로 생성 가능하며 
void glDeleteRenderbuffers(GLsizei n, const Gluint* ids)
로 삭제할 수 있다.
void glBindRenderbuffer(GLenum target, GLuint id)
의 target을 GL_RENDERBUFFER로 지정하여 바인딩 할 수 있다.

랜더버퍼오브젝트(이하 RBO)를생성할시 저장소(버퍼)에 아무런 데이터도 가지고 있지 않다. 즉 우리는 이를위한 메모리영역을 가져야 한다.
이는 
void glRenderbufferStorage(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height)

를 이용하여 수행될 수 있다.
target은 GL_RENDERBUFFER이어야 하고 internalFormat은
색상 (GL_RGB, GL_RGBA, etc), 또는
뎁스 (GL_DEPTH_COMPONENT), 또는
스텐실 (GL_STENCIL_INDEX)이다.
width와 height는 렌더버퍼이미지의 픽셀 수용크기이다.

width와 height는 GL_MAX_RENDERBUFFER_SIZE보다 작아야하고 그렇지않을시 GL_INVALID_VALUE에러를 내게 된다.

또한 우리는 현재 바운드된 RBO의 여러가지 파라미터를 얻을 수 있는데 이는 
void glGetRenderbufferParameteriv(GLenum target, GLenum param, GLint* value)
를 이용한다.
target은 GL_RENDERBUFFER이어야 하며 param은 얻고싶은 파라미터의 이름이다(GL_RENDERBUFFER_WIDTH, GL_REDERBUFFER_RED_SIZE, GL_RENDERBUFFER_DEPTH_SIZE etc.)
value는 리턴값을 저장할 포인터이다.

 상기한것처럼 FBO는 그자신은 아무런 이미지 저장소(버퍼)를 가지지 않는다.
대신 우리는 framebuffer-attachable images를 FBO에 어태치 할 수 있다.

2D택스쳐를 FBO에 어태치하는것은
glFramebufferTexture2D(GLenum target, GLenum attachmentPoint, GLenum textureTarget, GLuint textureId, GLint level)
를 이용하여 수행할 수 있다.
 targer은 GL_FRAMEBUFFER이어야 하며 attachmentPoint는 GL_COLOR_ATTACHMENTn, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT와같은 어태치먼트 포인트이다.
textureTarget은 대부분의경우 GL_TEXTURE_2D이며 4번째인자는 텍스쳐ID가 입력된다.
마지막 파라미터는 부탁될 텍스쳐의 밉맵 레벨이 입력된다.

만일textureID파라미터가 0으로 설정될경우, 텍스쳐이미지는 FBO에서부터 분리된다(detach).
만일 텍스쳐오브젝트가 FBO에 부탁된채로 삭제될경우, 택스쳐이미지는 자동적으로 분리된다. 그러나 텍스쳐오브젝트가 여러 FBO에 어태치되어있는상태에서 삭제될경우, 이것은 현재 바운딩되어있는FBO에서만 디테치될 것이다. (현재 바운딩되어있지않은 FBO상에서는 분리되지 않을것이다).

또한 RBO를 FBO에 부착하고싶을 경우
void glFramebufferRenderbuffer(GLenum target, GLenum attachmentPoint, GLenum renderbufferTarget, GLuint renderbufferId)
를 사용한다.
target과 attachmentPoint는 텍스쳐를 어태치할때와 같은 파라미터를 사용한다.
renderbufferTarget은 GL_RENDERBUFFER를 사용하며 마지막파라미터는 RBO의 ID를 넣는다.

renderbufferID가 0으로 설정되 경우 랜더버퍼이미지는 FBO로부터 분리될 것이다.
RBO의 삭제에관한 디테치는 택스쳐오브젝트와 동일하게 작동한다.

 일단 어태처블 이미지가 FBO에 어태치되고, FBO명령을 실행하기 전에 반드시 FBO의 상태가 완료되었는지, 또는 아직 완료되지 않았는지를 
GLenum glCheckFramebufferStatus(GLenum target)
를 이용하여 확인해봐야 한다. 만일 FBO가 완성되지 않았다면 glBegin, glCopyTexImage2D등의 드로잉, 리딩명령들은 실패하게 될 것이다.
위 체크함수는 현재 바운드되어있는 FBO의 모든 어태치되어있는 이미지와 프레임버퍼요소들을 확인한다. 또한 이 함수는 glBegin()/glEnd()쌍 안에서 사용될 수 없다.
target파라미터에는 GL_FRAMEBUFFER가 들어가야 하고 이것은 FBO를 체크한후 0이아닌 값을 리턴한다.
만일 모든 필요조건과 규칙을 만족한다면 이것은 GL_FRAMEBUFFER_COMPLETE를 리턴할 것이다.
그렇지않다면 그에 해당하는 에러값을 반환할 것이다.

FBO를 사용하기위해 끝나야하는 준비조건들은
1. 어태치된 이미지(RBO 나 텍스쳐이미지)의width와 height가 0이 아닐것.
2. 만일 이미지가 색상어태치먼트포인트에 부탁되었을경우 이미지는 반드시 랜더링이가능한 색상 내부포멧을 가지고 있어야 한다 (GL_RGBA, GL_DEPTH_COMPONENT, GL_LUMINANCE etc)
3. 만일 이미지가 GL_DEPTH_ATTACHMENT에 부탁되어있다면 이미지는 반드시 렌더링 가능한 뎁스 내부포멧을 가져야 한다(GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT24)
4. 만일 이미지가 GL_STENCIL_ATTACHMENT에 부탁되어있다면 이미지는 반드시 렌더링 가능한 스탠실 내부포멧을 가져야 한다(GL_STENCIL_INDEX, GL_STENCIL_INDEX8 etc)
5. FBO는 반드시 최소한 하나의 이미지가 부착되어있어야한다
6. FBO에 부착된 모든 이미지는 반드시 같은 width와 height를 가져야 한다
7. 모든 부탁되어있는 컬러어태치먼트들은 동일한 내부포멧을 사용하여야 한다.

만일이의모든 조건을 만족하여도 사용중인 OpenGL드라이버에서 지원하지않는 몇몇 내부포멧이나 인자조합을 사용중이거나 지원하지않는 특수한 기능을 사용할경우 위 체크함수는 GL_FRAMEBUFFER_UNSUPPORTED를 반환한다.



동적 텍스쳐링에 필요한 render to texture은 이제까지 glCopyTexSubImage2D()를 이용하여 프레임버퍼의 내용을 텍스쳐로 복사하므로써 구현되어왔다.
그러나 FBO를 사용하면 씬을 텍스쳐에 직접 렌더링 할 수 있다.
 종래의 방법은 텍스쳐에 렌더링하고싶은 이미지가 윈도우 크기보다 클경우 차분이 짤려나가는 문제가있었으나 FBO를이용한 방법에서는 단순히 택스쳐에 렌더링하고싶은 전체화면을 충분히 수용할수 있을만큼 큰 이미지를 만드는것으로 이를 해소할 수 있다.


 출처 : OpenGL Frame Buffer Object by http://www.songho.ca

 

 

 

=======================

=======================

=======================

 

 

 

출처: http://wanochoi.com/?p=1019

 

Framebuffer는 rendering의 최종 결과가 기록되는 buffer들의 집합이다.
OpenGL context가 생성되면서 기본적으로 하나의 framebuffer를 생성하여 사용하는데,
이러한 default framebuffer를 window-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하는 예제이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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하는 예제이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
// 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 );

 

 

 

 

=======================

=======================

=======================

 

 

 

출처: http://blog.daum.net/aero2k/53

 

 

Framebuffer Object를 이용한 Render To Texture(RTT) 구현.

 

OpenGL ES 2.0 programming guide 12장 Framebuffer Objects 의 예제 코드는 좀 많이 부실 하다.

 

이에 iPhone 3D Programming 책의 6장 블렌딩과 증강현실 장의 6-8 오프-스크린 FBO를 사용한 앤티 에일리어싱 기법 을 참조하였다.

 

iPhone 3D Programming 책의 6-8 장 이전까지는 FBO를 Single Surface로 사용한다.

 

이 장 이후 부터의 예제에는 EGL의 Double buffer과 유사하게 사용할 수 있는 FBO Off-screen 기법이 응용 된다.

 

<FBO에 대해서 궁금하신 분은 바른 생활님의 페이지(http://cafe.naver.com/gld3d/130)를 참고하시면 도움이 될것 같습니다.>

<VBO에 대해서 궁금하신 분은 바른 생활님의 페이지(http://cafe.naver.com/gld3d/129)를 참고하시면 도움이 될것 같습니다.>

<RTT에 대해서 궁금하신 분은 바른 생활님의 페이지(http://cafe.naver.com/gld3d/181)를 참고하시면 도움이 될것 같습니다.>

 

OpenGL ES 에서는 기본적으로 double buffering을 지원하지 않는다.

그럼으로 Framebuffer object를 이용하여, on-off screen 생성한 후 double buffering 처럼 활용할 수 있다.

 

이번에 RTT 기능을 만들면서 책의 예제를 응용 하다 보니깐 아 glFramebufferTexture2D 함수를 이용해서,

색상 버퍼를 Texture 객체로 실시간 변환 되는 것을 확인할 수 있었다.

void glFramebufferTexture2D(GLenum target,

     GLenum attachment,

      GLenum textarget,

      GLuint texture, GLint level);

 

FBO Texture 객체는 다른 텍스쳐 객체와 동일하게 만들어 진다.

glGenTexture() 명령으로 Identification을 생성하고 필터링과 래핑 모드를 갖고 있으며,

FBO 포맷과 일치하는 포맷을 가져야 한다.

 

FBO Texture 객체가 기존 Texture을 불러오기 위해서 이용했었던 glTexImage2D() 을 사용할때,

마지막 인자는 NULL 값인 zero가 들어가야하는 점을 기억해야한다.

 

GLuint RenderingEngine::CreateFboTexture(int w, int h) const
{
     GLuint texture;
     glGenTextures(1, &texture);
     glBindTexture(GL_TEXTURE_2D, texture);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
   

  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);


     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);


     return 

texture

;
}

 

glTexImage2D의 마지막 인자를 zero(0)로 하는 이유는 아직 그릴 그림이 없기 때문이다.

나중에 반환되는 texture를 binding해서 off-screen FBO에서 주전자를 그린다.

off-screen FBO에는 주전자가 실시간으로 렌더링 될 것이며, on-screen FBO에서는 이 주전자 texture를 cube box에 그려 넣을 것이다.

 

첫 번째 그림은 바른 생활님의 구현하신 RTT 소스에 있는 뽀로로의 친구 에디를 텍스쳐로 해서 그림을 그렸다.

이는 NeHe 07 상자 그리기 소스를 응용하면 쉽게 그릴 수 있다.

 

 

 

두 번째 그럼은  주전자 wavefront obj 파일을 teapot.h로 변환 해서 VBO에 저장 한후, RTT로 그린 결과 이다.

 

 

 

 

이제 NeHe.iOS.07.RTT 소스내에서 어떻게 onoff-screen을 구현 하는지 보도록 하자.

 

On Screen Frame Buffer Object

 

// Create the onscreen color render buffer. 

glGenRenderbuffers(1, &m_onscreen_colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_onscreen_colorRenderbuffer);

 

// Create the onscreen depth render buffer.

glGenRenderbuffers(1, &m_onscreen_depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_onscreen_depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
    
// Create the onscreen framebuffer object.
glGenFramebuffers(1, &m_onscreen_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_onscreen_framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,
                                      GL_COLOR_ATTACHMENT0,
                                      GL_RENDERBUFFER,
                                      m_onscreen_colorRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, 
                                       GL_DEPTH_ATTACHMENT, 
                                       GL_RENDERBUFFER, 
                                       m_onscreen_depthRenderbuffer);
 
// Bind the color buffer for rendering.
glBindRenderbuffer(GL_RENDERBUFFER, m_onscreen_colorRenderbuffer);

 

 

Off Screen Frame Buffer Object

 

// Create the off screen color render buffer.

glGenRenderbuffers(1, &m_offscreen_colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_offscreen_colorRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, width, height);

 

// Create the off screen depth render buffer.

glGenRenderbuffers(1, &m_offscreen_depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_offscreen_depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
 

// Create the off screen frame render buffer.
 glGenFramebuffers(1, &m_offscreen_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_offscreen_framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                              GL_RENDERBUFFER, m_offscreen_colorRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                              GL_RENDERBUFFER, m_offscreen_depthRenderbuffer);


m_offscreen_texture = 

CreateFboTexture(width, height);

 

 

On Screen FBO 와 Off Screen FBO 두 가지를 생성하였다.

 

위의 FBO가 정상적으로 생성 되었는지 확인하기 위해서 다음과 같은 함수를 제공한다.

GLenum glCheckFramebufferStatus(GLenum target);

 

다음과 같이 사용할 수 있다.

 

// Check the Framebuffer status
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
    std::cout << "FBO error:l" << status << "\r\n";

 

Render To Texture 를 하기 위해서 on Screen FBO에 렌더링 하기 전에 Off Screen FBO를 먼저 그려 주어야 한다.

 

다음과 같이 Render() 함수가 구성 하였다.

 

void RenderingEngine::RenderToTexture()
{

    (가) 

    glBindFramebuffer(GL_FRAMEBUFFER, m_offscreen_framebuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, m_offscreen_colorRenderbuffer);
           ...

 

void RenderingEngine::Render()

{

...

 // RenderToTexture를 통해서 teapot texture를 얻는다.
 if (m_texMode == 1) 
 {
      

RenderToTexture();    (가)

      xyrot_texture += 1;

     glBindTexture(GL_TEXTURE_2D, m_offscreen_texture); 
 } else {
      /* Select A Texture Based on filter */
      glBindTexture(GL_TEXTURE_2D, m_texture_ids[m_texFilter]);
 }
 

(나)


 glBindFramebuffer(GL_FRAMEBUFFER, m_onscreen_framebuffer);
 glBindRenderbuffer(GL_RENDERBUFFER, m_onscreen_colorRenderbuffer);

}

 

On - Off Screen 상에 순서에 유의 하도록 하자.

 

(가)

 RenderToTexture() 함수에서 m_offscreen_framebuffer 와 m_offscreen_colorRenderbuffer로 바인딩 한후, 주전자를 그린다.

 

(나) 렌더링된 주전자 color buffer는 m_offscreen_texture 객체로 변환이 되어 있음으로, m_onscreen_framebuffer 와

m_onscreen_colorbuffer를 다시 바인딩 한후, cube box를 그린다.

 

cube box에 그림을 그릴때는 기존의 뽀로로의 친구 에디가 아닌, 주전자(

m_offscreen_texture) 텍스쳐로 그린다.

teapot texture 는 (가)에서 이미 바인딩 하였다.

 

순서에 유의 하도록 하자.

 

Render To Texture 를 이용해서 텍스쳐를 처리할 경우에는 POT(Power Of Two;16x16, 32x16, 128x64 크기) 텍스쳐가 아니어도 가능하다.

아이폰 이나 안드로이드(2.2 이후)에서는 텍스쳐의 크기가 2의 배수인 POT 텍스쳐여야 한다.

 

NPOT 텍스쳐의 경우, RTT를 이용하여 이용할 수 있겠다.

 

iPhone 3D Programming 에서 언급 되었지만, NPOT 텍스쳐는 다음과 같은 방법으로도 사용할 수 있다.

bool isPOT(unsigned int n)
{
    bool bRet = false;
    int tmp = n;
     tmp--;

     tmp |= tmp >> 1;  tmp |= tmp >> 2;
     tmp |= tmp >> 4;  tmp |= tmp >> 8;
     tmp |= tmp >> 16;
     tmp++;

     if (tmp == n) {
         bRet = true;
     }

     return bRet;

 

unsigned int NextPot(unsigned int n)
{
    n--;
    n |= n >> 1; n |= n >> 2;
    n |= n >> 4; n |= n >> 8;
    n |= n >> 16;
    n++;
    return n;
}

 

 

if (isPOT(description.OriginalSize.x) && isPOT(description.OriginalSize.y)) {
    glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
} else {

    width = NextPot(description.OriginalSize.x);

    height = NextPot(description.OriginalSize.y);

    unsigned char* temp = new unsigned char[width * height * 4];
    memset(temp, 0x00, width*height*4);
    glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, description.OriginalSize.x, description.OriginalSize.y, format, type, data);
 
    delete[] temp;

}

 

 

마지막으로, OGLES 2.0과 OGLES 1.1 두 버전을 구현 하였으며 두가지 버전을 구현할려니 빡셨다 ㅠ ㅠ

공부라 생각하고 하는데 까지 두 가지 버전으로 가봐야 할 것 같다.

 

프로젝트의 기본 구성이 OGLES 2.0으로 되어 있기 때문에,

OGLES 1.1로 전환을 위해서는 

GLView.mm 

파일의 EAGLRenderingAPI

kEAGLRenderingAPIOpenGLES1 으로 변경해 주면 된다.

 

 

예제는 Google Code(http://code.google.com/p/myastro/)의 Download 페이지에 올려 놓았습니다.

 

 

 

 

=======================

=======================

=======================

 

 

 

 

출처: http://www.songho.ca/opengl/gl_fbo.html

 

 

Related Topics: Pixel Buffer Object (PBO) 
Download: fbo.zipfboDepth.zipfboStencil.zipfboBlit.zipfboMsaa.zip

Update: Framebuffer object extension is promoted as a core feature of OpenGL version 3.0, and is approved by ARB combining the following extensions;

  • EXT_framebuffer_object
  • EXT_framebuffer_blit
  • EXT_framebuffer_multisample
  • EXT_packed_depth_stencil

Overview

In OpenGL rendering pipeline, the geometry data and textures are transformed and passed several tests, and then finally rendered onto a screen as 2D pixels. The final rendering destination of the OpenGL pipeline is called framebuffer. Framebuffer is a collection of 2D arrays or storages utilized by OpenGL; colour buffers, depth buffer, stencil buffer and accumulation buffer. By default, OpenGL uses the framebuffer as a rendering destination that is created and managed entirely by the window system. This default framebuffer is called window-system-provided framebuffer.

The OpenGL extension, GL_ARB_framebuffer_object provides an interface to create additional non-displayable framebuffer objects (FBO). This framebuffer is called application-created framebuffer in order to distinguish from the default window-system-provided framebuffer. By using framebuffer object (FBO), an OpenGL application can redirect the rendering output to the application-created framebuffer object (FBO) other than the traditional window-system-provided framebuffer. And, it is fully controlled by OpenGL.

Similar to window-system-provided framebuffer, a FBO contains a collection of rendering destinations; color, depth and stencil buffer. (Note that accumulation buffer is not defined in FBO.) These logical buffers in a FBO are called framebuffer-attachable images, which are 2D arrays of pixels that can be attached to a framebuffer object.

There are two types of framebuffer-attachable images; texture images and renderbuffer images. If an image of a texture object is attached to a framebuffer, OpenGL performs "render to texture". And if an image of a renderbuffer object is attached to a framebuffer, then OpenGL performs "offscreen rendering".

By the way, renderbuffer object is a new type of storage object defined in GL_ARB_framebuffer_object extension. It is used as a rendering destination for a single 2D image during rendering process.

 
 
 
 

 

 
Connectivity between FBO, texture and Renderbuffer

The following diagram shows the connectivity among the framebuffer object, texture object and renderbuffer object. Multiple texture objects or renderbuffer objects can be attached to a framebuffer object through the attachment points.

There are multiple color attachment points (GL_COLOR_ATTACHMENT0,..., GL_COLOR_ATTACHMENTn), one depth attachment point (GL_DEPTH_ATTACHMENT), and one stencil attachment point (GL_STENCIL_ATTACHMENT) in a framebuffer object. The number of color attachment points is implementation dependent, but each FBO must have at least one color attachement point. You can query the maximum number of color attachement points with GL_MAX_COLOR_ATTACHMENTS, which are supported by a graphics card. The reason that a FBO has multiple color attachement points is to allow to render the color buffer to multiple destinations at the same time. This "multiple render targets" (MRT) can be accomplished by GL_ARB_draw_buffers extension. Notice that the framebuffer object itself does not have any image storage(array) in it, but, it has only multiple attachment points.

Framebuffer object (FBO) provides an efficient switching mechanism; detach the previous framebuffer-attachable image from a FBO, and attach a new framebuffer-attachable image to the FBO. Switching framebuffer-attachable images is much faster than switching between FBOs. FBO provides glFramebufferTexture2D() to switch 2D texture objects, and glFramebufferRenderbuffer() to switch renderbuffer objects.

Creating Frame Buffer Object (FBO)

Creating framebuffer objects is similar to generating vertex buffer objects (VBO).

glGenFramebuffers()

void glGenFramebuffers(GLsizei n, GLuint* ids) void glDeleteFramebuffers(GLsizei n, const GLuint* ids) 

glGenFramebuffers() requires 2 parameters; the first one is the number of framebuffers to create, and the second parameter is the pointer to a GLuint variable or an array to store a single ID or multiple IDs. It returns the IDs of unused framebuffer objects. ID 0 means the default framebuffer, which is the window-system-provided framebuffer. 
And, FBO may be deleted by calling glDeleteFramebuffers() when it is not used anymore.

glBindFramebuffer()

Once a FBO is created, it has to be bound before using it.

void glBindFramebuffer(GLenum target, GLuint id)

The first parameter, target, should be GL_FRAMEBUFFER, and the second parameter is the ID of a framebuffer object. Once a FBO is bound, all OpenGL operations affect onto the current bound framebuffer object. The object ID 0 is reserved for the default window-system provided framebuffer. Therefore, in order to unbind the current framebuffer (FBO), use ID 0 in glBindFramebuffer().

Renderbuffer Object

In addition, renderbuffer object is newly introduced for offscreen rendering. It allows to render a scene directly to a renderbuffer object, instead of rendering to a texture object. Renderbuffer is simply a data storage object containing a single image of a renderable internal format. It is used to store OpenGL logical buffers that do not have corresponding texture format, such as stencil or depth buffer.

glGenRenderbuffers()

void glGenRenderbuffers(GLsizei n, GLuint* ids) void glDeleteRenderbuffers(GLsizei n, const Gluint* ids) 

Once a renderbuffer is created, it returns non-zero positive integer. ID 0 is reserved for OpenGL.

glBindRenderbuffer()

void glBindRenderbuffer(GLenum target, GLuint id)

Same as other OpenGL objects, you have to bind the current renderbuffer object before referencing it. The target parameter should be GL_RENDERBUFFER for renderbuffer object.

glRenderbufferStorage()

void glRenderbufferStorage(GLenum  target,                            GLenum  internalFormat,                            GLsizei width,                            GLsizei height) 

When a renderbuffer object is created, it does not have any data storage, so we have to allocate a memory space for it. This can be done by using glRenderbufferStorage(). The first parameter must be GL_RENDERBUFFER. The second parameter would be color-renderable (GL_RGB, GL_RGBA, etc.), depth-renderable (GL_DEPTH_COMPONENT), or stencil-renderable formats (GL_STENCIL_INDEX). The width and height are the dimension of the renderbuffer image in pixels.

The width and height should be less than GL_MAX_RENDERBUFFER_SIZE, otherwise, it generates GL_INVALID_VALUE error.

glGetRenderbufferParameteriv()

void glGetRenderbufferParameteriv(GLenum target,                                   GLenum param,                                   GLint* value) 

You also get various parameters of the currently bound renderbuffer object. target should be GL_RENDERBUFFER, and the second parameter is the name of parameter. The last is the pointer to an integer variable to store the returned value. The available names of the renderbuffer parameters are;

 GL_RENDERBUFFER_WIDTH GL_RENDERBUFFER_HEIGHT GL_RENDERBUFFER_INTERNAL_FORMAT GL_RENDERBUFFER_RED_SIZE GL_RENDERBUFFER_GREEN_SIZE GL_RENDERBUFFER_BLUE_SIZE GL_RENDERBUFFER_ALPHA_SIZE GL_RENDERBUFFER_DEPTH_SIZE GL_RENDERBUFFER_STENCIL_SIZE  

Attaching images to FBO

FBO itself does not have any image storage(buffer) in it. Instead, we must attach framebuffer-attachable images (texture or renderbuffer objects) to the FBO. This mechanism allows that FBO quickly switch (detach and attach) the framebuffer-attachable images in a FBO. It is much faster to switch framebuffer-attachable images than to switch between FBOs. And, it saves unnecessary data copies and memory consumption. For example, a texture can be attached to multiple FBOs, and its image storage can be shared by multiple FBOs.

Attaching a 2D texture image to FBO

glFramebufferTexture2D(GLenum target,                        GLenum attachmentPoint,                        GLenum textureTarget,                        GLuint textureId,                        GLint  level) 

glFramebufferTexture2D() is to attach a 2D texture image to a FBO. The first parameter must be GL_FRAMEBUFFER, and the second parameter is the attachment point where to connect the texture image. A FBO has multiple color attachment points (GL_COLOR_ATTACHMENT0, ..., GL_COLOR_ATTACHMENTn), GL_DEPTH_ATTACHMENT, and GL_STENCIL_ATTACHMENT. The third parameter, "textureTarget" is GL_TEXTURE_2D in most cases. The fourth parameter is the identifier of the texture object. The last parameter is the mipmap level of the texture to be attached.

If the textureId parameter is set to 0, then, the texture image will be detached from the FBO. If a texture object is deleted while it is still attached to a FBO, then, the texture image will be automatically detached from the currently bound FBO. However, if it is attached to multiple FBOs and deleted, then it will be detached from only the bound FBO, but will not be detached from any other un-bound FBOs.

Attaching a Renderbuffer image to FBO

void glFramebufferRenderbuffer(GLenum target,                                GLenum attachmentPoint,                                GLenum renderbufferTarget,                                GLuint renderbufferId) 

A renderbuffer image can be attached by calling glFramebufferRenderbuffer(). The first and second parameters are same as glFramebufferTexture2D(). The third parameter must be GL_RENDERBUFFER, and the last parameter is the ID of the renderbuffer object.

If renderbufferId parameter is set to 0, the renderbuffer image will be detached from the attachment point in the FBO. If a renderbuffer object is deleted while it is still attached in a FBO, then it will be automatically detached from the bound FBO. However, it will not be detached from any other non-bound FBOs.

FBO with MSAA (Multi Sample Anti Aliasing)

 

 
Comparison: No Anti-aliasing vs. MSAA

When you render to a FBO, anti-aliasing is not automatically enabled even if you properly create a OpenGL rendering context with the multisampling attribute (SAMPLEBUFFERS_ARB) for window-system-provided framebuffer.

In order to activate multisample anti-aliasing mode for rendering to a FBO, you need to prepare and attach multisample images to a FBO's color and/or depth attachement points.

FBO extension provides glRenderbufferStorageMultisample() to create a renderbuffer image for multisample anti-aliasing rendering mode.

void glRenderbufferStorageMultisample(GLenum  target,                                       GLsizei samples,                                       GLenum  internalFormat,                                       GLsizei width,                                       GLsizei height) 

It adds new parameter, samples on top of glRenderbufferStorage(), which is the number of multisamples for anti-aliased rendering mode. If it is 0, then no MSAA mode is enabled and glRenderbufferStorage() is called instead. You can query the maximum number of samples with GL_MAX_SAMPLES token in glGetIntegerv().

The following code is to create a FBO with multisample colorbuffer and depthbuffer images. Note that if multiple images are attached to a FBO, then all images must have the same number of multisamples. Otherwise, the FBO status is incomplete.

 // create a 4x MSAA renderbuffer object for colorbuffer int msaa = 4; GLuint rboColorId; glGenRenderbuffers(1, &rboColorId); glBindRenderbuffer(GL_RENDERBUFFER, rboColorId); glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa, GL_RGB8, width, height);  // create a 4x MSAA renderbuffer object for depthbuffer GLuint rboDepthId; glGenRenderbuffers(1, &rboDepthId); glBindRenderbuffer(GL_RENDERBUFFER, rboDepthId); glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa, GL_DEPTH_COMPONENT, width, height);  // create a 4x MSAA framebuffer object GLuint fboMsaaId; glGenFramebuffers(1, &fboMsaaId); glBindFramebuffer(GL_FRAMEBUFFER, fboMsaaId);  // attach colorbuffer image to FBO glFramebufferRenderbuffer(GL_FRAMEBUFFER,       // 1. fbo target: GL_FRAMEBUFFER                           GL_COLOR_ATTACHMENT0, // 2. color attachment point                           GL_RENDERBUFFER,      // 3. rbo target: GL_RENDERBUFFER                           rboColorId);          // 4. rbo ID  // attach depthbuffer image to FBO glFramebufferRenderbuffer(GL_FRAMEBUFFER,       // 1. fbo target: GL_FRAMEBUFFER                           GL_DEPTH_ATTACHMENT,  // 2. depth attachment point                           GL_RENDERBUFFER,      // 3. rbo target: GL_RENDERBUFFER                           rboDepthId);          // 4. rbo ID  // check FBO status GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if(status != GL_FRAMEBUFFER_COMPLETE)     fboUsed = false;  

It is important to know that glRenderbufferStorageMultisample() only enables MSAA rendering to FBO. However, you cannot directly use the result from MSAA FBO. If you need to transfer the result to a texture or other non-multisampled framebuffer, you have to convert (downsample) the result to single-sample image using glBlitFramebuffer().

void glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, // source rectangle                        GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, // destination rect                        GLbitfield mask,                        GLenum filter) 

glBlitFramebuffer() copies a rectangle of images from the source (GL_READ_BUFFER) to the destination framebuffer (GL_DRAW_BUFFER). The "mask" parameter is to specify which buffers are copied, GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT and/or GL_STENCIL_BUFFER_BIT. The last parameter, "filter" is to specify the interpolation mode if the source and destination rectangles are not same dimension. It is either GL_NEAREST or GL_LINEAR.

The following code is to transfer a multisampled image from a FBO to another non-multisampled FBO. Notice it requires an additional FBO to get the result of MSAA rendering. Please see fboMsaa.zip for details to perform render-to-texture with MSAA.

 // copy rendered image from MSAA (multi-sample) to normal (single-sample) // NOTE: The multi samples at a pixel in read buffer will be converted // to a single sample at the target pixel in draw buffer. glBindFramebuffer(GL_READ_FRAMEBUFFER, fboMsaaId); // src FBO (multi-sample) glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);     // dst FBO (single-sample)  glBlitFramebuffer(0, 0, width, height,             // src rect                   0, 0, width, height,             // dst rect                   GL_COLOR_BUFFER_BIT,             // buffer mask                   GL_LINEAR);                      // scale filter  

Checking FBO Status

Once attachable images (textures and renderbuffers) are attached to a FBO and before performing FBO operation, you must validate if the FBO status is complete or incomplete by using glCheckFramebufferStatus(). If the FBO is not complete, then any drawing and reading command (glBegin(), glCopyTexImage2D(), etc) will be failed.

GLenum glCheckFramebufferStatus(GLenum target)

glCheckFramebufferStatus() validates all its attached images and framebuffer parameters on the currently bound FBO. And, this function cannot be called within glBegin()/glEnd() pair. The target parameter should be GL_FRAMEBUFFER. It returns non-zero value after checking the FBO. If all requirements and rules are satisfied, then it returns GL_FRAMEBUFFER_COMPLETE. Otherwise, it returns a relevant error value, which tells what rule is violated.

The rules of FBO completeness are:

  • The width and height of framebuffer-attachable image must be not zero.
  • If an image is attached to a color attachment point, then the image must have a color-renderable internal format. (GL_RGBA, GL_DEPTH_COMPONENT, GL_LUMINANCE, etc)
  • If an image is attached to GL_DEPTH_ATTACHMENT, then the image must have a depth-renderable internal format. (GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT24, etc)
  • If an image is attached to GL_STENCIL_ATTACHMENT, then the image must have a stencil-renderable internal format. (GL_STENCIL_INDEX, GL_STENCIL_INDEX8, etc)
  • FBO must have at least one image attached.
  • All images attached a FBO must have the same width and height.
  • All images attached the color attachment points must have the same internal format.

Note that even though all of the above conditions are satisfied, your OpenGL driver may not support some combinations of internal formats and parameters. If a particular implementation is not supported by OpenGL driver, then glCheckFramebufferStatus() returns GL_FRAMEBUFFER_UNSUPPORTED.

The sample code provides some utility functions to report the information of the current FBO; printFramebufferInfo() and checkFramebufferStatus().

Example: Render To Texture

 

 

Download the source and binary: fbo.zip (Updated: 2016-11-14) 
Extras: 
- Rendering to the depth buffer only: fboDepth.zip 
- Rendering the outlines of an object using stencil buffer: fboStencil.zip 
- Bliting between 2 FBOs using glBlitFramebuffer()fboBlit.zip 
- Rendering to texture with MSAA: fboMsaa.zip

Sometimes, you need to generate dynamic textures on the fly. The most common examples are generating mirroring/reflection effects, dynamic cube/environment maps and shadow maps. Dynamic texturing can be accomplished by rendering the scene to a texture. A traditional way of render-to-texture is to draw a scene to the framebuffer as normal, and then copy the framebuffer image to a texture by using glCopyTexSubImage2D().

Using FBO, we can render a scene directly onto a texture, so we don't have to use the window-system-provided framebuffer at all. Further more, we can eliminate an additional data copy (from framebuffer to texture).

This demo program performs render to texture operation with/without FBO, and compares the performance difference. Other than performance gain, there is another advantage of using FBO. If the texture resolution is larger than the size of the rendering window in traditional render-to-texture mode (without FBO), then the area out of the window region will be clipped. However, FBO does not suffer from this clipping problem. You can create a framebuffer-renderable image larger than the display window.

The following codes is to setup a FBO and framebuffer-attachable images before the rendering loop is started. Note that not only a texture image is attached to the FBO, but also, a renderbuffer image is attached to the depth attachment point of the FBO. We do not actually use this depth buffer, however, the FBO itself needs it for depth test. If we don't attach this depth renderable image to the FBO, then the rendering output will be corrupted because of missing depth test. If stencil test is also required during FBO rendering, then additional renderbuffer image should be attached to GL_STENCIL_ATTACHMENT.

 ... // create a texture object GLuint textureId; glGenTextures(1, &textureId); glBindTexture(GL_TEXTURE_2D, textureId); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0,              GL_RGBA, GL_UNSIGNED_BYTE, 0); glBindTexture(GL_TEXTURE_2D, 0);  // create a renderbuffer object to store depth info GLuint rboId; glGenRenderbuffers(1, &rboId); glBindRenderbuffer(GL_RENDERBUFFER, rboId); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT,                       TEXTURE_WIDTH, TEXTURE_HEIGHT); glBindRenderbuffer(GL_RENDERBUFFER, 0);  // create a framebuffer object GLuint fboId; glGenFramebuffers(1, &fboId); glBindFramebuffer(GL_FRAMEBUFFER, fboId);  // attach the texture to FBO color attachment point glFramebufferTexture2D(GL_FRAMEBUFFER,        // 1. fbo target: GL_FRAMEBUFFER                         GL_COLOR_ATTACHMENT0,  // 2. attachment point                        GL_TEXTURE_2D,         // 3. tex target: GL_TEXTURE_2D                        textureId,             // 4. tex ID                        0);                    // 5. mipmap level: 0(base)  // attach the renderbuffer to depth attachment point glFramebufferRenderbuffer(GL_FRAMEBUFFER,      // 1. fbo target: GL_FRAMEBUFFER                           GL_DEPTH_ATTACHMENT, // 2. attachment point                           GL_RENDERBUFFER,     // 3. rbo target: GL_RENDERBUFFER                           rboId);              // 4. rbo ID  // check FBO status GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if(status != GL_FRAMEBUFFER_COMPLETE)     fboUsed = false;  // switch back to window-system-provided framebuffer glBindFramebuffer(GL_FRAMEBUFFER, 0); ...  

The rendering procedure of render-to-texture is almost same as normal drawing. We only need to switch the rendering destination from the window-system-provided to the non-displayable, application-created framebuffer (FBO).

 ... // set rendering destination to FBO glBindFramebuffer(GL_FRAMEBUFFER, fboId);  // clear buffers glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  // draw a scene to a texture directly draw();  // unbind FBO glBindFramebuffer(GL_FRAMEBUFFER, 0);  // trigger mipmaps generation explicitly // NOTE: If GL_GENERATE_MIPMAP is set to GL_TRUE, then glCopyTexSubImage2D() // triggers mipmap generation automatically. However, the texture attached // onto a FBO should generate mipmaps manually via glGenerateMipmap(). glBindTexture(GL_TEXTURE_2D, textureId); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); ...  

Note that glGenerateMipma

 

 

 

 

=======================

=======================

=======================

 

 

 

 

OpenGL FBO 링크

 

http://dogfoottech.tistory.com/61

 

 

1.

http://wanochoi.com/?p=1019

 

2.

http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-14-render-to-texture/

 

3

FBO를 이용한 Render To Texture 구현

http://blog.daum.net/aero2k/53

 

4.

네이버 3D카페

http://cafe.naver.com/gld3d/181

 

 

5.

glPixelRead Example

http://www.lighthouse3d.com/tutorials/opengl-selection-tutorial/

 

 

6.

glPixelRead

https://www.khronos.org/opengles/sdk/docs/man3/html/glReadPixels.xhtml

 



출처: http://dogfoottech.tistory.com/61 [sweetzLab 기술블로그]

 

 

 

=======================

=======================

=======================

 

 

반응형


관련글 더보기

댓글 영역