Read me About Demoscene? Links Contact

Render to texture - frame and render buffers

I finally got to the point where I needed to upgrade my render to texture routines. For a long time I’d copy the pixels from the video ram to conventional ram which consumes quites some cycles and loweres performance considerably. This is of course oldskool and not a good idea (performance wise). In addition, the method was limited to handling quadratic textures in power of two sizes (e.g. 512x512 or 1024x1024).

So, I looked into GL_RENDERBUFFER_EXT and came up with the following - working - code.

unsigned int    render_texture=0;
unsigned int    render_texture_x=SCREEN_WIDTH, render_texture_y=SCREEN_HEIGHT;
unsigned int    render_fbo=0, render_rbo=0;

void Init()
{
    // allocate renderbuffer
    glGenRenderbuffersEXT(1, &render_rbo);
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_rbo);
    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, render_texture_x, render_texture_y);
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);


    // allocate texture
    glGenTextures(1, &render_texture);
    glBindTexture(GL_TEXTURE_2D, render_texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  render_texture_x, render_texture_y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    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);

    // allocate framebuffer
    glGenFramebuffersEXT(1, &render_fbo);
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, render_fbo);
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, render_texture, 0);

    // bind framebuffer and renderbuffer
    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, render_rbo);

    // check status
    GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
    if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
    {
        throw “FBO not ready”;
    }

    // detach
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}


void Prepare()
{
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, render_fbo);
}

void Fetch()
{
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}

Comparing the code to my old version you’ll see that the above is a lot simpler. One of the obvious advantages is that we are allowed to render in the same resolution as the screen and thus all the fiddling with glViewPort and projection is removed.

The old version used to render the scene to a designated quad on the screen and subsequently scraping the screen and transfer the pixels to conventional ram. This new version is a bit different. We have two buffers prepared - a frame buffer and a render buffer.

A frame buffer is an off screen canvas that OpenGL can use for drawing the color pixels etc. This canvas is coupled with a normal 2D texture reference. We inform OpenGL to copy the pixels via the glFramebufferTexture2DEXT call and the GL_COLOR_ATTACHMENT0_EXT parameter.

If we need, we could also attach additional textures by more calls to glFramebufferTexture2DEXT and GL_COLOR_ATTACHMENTn_EXT. Having more render targets may sound odd, however it can be usefull and the power is unveiled when adding fragment shaders where data may be written to different frame buffers per pixel:

void main()
{
    gl_FragData[0] = vec4(0.0, 1.0, 0.0);
    gl_FragData[1] = vec4(0.0, 0.0, 1.0);
}

The render buffer is also an off screen object that OpenGL use for drawing. However, this buffer holds whatever is not storable in the frame buffer, i.e. depth information and stencil information.