Simple example of texture/convolution?

Hi,

Does anyone know of a really SIMPLE C example that does some sort of texture convolution? What I want to do is to draw a scene to a frame buffer object, convert the image to a texture, do some image processing on the texture in a fragment shader, and then apply the texture to a screen-aligned quad.

I’ve been beating my brains out on this for a week. I can do parts of it: capture the texture & apply it to the quad (w/o a shader), or in the other direction read in an image, make a texture of it, and apply it (with various manipulations) via shaders to an object. But when I try to put the two parts together, I just get a blank screen. There has to be something simple that I’m missing, but the only examples I’ve been able to find (here: http://prideout.net/bloom/index.php for instance) are complicated enough that I can’t find the key part(s).

Thanks,
James

Conceptually convolution is very simple. Think of centering a small, square window (the kernel) over an image texel, performing an element-by-element dot product between the kernel and the image under the kernel, then saving the result as the new texel. Move to the next texel and repeat.

The usual practice with shaders is to render a fullscreen quad/triangle, then use a fragment shader to implement a custom kernel, which can be anything you want (within the limitations of your hardware).

I guess I wasn’t quite clear about where I’m having the problem. It’s not the convolution operation itself. That’s simple enough, and I can find examples of shaders that do it. It’s getting the modified image to display, with any sort of operation on the texture.

Say I do something dead simple, like swap two color components:

base_color = texture2D (texture, texCoord);
gl_FragColor.rgba = base_color.rbga;

If I have read in an image and created a texture from it, I can do that operation in the fragment shader, and display the result on a screen-aligned quad. However, if I render a scene into a framebuffer object, make that into a texture, and do the same operation, I get a blank screen. I display that same scene-as-texture on a screen-aligned quad without invoking any shader, it works just fine.

Seems like there has to be some essential step that I’m missing, maybe something so obvious that I can’t see it. If I had a working program that was simple enough that I could trace through it, I might be able to find the problem, but I haven’t been able to find anything that’s both simple and complete.

James

If I have read in an image and created a texture from it, I can do that operation in the fragment shader, and display the result on a screen-aligned quad. However, if I render a scene into a framebuffer object, make that into a texture, and do the same operation, I get a blank screen. I display that same scene-as-texture on a screen-aligned quad without invoking any shader, it works just fine.

A piece of code that show us how you setup shaders and fbo before doing convolution might help.

Yet reading you, it seems like fbo setup is fine, maybe you don’t bind the texture correctly. Do you get the shader program sampler2D location in your gl program and set its value to 0 if you bind your texture to the texture unit 0, like this:


// shader program setup (assuming that link step occurs successfully)
glUniform1i(samplerLoc, 0);

And before doing the convolution:


glActiveTexture( GL_TEXTURE0_ARB );
glBindTexture( GL_TEXTURE_2D, texture_to_be_convolved_name);
//draw screen aligned quad

The smallest source I could come up with is several hundred lines, and I don’t know how to post it acceptably. However, the framebuffer object code I use is taken almost directly from the example here: http://www.gamedev.net/reference/programming/features/fbo1/ (I just cut out the mipmapping.) I created a simple example from that code, and it shows the same behavior.

In main, I added an option to load shaders or not. If not loaded, the code execution is pretty much like the example. If loaded, main loads the shader code, checks for errors, and gets the location of the sampler2D “Image” in the shader program.

Then in the display function, if I’m using the shaders, I set the active texture to 0 and bind the FBO texture, as you have, then set the location of “Image” to 0. The vertex shader code is

00001 varying vec2 TexCoord;
00002
00003 void main ()
00004 {
00005 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
00006 TexCoord = gl_MultiTexCoord0.xy;
00007 }

and the fragment shader is

00001 uniform sampler2D Image;
00002 varying vec2 TexCoord;
00003
00004 void main (void)
00005 {
00006 vec4 base_color;
00007
00008 base_color = texture2D (Image, TexCoord);
00009 gl_FragColor = base_color;
00010 }

When I run the program without the shader, it works exactly as the example code. With the shader, I get a texture that’s uniformly black - or some other color if I explicitly set the value of gl_FragColor to e.g. vec4 (1.0, 0.0, 0.0, 1.0), so the shader is being called and doing something.

What appears to be happening is that the fragment shader just isn’t getting anything for the Image sampler, but I can’t see why, because it works as a texture outside the shader. But again, if I use the same shader code and setup with a texture that I’ve read in from a file, it works.

(I just cut out the mipmapping.)

Sounds fishy, by default mipmapping is used, so if no mipmaps levels are defined, the texture gets invalid, and is sampled as white…
Try to generate mipmap levels after texture is updated, with glGenerateMipmapEXT(GL_TEXTURE_2D) (or glGenerateMipmap(GL_TEXTURE_2D); with GL3.0)

I confess that I don’t understand why mipmapping should ever get invoked in this context. The texture map is generated by writing an image at display resolution, then drawn on a screen-aligned quad at the same resolution.

In any case, it works without the mipmapping if I just read a texture from a file.

At this point I don’t have any idea why your program does not work. I think you must miss something. Have you tried what Zbuffer suggests, even if it is screen aligned?

Try to simplify your code the more you can commenting code, then uncomment each processing step one by one, checking opengl error.

You can also look at glsl debugging functions, looking for example at the number of active uniforms in your shader and its content.

I am not sure to follow you, at the beginning you said that if you display on screen the fbo texture disabling the shader program it works and now the shader program works on texture loaded from hard disk, is that correct?

Humm - I didn’t know there were debugging functions. As you may have guessed, I’m new to using shaders. (And really don’t do all that much graphics these days…)

I am not sure to follow you, at the beginning you said that if you display on screen the fbo texture disabling the shader program it works and now the shader program works on texture loaded from hard disk, is that correct?

Yes. In various test programs I can A) load a texture from disk, and use the shader to do pretty much any sort of processing I’ve tried (and I think the failures are probably just simple bugs); or B) create an FBO and draw to it, then use the texture in the FBO to do normal texturing of an object (which can be a screen-aligned quad, or anything else). What I can’t seem to do is to get the texture from FBO to the fragment shader. It acts like the sampler2D is all zeros. If I write values that aren’t derived from the sampler to gl_FragColor, they work as expected.

Do you properly detach the FBO after rendering to the texture, and before doing the shader setup ?

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

Yes, the code is like this (F is a structure that holds all the framebuffer-related variables.):

 glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, F->fbo);
 glPushAttrib (GL_VIEWPORT_BIT);
 glViewport (0, 0, F->width, F->height);
    ...Draw scene....
 glPopAttrib ();
 glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);

Then going on I make the texture active, set the location of the texture in the shader, and draw a textured quad:

 glActiveTexture (GL_TEXTURE0);
 glBindTexture (GL_TEXTURE_2D, F->texture);
 glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
 glUniform1i  (Loc_Image, 0);
 glEnable (GL_TEXTURE_2D);
 glBegin (GL_QUADS);       
 glTexCoord2f (0.0, 0.0);  
 glVertex2f (0.0, 0.0);    
 glTexCoord2f (1.0, 0.0);  
 glVertex2f (w, 0.0);      
 glTexCoord2f (1.0, 1.0);  
 glVertex2f (w, h);         
 glTexCoord2f (0.0, 1.0);  
 glVertex2f (0.0, h);      
 glEnd ();
 glutSwapBuffers ();

you never said if you’re getting errors (glGetError, glCheckFramebufferStatus). All it takes is one foul up somewhere along the line to hose the whole shebang.

One thing that tripped me up early on was forgetting to set/reset the read/draw buffers to/from NONE with no color targets, but that doesn’t seem to apply here.

Btw, attribute stacks are deprecated in GL3…

P.S. Might be a good idea to start with one of those demos that actually works, then whittle it down to the nub of your liking until you find your error. Sometimes details get lost in translation.

I noticed also that you’re using gl_FragColor, you may want to try and change this to gl_FragData[0] or which ever color attachment you’re writing into. I just kinda skimmed the post, but I figured it’d be worth a shot.