Skip to main content
GameDev.net gamedev.net
🔒 Locked

GPU based histogram generation

Started by mobeen Mar 5, 2009 at 3:09 AM 9 replies 12.3k views
Original Post
mobeen
mobeen
Hi all, I am trying to implement GPU based histogram generation. I want to use the method given here but there are some steps that I m not able to grasp. From my reading, the following is how this is done. The algorithm renders point primitive for each pixel. There are two steps 1) Calcualtion of the histogram bin index In the vertex shader, the bin index is calculated, this is easily done using the following vertex shader code assuming that the color attribute is assigned with each point.

half3 channel=half3(0.299, 0.587, 0.114);
half index =  dot(IN.Color, channel);

After the bin location is calculated, it is converted to an output location that maps into the 1D bin texture. Now this is something I m not able to understand. Does it mean that I use the index and output a specific position something like this,

OUT.position = half4(index,0,0,1);

this will output position from [0---1,0] in clipspace. So how does it relate to the histogram texture? Do I render my histogram texture at exactly the same position using a seperate render target? 2)Accumalation of the histogram bins This is achieved by setting the blend mode as follows
glBlendMode(GL_ONE, GL_ONE);
Now the thing that i dont get is what will the geometry that i will be rendering on this rendertarget? If anyone can give pointers to any detailed docs or perhaps someone who has implemented this before, please help. Thanks, Mobeen
Proud to be a PAKISTANI.
execute42
execute42
Hi,
to do this, you need to understand the OpenGL pipeline of how input vertexes are transformed into pixel locations.
The basic algorithm is, given an nx by ny image, you will render nx*ny vertexes as GL_POINTS. You can select any coordinate system you want but I would pick integer indicies so each pixel gets a vertex with coord (i,j) in the range [0,nx) and [0,ny).

In the vertex shader, you use the input vertex to lookup the value in the input texture to determine the histogram bin. As you said, you need to translate this index into the correct pixel location in your histogram. If you setup your viewport for a 1D texture as glViewport(0,0,nbins,1) then in Clip Space, an x value of -1 corresponds to the left edge of bin 0 and an x value of +1 corresponds to the right edge of the last bin. This assumes the w component is set to 1.0 (so there is no perspective division). If you do this with blending enabled while rendering to an integer or floating point texture, you will get your histogram.
mobeen
mobeen
Hi execute42,
Thanks for ur reply.

Quote:
Original post by execute42
Hi,
to do this, you need to understand the OpenGL pipeline of how input vertexes are transformed into pixel locations.
The basic algorithm is, given an nx by ny image, you will render nx*ny vertexes as GL_POINTS. You can select any coordinate system you want but I would pick integer indicies so each pixel gets a vertex with coord (i,j) in the range [0,nx) and [0,ny).

Yep this part if fine I am doing it exactly as you have described here.

Quote:
Original post by execute42
In the vertex shader, you use the input vertex to lookup the value in the input texture to determine the histogram bin. As you said, you need to translate this index into the correct pixel location in your histogram.

I have given the vshader code for doing this in my post.

Quote:
Original post by execute42
If you setup your viewport for a 1D texture as glViewport(0,0,nbins,1) then in Clip Space, an x value of -1 corresponds to the left edge of bin 0 and an x value of +1 corresponds to the right edge of the last bin. This assumes the w component is set to 1.0 (so there is no perspective division). If you do this with blending enabled while rendering to an integer or floating point texture, you will get your histogram.

This part is what is really unclear to me. Currently, to test it out, I m simply loading a 2d image on screen and then draw point primitives from this image. The vertex shader that is applied to get index is as given in my post. This gives me the bin index.
my code so far can be given in pseudocode as follows
SetVertexShader(vs);SetPixelShader(ps);   for j=0 to h      for i=0 to w          x = pixels[i,j]; //assuming the image is contained in this 2d array         glVertex2i(x,y);      end for   end forglViewport(0,0,256,1);glEnable(GL_BLEND);glBlendFunc(GL_ONE, GL_ONE);		glColor4f(0,0,0,1);    glBegin(GL_QUADS);			   glVertex2f(0,0);			   glVertex2f(1,0);   glVertex2f(1,1);   glVertex2f(0,1);glEnd();glDisable(GL_BLEND)

Thanks

[Edited by - mobeen on March 6, 2009 11:11:00 PM]
Proud to be a PAKISTANI.
execute42
execute42
If your pseudo-code is representative of what you are doing, then you are doing it wrong.
The whole point of this algorithm is to compute the histogram in a single rendering pass, where the "rendering" is the step of drawing your nx*ny point primitives.
A pixel shader is not required because the summing in the different histogram bins is done implicitly by the fact that blending is enabled.
So in your vertex shader, you calculate the index in the histogram and then convert to the proper clip space coord by:
clip_space_x = 2*index/nbins - 1 + 0.5/nbins
clip_space_w = 1.0

Before you draw the nx*ny grid, you setup a FBO with a 32-bit floating point 1D texture of length nbins and enable blending.
mobeen
mobeen
Quote:
Original post by execute42
If your pseudo-code is representative of what you are doing, then you are doing it wrong.
The whole point of this algorithm is to compute the histogram in a single rendering pass, where the "rendering" is the step of drawing your nx*ny point primitives.
A pixel shader is not required because the summing in the different histogram bins is done implicitly by the fact that blending is enabled.
So in your vertex shader, you calculate the index in the histogram and then convert to the proper clip space coord by:
clip_space_x = 2*index/nbins - 1 + 0.5/nbins
clip_space_w = 1.0

Before you draw the nx*ny grid, you setup a FBO with a 32-bit floating point 1D texture of length nbins and enable blending.

Thanks for the clarifications execute42. I have made the changes as u have mentioned but still I m not getting the result. This is how my FBO is setup currentlt i m creating a 256 bin histogram so i have hardcoded it, later i'll change it once this is working.
void InitFBO(){glGenFramebuffersEXT(1, &fboID);glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID);	 glGenTextures (1, &histogramID);glBindTexture(GL_TEXTURE_1D,histogramID);glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexImage1D(GL_TEXTURE_1D, 0,GL_LUMINANCE32F_ARB, 256, 0, GL_LUMINANCE, GL_FLOAT, NULL);glFramebufferTexture1DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT,GL_TEXTURE_1D,histogramID,0);glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);if(status == GL_FRAMEBUFFER_COMPLETE_EXT )   printf("OK for render to texture\n");else   printf("Problem");	}

And I have modified my rendering code as you mentioned before to
void Render(){   ...GLint viewport[4];glGetIntegerv(GL_VIEWPORT, viewport);glViewport(0,0,256,1);glEnable(GL_BLEND);	 glBlendFunc(GL_ONE, GL_ONE);glDisable(GL_DEPTH_TEST);AttachFBO();	    glClear(GL_COLOR_BUFFER_BIT);   setVertexShader();      DrawPointsGeometryFromImage();   glDisable(GL_BLEND);   static bool printed=false;   if(!printed){	GLfloat histogram[256]={0}; 	glReadPixels(0,0,256,1,GL_LUMINANCE32F_ARB, GL_FLOAT,histogram);	for(int i=0;i<256;i++)  	   cout<<  histogram<<" ";	cout<<endl;        printed=true;		   }DetachFBO();glViewport(viewport[0],viewport[1],viewport[2],viewport[3]);...}

The output displays all zeros at the moment. Any ideas?
Proud to be a PAKISTANI.
execute42
execute42
The only thing I see incorrect are the parameters to glReadPixels() which would explain why you are getting all zeroes.

It should be: glReadPixels(0,0,256,1,GL_LUMINANCE,GL_FLOAT,histogram);

At the end of your code, you should do a glGetError() and verify that it returns zero.
mobeen
mobeen
Quote:
Original post by execute42
The only thing I see incorrect are the parameters to glReadPixels() which would explain why you are getting all zeroes.

It should be: glReadPixels(0,0,256,1,GL_LUMINANCE,GL_FLOAT,histogram);

Even this gives the same result all zeros.

Quote:
Original post by execute42
At the end of your code, you should do a glGetError() and verify that it returns zero.

I added glGetError to the code and found that the error bit is set. I tried to trace the error and found out that I get an error after the first gl call that i make after AttachFBO in my render code. This is what my attach/detachFBO code is
void AttachFBO(){glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID); }void DetachFBO(){glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); }

If i remove the fbo attach/detachFBO call, there is no error. This suggests that the fbo is not setup correctly. So now I have added another color attachment for the final image so now this is my FBO setup code
#define glAssert assert(glGetError()==GL_NO_ERROR);void initFBO(){glGenFramebuffersEXT(1, &fboID);glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID);	 // create histogram textureglGenTextures (1, &histogramID);glBindTexture(GL_TEXTURE_1D,histogramID);glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexImage1D(GL_TEXTURE_1D, 0,GL_LUMINANCE32F_ARB, 256, 0, GL_LUMINANCE, GL_FLOAT, NULL);glFramebufferTexture1DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_1D,histogramID,0);glGenTextures(1, &finalImageID);glBindTexture(GL_TEXTURE_2D, finalImageID);glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA16F_ARB, width, height, 0, GL_RGBA, GL_FLOAT, NULL);glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,finalImageID,0);glAssert;glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);	glAssert;	GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);if(status == GL_FRAMEBUFFER_COMPLETE_EXT )   printf("OK for render to texture\n");else   printf("Problem");glAssert;}

Now gluGetError is zero. The glReadPixels returns a histogram with all 2s. Any ideas?

[Edited by - mobeen on March 9, 2009 1:45:34 AM]
Proud to be a PAKISTANI.
execute42
execute42
What was the error code returned by glGetError()? If it is 1286 then that means the error is a framebuffer error. What graphics card are you using? It might not support rendering to LUMINANCE32F_ARB textures. In your framebuffer setup code, the finalImage attachment is overwriting your histogram attachment. If you have not changed your glReadPixels code or are still binding the old 1D texture the results are undefined.
You are checking the status of the FBO incorrectly. The FBO that you want to test needs to be bound when you glCheckFramebufferStatus. Make sure you are rendering to a framebuffer complete fbo.
mobeen
mobeen
Quote:
Original post by execute42
What was the error code returned by glGetError()? If it is 1286 then that means the error is a framebuffer error.

Yeah the error code is 1286 meaning its a framebufferobject error.

Quote:
Original post by execute42
What graphics card are you using? It might not support rendering to LUMINANCE32F_ARB textures.

My card is GeForce 7800 GTX and i think it supports 32bit floating point textures.Here is the official page which clearly shows this.
Quote:
Original post by execute42
In your framebuffer setup code, the finalImage attachment is overwriting your histogram attachment. If you have not changed your glReadPixels code or are still binding the old 1D texture the results are undefined.
You are checking the status of the FBO incorrectly. The FBO that you want to test needs to be bound when you glCheckFramebufferStatus. Make sure you are rendering to a framebuffer complete fbo.

Yeah you are right, when i bind the fbo, i get errors. I was missing a renderbuffer attaching that removes the errors. One more thing is that i cannot attach a GL_LUMINANCE32F_ARB texture it gives error when i test fbocomplete?
Proud to be a PAKISTANI.
execute42
execute42
If your card can't render to GL_LUMINANCE32F_ARB, it should be able to render to GL_RGB32F_ARB. Make sure you have video drivers updated.
If you change the texture to RGB, make sure you changed the internal format parameter in glReadPixels().
mobeen
mobeen
Quote:
Original post by execute42
If your card can't render to GL_LUMINANCE32F_ARB, it should be able to render to GL_RGB32F_ARB. Make sure you have video drivers updated.

I changed the format to GL_RGB32F_ARB and it works ok however there are two issues
1) the frame rate drops to 1 fps.
2) I cannot create a 1D texture for this format. And for 2D texture, if i set the width and height to 256,1 resp. the fbo throws exception. One thing that i dont know is if its possible to create an FBO attachment to color0 with a dimension other than the window size like (256*1). And is it necessary that i must have two textures to two color attachments one for the histogram and other for the rendering image?
Quote:
Original post by execute42
If you change the texture to RGB, make sure you changed the internal format parameter in glReadPixels().

I changed the internal format but still, i get fbo error code. I dont know whether there is something wrong with my code or my hardware simply doesnot support this feature. Do u know of any way I can test whether my card supports 32 bit floating pt textures?
One more thing, I want to know more about FBOs any good reference?
Thanks
Mobeen
Proud to be a PAKISTANI.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.