Archive for the ‘OpenGL’ Category.

Normalization of a floating point texture

Introduction

As I’ve explained in my previous post, floating point textures don’t clamp values to the range [0.0, 1.0], so if you want to use them you have to normalize the values after the computation.

In this post I’m going to explain the technique I’m using to normalize the floating point textures so to display them correctly after the computation.

Normalization

The normalization formula is quite simple, it’s:

norm_value = (value - min) / (max - min);

Where min and max are the smallest and the biggest values stored in the texture.

The first thing to do to normalize a texture is to find the min and max values and the simplest way to do that is dumping the texture data into a float array using the function glGetTexImage, after that it’s easy to find the required values with a simple for cycle.

The last step is to apply the normalization formula on all the texels, I do that using a fragment shader.

Times

The average execution times for the normalization are the following:

  • dump data with glGetTexImage: 30 ms.
  • find min and max: 5 ms.
  • normalization shader: 2 ms.

so the total average time is 37 ms.

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

Float textures and data accuracy

Introduction

Here I’m going to discuss about floating point textures and relative errors you can get using the different internal formats, furthermore I’m going to explain why I don’t really need full floating point precision for my project.

Texture internal formats

When you use a “standard” internal format for a texture, like GL_RGB for example, each component of the texel (usually) has 8 bits and the stored values are floating point values clamped to the range [0.0, 1.0].

So what to do if you need more accuracy and a not-clamped value? You have to use the internal formats defined by the GL_ARB_texture_float extension.

The texture float extensions offers two kind of floating point textures, the first one uses 16 bits for each component and the second one 32. An example of the first kind of format is GL_RGB16F_ARB, this internal format uses 3 components for each textel and 16 bits for each component (so 6 bytes for each texel). An example of the second kind of format is GL_RGB32F_ARB, this internal format uses 3 components for each texel and 32 bits for each component (so 12 bytes for each texel).

Accuracy errors

Usually a floating poit value uses 32 bits, so using less bits means less accuracy so some rounding errors, I’ve run some test so to understand how much this error could be.

I’ve used the following code to generate the texture data

float inc = 0.01;
float val = 0.0;

for(int i = 0; i < n; i++)
{
 	val += inc;

 	if(val > 1.0)

 	 	val /= ((rand() % 9) + 1);

 	data[i] = val;
}

This code fills the array data with growing values from the range [0.0, 1.0], the inc variable determines the increment of the values at every iteration, I’ve chosen the value 0.01 because is the lowest value that it’s used in the developed generation algorithms.

Then I’ve created the textures using the array data as image data and after that I’ve retrieved the stored data using the function glGetTexImage, finally, I’ve computed the percentage error using the formula:

rel_err = (100.0 * abs(tex[i] - data[i]) / data[i]);

These are the results of my tests:

GL_RGBA - 8bits components

elements = 40000 - avg err = 0.209%
elements = 160000 - avg err = 0.207%
elements = 360000 - avg err = 0.206%
elements = 640000 - avg err = 0.206%

GL_RGBA16F_ARB - 16bits components

elements = 40000 - avg err = 0.016%
elements = 160000 - avg err = 0.016%
elements = 360000 - avg err = 0.016%
elements = 640000 - avg err = 0.016%

of course the 32-bits format gives a 0% error.

Floating point textures in my project

Do I really need floating point textures? Yes and no.

I don’t need them for the accuracy, because the 8-bits format gives me good results and very short execution times, if I would to analyze the problem better, I can say that the minimum percentage error required to have a wrong pixel is abs(254 - 255) / 255 * 100 = 0.392% and this error is bigger than the average error I got from 8-bits textures, so, on the average, I can’t have wrong pixels in my height-map.

Furthermore my work is based on pseudo-random generations, noise, erosion and etc. so a value that’s slightly different from what it should be can be considered a feature, not a bug :-)

But… I need floating point textures because for some algorithms I need not-clamped values as I’m going to show in my next posts, so all I need is the 16-bits texture because it gives me not-clamped values and an insignificant rounding error.

So the better solution for my project is to use both 8 and 16 bits textures and leave the user the possibility to choose what accuracy he needs for his generations.

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

Building blocks: the graphic contest

Introduction

This post explains how to set and initialize properly the GL graphic contest for a GPGPU project.

The following code uses some SDL functions, if you need to use some other library (like GLUT) look for the corresponding functions.

Basic settings

If we want to display the results of our computation we would set something like the clear color with glClearColor, enable the double buffer using SDL_GL_SetAttribute SDL function. Furthermore we have to enable 2D texturing with glEnable.

The complete code for those settings is:

SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

glClearColor(0.0, 0.0, 0.0, 1.0);

glEnable(GL_TEXTURE_2D);

The coordinate system

Two very important settings of the graphic contest are the coordinate system of the screen and the projection mode.

For the computational phase an orthogonal view is required so to have an exact correspondence between the texture texels and the screen pixels (so to avoid any interpolation), the function used for this is glOrtho. Another important setting is the viewport that represents the screen area affected by drawing operations, in order to have the right results the viewport has to be set to the texture dimensions with glViewport.

A good thing is that you can totally customize the coordinate system using glOrtho, so I’m used to set a coordinate system like this:

0,0----------w,0
|              |
|              |
|              |
0,h-----------w,h

and use a texture that has the same dimensions of the screen, so to avoid any changes between the computational and visualization phases.

Of course you can use a texture that has dimensions different from the screen’s ones, but you have to manage the thing with glViewport.

The code I use to set the coordinate system is the following:

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, w, h, 0.0, -1.0, 1.0);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glViewport(0, 0, w, h);

Computation and visualization

If you want to use properly the shaders for computational purposes and then display the results of your computation (draw the texture you get), you have to keep in mind that the coordinate system of the texture is different from the screen one, in fact it’s this one:

0,1----------1,1
|              |
|              |
|              |
|              |
|              |
0,0-----------1,0

This involves that you have to use two different functions for the computation and the visualization.
Using the screen coordinate system described above, these are the two functions I use.

This function is used for the computation:

void ShaderDraw(GLfloat w, GLfloat h)
{
	glBegin(GL_QUADS);

	glTexCoord2f(0.0, 1.0);
	glVertex2f(0.0, 0.0);

	glTexCoord2f(1.0, 1.0);
	glVertex2f(w, 0);

	glTexCoord2f(1.0, 0.0);
	glVertex2f(w, h);

	glTexCoord2f(0.0, 0.0);
	glVertex2f(0.0, h);

	glEnd();
}

This function is used for the visualization of the texture on screen

void ScreenDraw(GLfloat w, GLfloat h)
{
	glBegin(GL_QUADS);

	glTexCoord2d(0.0, 0.0);
	glVertex2f(0.0, 0.0);

	glTexCoord2d(1.0, 0.0);
	glVertex2f(w, 0.0);

	glTexCoord2d(1.0, 1.0);
	glVertex2f(w, h);

	glTexCoord2d(0.0, 1.0);
	glVertex2f(0.0, h);

	glEnd();
}

These functions draw the texture on the whole screen, if you want to change this you have to modify properly the parameters of the glVertex call.

Example code

This is the complete code I use for my init function:

void GLInit(int w, int h)
{
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

	glClearColor(1.0, 0.0, 1.0, 1.0);

	glEnable(GL_TEXTURE_2D);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0.0, w, h, 0.0, -1.0, 1.0);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glViewport(0, 0, w, h);
}
[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

The Ping-Pong technique

Introduction

In this post I’m going to discuss a common technique in GPGPU projects, this technique allows a shader to use two textures as input and output data so to be able to perform a computation based on more iterations.

The technique described in this post is based on Textures and the Frame Buffer Object, so I suggest you to read my two previous posts before to read this one.

The technique

The base concept of this technique is quite simple, all we need is a Frame Buffer Object and two textures linked to it.

The Ping-Pong technique is used for all the programs that need to store the result of a computation, so all the programs based on more iterations.

The first thing to do is to create a FBO and two textures (with same dimensions and internalFormat), then attach the two texturs to the FBO, after that it’s possible to use one texture as input, so read its values in the shader (vertex and fragment shaders can read one or more textures as a rule) and use the other one as output thanks to the glDrawBuffer function that sets the second texture as output buffer (so the fragments computed in the fragment shader will be wrote in the texture).

After the first iteration, it’s enough to swap the two textures so to use the computed values in the previous step as input for the next computation.

Some implementation details

The key element of this technique is the texture swapping, an easy way to do that is storing the Texture ids in a two elements array and access the array with two integer variables, for example read_tex and write_tex .

So to swap the textures all you have to do is swapping the values of these two variables.

Example code

This is the code used to init properly the FBO and the two textures:

int read_tex = 0;
int write_tex = 1;
GLuint tex[2];

glGenTextures(2, tex);
…
// create and init the FBO
GLuint fb;
glGenFramebuffersEXT(1, &fb);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);
…
// create and init the the two textures - this is the code for the first one
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex[read_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_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_FLOAT, data);
…
// attach the textures to the FBO
GLenum att_point[] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT};
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, att_point[read_tex], GL_TEXTURE_2D, tex[read_tex], 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, att_point[write_tex], GL_TEXTURE_2D, tex[write_tex], 0);
…
// create, init and enable the shaders

now everything is ready for the computational loop:

for(int i = 0; i < num_it; i++)
{
	// set the second texture as output buffer for the shader
	glDrawBuffer(att_point[write_tex]);

	// attach the input texture to the first Texture Unit
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, tex[read_tex]);
	glUniform1i(tex0_loc, 0);

	// draw a square with the texture on it so to perform the computation
	ShaderDraw();

	// swap read and write indexes
	if(read_tex)
	{
		read_tex = 0;
		write_tex = 1;
	}
	else
	{
		read_tex = 1;
		write_tex = 0;
	}
}

Useful resources

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

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]