"Pixel-precise" sum of rendered textures ?

Hello,

I’m using OpenGL to do some calculations: summing grayscale textures together. Typically dozens of textures are rendered on top of each other and the output is/should be the “sum” of those textures.

The method I am using now is:

  • clear Accum buffer
  • for texNum = 1 to N.
    Clear frame buffer;
    Render texture <texNum>;
    glAccum(GL_ACCUM) to accum buffer; // Adds frame buffer to accum buffer.
  • end for;
    // end result is in accum buffer now.

This is painfully slow, as you would expect.

What I would like to do is to render the textures on top of each other while calculating the sum. Much like in alpha blending except that I don’t calculate the “average” or whatever of each pixel but sum the pixels up instead.

Would it possible to do this with a pixel/fragment shader?

Correct me if I’m astray here, but to my knowledge, in OpenGL it is NOT possible to:

  • Read frame buffer pixels from a fragment shader.
  • Write to a buffer that is bound to a texture. (pbuffer usage).

Do I have to switch to D3D ( shivers … ? ) I doubt it’s possible with Direct3D either.

Intuitively, the algorithm is pretty straightforward:

frame buffer pixel value += texel value rendered;

Essentially the algorithm needs to read to and write from the same buffer using a fragment shader… is it possible?

The ps2.0 spec hints that I should be able to at least write to two outputs, I wonder if that would help any.

Any help/hints appreciated

Andru

You could always ping pong between two buffers, i.e

while textures left to render:
BindTexture(read_buf);
RenderToTexture(write_buf);
swap(read_buf, write_buf)
end while

If you’re using a regular frame buffer (not floating point) then blending works.

Thus, you can simply:

glClear
glEnable( GL_BLEND )
glBlendFunc( GL_ONE, GL_ONE )
foreach( Texture t in textures ) {
drawQuad( t, size );
}

Note that you’ll saturate at 1.0 (pixel value 255).

Originally posted by jwatte:
[b]If you’re using a regular frame buffer (not floating point) then blending works.

Thus, you can simply:

glClear
glEnable( GL_BLEND )
glBlendFunc( GL_ONE, GL_ONE )
foreach( Texture t in textures ) {
drawQuad( t, size );
}

Note that you’ll saturate at 1.0 (pixel value 255).

[/b]

Yes, good point there. The only problem is the saturation at 255. AFAIK blending does not work in hardware in modes with more than 8 bits/channel

I wonder if it could be done with fragment shaders … but that would mean the shader should be able to read and write to the same pbuffer/frame buffer.

Guess I’m out of luck here

Andru

That’s what Joakim mentioned:
As you cannot read and write to a pbuffer at the same time, you have to ping-pong.

But using floating point ping-pong pbuffers you should be able to implement a high-precision accumulation buffer.

Klaus

Originally posted by KlausE:
[b]That’s what Joakim mentioned:
As you cannot read and write to a pbuffer at the same time, you have to ping-pong.

But using floating point ping-pong pbuffers you should be able to implement a high-precision accumulation buffer.

Klaus[/b]

That would be good enough if it works. There’s a big but here though:

I’m sorry but I do not understand the ping-pong tactics. Could you please care to explain, I’m sure it is trivial but I just don’t get it (yet).

Thanks,

Andru

I don’t know if it is very fast, but you can use texenv with GL_ADD, if support multitexturing

for each texture
{
//Save pbuffer to texture0 unit
glActiveTextureARB(GL_TEXTURE0_ARB);
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
glBindTexture(GL_TEXTURE_2D,tex);
glCopyTexImage2D(…);
glEnable(GL_TEXTURE_2D);
//clear pbuffer
glClear(GL_COLOR_BUFFER_BIT);
//Load and enable the texture to add
glActiveTextureARB(GL_TEXTURE1_ARB);
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_ADD);
glBindTexture(GL_TEXTURE_2D,yourTexture);
glEnable(GL_TEXTURE_2D);
//Draw a quad
DrawQuad(…);
//Disable texturing
glDisable(GL_TEXTURE_2D);
glActiveTextureARB(GL_TEXTURE0_ARB);
glDisable(GL_TEXTURE_2D);
}

At the end, you have the sum of the textures in the frame buffer (there may be also a problem of saturation)

[This message has been edited by Acheum (edited 06-18-2003).]

What part don’t you understand? The idea is very simple, you have two floating point textures. On every pass/draw call you use one bound as a texture and the other bound as a render target. After each pass you swap them. Here’s how it would work for accumulating two pixels in a single texel texture.

Clear both textures to 0.
Bind texture 0 as texture.
Bind texture 1 as render target.
Render sum of pixel one and texture 0
Bind texture 1 as texture.
Bind texture 0 as render target.
Render sum of pixel 2 and texture 1.

Doing this you will end up with pixel1+pixle2 in texture 1. The more general post is in my post above.

Yes, I see your point now. Thank you very much for clearing it up, both of you.

But I can’t see any GL_ADD token for the glTexEnv() function… see here:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/opengl/glfunc03_6xyu.asp

Then again, if MSDN’s OpenGL reference is outdated, could you point me to a more recent one ? I’ve been using the MSDN library for online reference so far…

Thanks, and sorry for being so dumb

Andru

The MSDN describes GL 1.1 and probably has mistakes.

You can find the spec from the main page. This function has been available since 1.3 and it refers to the old GL_ARB_texture_env_add
http://oss.sgi.com/projects/ogl-sample/registry/

Thank you all. You have been very helpful.

I will try the ping-pong tactics, seems like a good idea

Wasn’t the first time the OpenGL specs on MSDN were incomplete.

Andru

One easy way to implement the ping pong is to use the front and back buffers of a double-buffered pbuffer, and use swapbuffers to perform the switch. I guess this is pretty obvious.

-Won

This would only work if SwapBuffers doesn’t copy.
If you use render to texture on a pbuffer you must release the texture before you can paint on it again. Means there’s no additional overhead binding it to either of the two buffers anyway.