Posts tagged ‘extensions’

Building blocks: the Frame Buffer object

Introduction

In order to perform computation with shaders you have to read and write data from/to textures, even if the former task is an usual operation for a shader, the latter requires some GL code, in fact you can’t directly write data to a texture with a shader, but just to the buffers provided to GL by the window system.

In this post I’m going to discuss how to render directly to textures, so how to use texture for computational purpose.

The Frame Buffer object

All we need to render directly to a texture is an extension called GL_EXT_framebuffer_object, this extensions allows us to draw to rendering destinations other than the buffers provided by GL.

The Frame buffer is an OpenGL object, so the first thing we need to do is to create an handle to it using the function glGenFramebuffersEXT, then we have to bind it to the current context with the function glBindFramebufferEXT.

After that we can attach one or more textures to the FBO using the function glFramebufferTexture2DEXT, each texture is so attached to a different attach point identified by the constants GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT, etc…

After we have set everything up, we can use the attached textures for reading data with the function glReadBuffer and for drawing data with the function glDrawBuffer.

Example code

this is an example code that shows how to set up a FBO, attach a texture to it and use this texture as target buffer for writing operations.

GLuint fb;
glGenFramebuffersEXT(1, &fb);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);

… // create a texture with texture name tex

glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0);

glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);

It’s important to remember that you can attach to a single FBO just textures of the same type (GL_TEXTURE_2D) and with the same dimension, if you need to work with textures of different type and/or dimension you have to use two or more FBOs.

Useful resources

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Building blocks: the Texture

Introduction

This is the first post about coding, here I’m going to discuss about how to store data for a GPGPU project.

The main way to read and write data with a shader is using Textures, a 2D texture can be viewed as 2D array of data in an usual programming language.

The main function

If you want use textures as an array of data, the most important function to understand is glTexImage2D. You can compare this function to the C function used to create memory blocks: malloc.

Here I don’t want to explain everything about glTexImage2D (you can study the reference and the Red Book for detailed explanations), but I want to discuss the 2 most important parameters for our purpose: internalFormat and format.

internalFormat determines the number of components for each texel (a single element of the texture) and how many bits are used for these elements. So, for example, passing GL_RGBA gives you a texture with 4 components (R, G, B, A) for each texel and usually 8 bits for each component (this depends by the current graphic contest), instead passing GL_LUMINANCE gives you a texture with 1 component (L) each textel.

format describes how the elements are stored in the image data passed to the function. So, for example, GL_RGBA means that the first 4 values represents the 4 components (R, G, B, A) of a pixel, instead GL_LUMINANCE means that each value correspond to a single element with a single component.

Storing data

Using different combination of internalFormat and format with the same image data gives us different textures, some examples here:

internalFormat = GL_RGBA / format = GL_RGBA -> image data and texture data are mapped in the same way, so you have something like this (this is just a conceptual representation)

data[] = {0.1, 0.2, 0.3, 1.0, 0.1, 0.2, 0.3, 1.0, …}

texture[] = {0.1, 0.2, 0.3, 1.0, 0.1, 0.2, 0.3, 1.0, …}

internalFormat = GL_RGBA / format = GL_LUMINANCE -> each element from image data is replicated in the RGB components of the texture, and 1.0 is attached to the A component, so you have something like this (this is just a conceptual representation)
data[] = {0.1, 0.2, 0.3, 1.0, 0.1, 0.2, 0.3, 1.0, …}

texture[] = {0.1, 0.1, 0.1, 1.0, 0.2, 0.2, 0.2, 1.0, …}

I suggest you read the glTexImage2D reference and to experiment different combination in order to get the result you need.

Some useful extensions

Now that you know how to store and arrange your data, it’s important to decide what type of data to use in our texture, as I said before, passing GL_RGBA gives you elements with 8-bits components, but what if we need more accuracy?

We can use the extension ARB_texture_float, this extension adds texture internal formats with 16- and 32-bit floating-point components.

All we have to do in order to use this extension is using one of the new internalFormat types, for example GL_RGBA32F_ARB if we need 4 float components of 32-bit each one or GL_LUMINANCE16F_ARB if we need a single component of 16-bit.

An alternative could be using the GL_NV_float_buffer or the GL_ATI_texture_float propietary extensions, bu they are hardware dependent and I can’t tell you if they give you some better performance (I’ll do some benchmark someday).

Example code

This is an example code used to create a texture for GPGPU computing:

GLint n = tex_size * tex_size * NUM_COMPONENTS_X_TEXEL;
GLfloat * data = (GLfloat *)malloc(n * sizeof(GLfloat));
…
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, tex_size, tex_size, 0, GL_RGBA, GL_FLOAT, data);

It’s important to notice that GL_NEAREST parameter for MIN and MAG filters is really important, in fact using GL_LINEAR or something else gives you inaccurate results.

Another important thing to understand is that GL_FLOAT is not related to the texture elements or components, but it just describes the type of the elements stored in the memory pointed by data.

If you want to read data stored in a texture and store them in an usual array, you can use this code:

GLfloat * results = (GLfloat *)malloc(n * sizeof(GLfloat));
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, results);
[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]