Here’s an example that I use for my post-process FBOs.
if( !glrimp.exts.EXT_framebuffer_object ) {
return;
}
glActiveTextureARB( GL_TEXTURE0 );
if( regenerate )
{
common->RSafePrintf("Reparameterizing render target
");
/*if( rt.fbo ) {
glDeleteFramebuffersEXT(1, &rt.fbo->_handle);
rt.fbo->_handle = 0;
}*/
const bool deleteTexturesOnResize = Renderer::kATI == Renderer::GetSingleton()->vendor ? true : false;
if( deleteTexturesOnResize ) {
if( rt.fb_colorTex2D ) {
glDeleteTextures(1, &rt.fb_colorTex2D->_handle );
rt.fb_colorTex2D->_handle = 0;
}
if( rt.fb_depthTex2D ) {
glDeleteTextures(1, &rt.fb_depthTex2D->_handle );
rt.fb_depthTex2D->_handle = 0;
}
}
if( !rt.fbo || !rt.fbo->_handle ) {
common->RSafePrintf(" Generating FBO
");
rt.fbo = Renderer::GetSingleton()->GenFrameBufferObject();
}
if( !rt.fb_colorTex2D || !rt.fb_colorTex2D->_handle ) {
common->RSafePrintf(" Generating FBO's color texture2D
");
rt.fb_colorTex2D = Renderer::GetSingleton()->GenTexture();
}
if( !rt.fb_depthTex2D || !rt.fb_depthTex2D->_handle) {
common->RSafePrintf(" Generating FBO's depth texture2D
");
rt.fb_depthTex2D = Renderer::GetSingleton()->GenTexture();
}
if( !rt.fb_colorTexRect || !rt.fb_colorTexRect->_handle ) {
common->RSafePrintf(" Generating FBO's color textureRect
");
rt.fb_colorTexRect = Renderer::GetSingleton()->GenTexture();
}
if( !rt.fb_depthTexRect || !rt.fb_depthTexRect->_handle) {
common->RSafePrintf(" Generating FBO's depth textureRect
");
rt.fb_depthTexRect = Renderer::GetSingleton()->GenTexture();
}
if( cvarSystem->GetBool("r_postProcessUseNVRECT") ) {
rt.textureTarget = GL_TEXTURE_RECTANGLE_NV;
rt.fb_colorTex = rt.fb_colorTexRect;
rt.fb_depthTex = rt.fb_depthTexRect;
}
else {
rt.textureTarget = GL_TEXTURE_2D;
rt.fb_colorTex = rt.fb_colorTex2D;
rt.fb_depthTex = rt.fb_depthTex2D;
}
float samples = cvarSystem->GetFloat("r_postProcessSuperSamples");
samples = NxMath::clamp( samples, 16.0f, 0.025f );
common->RSafePrintf(" ...samples: %4.4f
", samples );
const int reqBufferWidth = static_cast< int >( float(g_appWindow.width) * samples );
// const int reqBufferHeight = static_cast< int >( float(g_appWindow.height) * samples );
const int reqBufferHeight = reqBufferWidth;
if( cvarSystem->GetBool("r_postProcessAllowNPOT") ) {
rt.width = reqBufferWidth;
rt.height = reqBufferHeight;
}
else if( cvarSystem->GetInteger("r_backBufferWidth") ) {
rt.width = cvarSystem->GetInteger("r_backBufferWidth");
rt.height = cvarSystem->GetInteger("r_backBufferHeight");
}
else {
rt.width = PowerOf2Ceil( reqBufferWidth );
rt.height = PowerOf2Ceil( reqBufferHeight );
}
g_rgl->BindTexture( rt.textureTarget, rt.fb_colorTex->_handle );
Renderer::GetSingleton()->UploadTextureData2D( rt.textureTarget, 0, GL_RGBA8, rt.width, rt.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
glGenerateMipmapEXT( rt.textureTarget );
// TODO: mess with magnifcation filtering for render target
glTexParameteri( rt.textureTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
bool mipmap = cvarSystem->GetBool("r_postProcessGenerateMipmaps");
if( mipmap ) {
glTexParameteri( rt.textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
common->RSafePrintf("Establishing mipmap chain for render texture
");
}
else {
glTexParameteri( rt.textureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
}
g_rgl->BindTexture( rt.textureTarget, 0 );
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, rt.fbo->_handle );
glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, rt.textureTarget, rt.fb_colorTex->_handle, 0 );
// Detach renderBuffer (if there was one)
glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0 );
// Detach depth Texture if there was one
glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, rt.textureTarget, 0, 0 );
const int depthType = cvarSystem->GetInteger("r_postProcessDepthTexture") ;
GLint internalDepthFormat = GL_DEPTH_COMPONENT24_ARB;
if( Renderer::kATI == Renderer::GetSingleton()->vendor ) {
internalDepthFormat = GL_DEPTH_COMPONENT16_ARB;
common->RSafePrintf(" ...using GL_DEPTH_COMPONENT16_ARB
");
}
if( kTexture == depthType ) {
g_rgl->BindTexture( rt.textureTarget, rt.fb_depthTex->_handle );
glTexImage2D( rt.textureTarget, 0, internalDepthFormat, rt.width, rt.height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
glTexParameteri( rt.textureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( rt.textureTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
g_rgl->BindTexture( rt.textureTarget, 0);
glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, rt.textureTarget, rt.fb_depthTex->_handle, 0 );
}
else if( kRenderBuffer == depthType ) {
static GLuint depth_rb = 0;
glGenRenderbuffersEXT( 1, &depth_rb );
glGenRenderbuffersEXT(1, &depth_rb);// render buffer
// initialize depth renderbuffer
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_rb);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, internalDepthFormat, rt.width, rt.height );
// attach renderbuffer to framebufferdepth buffer
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_rb);
}
common->RSafePrintf( COM_GREEN(" RenderTexture( %d, %d )
"), rt.width, rt.height);
if( depthType == kTexture ) {
common->RSafePrintf( COM_GREEN(" with depth texture
") );
}
else if( depthType == kRenderBuffer ) {
common->RSafePrintf( COM_GREEN(" with depth render buffer
") );
}
else {
common->RSafePrintf( COM_GREEN(" with no depth information
") );
}
}
//Set of state that can change on a framebuffer bind
// – AUX_BUFFERS, MAX_DRAW_BUFFERS, STEREO, AUX_BUFFERS, MAX_DRAW_BUFFERS, STEREO,
// SAMPLES, X_BITS, DOUBLE_BUFFER and a few others
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, rt.fbo->_handle );
CHECKGL;
GL_CHECK_FRAMEBUFFER_STATUS();
}