doing exact box-filtering with pbuffers

Hello!

I´m fiddling around with HDR rendering. For the tonemapping (Reinhard) to work I need the avg. logarithmic luminance of the image. So I first render the image-containing pbuffer texture into a lower resolution pbuffer, calculating the luminance at each pixel. The lowres pbuffer is now containing the log. luminance of each pixel on the screen. Whats needs to be done now, is to sum up all those luminance values and divide them by the number of pixels.
In order to achieve this I use two ping-pong pbuffers (of same size):

  1. render the full “ping” texture to quarter-pong
  2. render the quarter-pong texture to 1/16th ping
  3. render 1/16 ping to …you get the idea

I´m using a shader that reads 4 neighboured texels, sums them up and divides the result by four.

When this process is finished, the lower-left pixel of the last pbuffer rendered into should contain the avg. log.luminance of the scene.

BUT it does not seem to work right. I guess, my code has problems with the last few steps of the algorithm. My assumption is, that I somehow don´t follow OpenGL´s rasterizing rules (where to find them?). For instance,in the last step, how to make sure, to fetch exactly the last 2x2 texel group and place the result exactly at pixel(0,0) of the target pbuffer?
I´m using an orthogonal frustum [0…1, 0…1], power-of-two pbuffers (I adress them with normalized texture coordinates), I use 1/texture_width as offset for the sum-up-shader…

Some help would be great, probably some example code would already point me in the right direction.

thank you in advance

For full control you need to use nearest filtering. OpenGL’s texture lookup is similar to the rasterization, everything is depending on the texel’s center. In D3D it’s the corner, I fear.
With nearest any point inside the texels area would do, but avoid the edges.
To lookup the four texels in the lower left corner in a, lets say, 16*16 texture (ping) I would use the texture coordinates

(0.5f/16.0f, 1.5f/16.0f) (1.5f/16.0f, 1.5f/16.0f)
(0.5f/16.0f, 0.5f/16.0f) (1.5f/16.0f, 0.5f/16.0f)

The coordinates for the rendering need to include the whole pixels you want to render.
That is the lower left pixel in the 16*16 destination buffer (pong) with an glOrtho(0, 1, 0, 1, -1, 1) setup is rendered with
glRectf(0.0f, 0.0f, 1.0f/16.0f, 1.0f/16.0f)

Make sure you have no issues with clamping.
If the sum exceeds 1.0 you need floating point pbuffers.

The OpenGL rasterization rules are in the specs chapter 3 Rasterization.

Thank you, it seems to work now.
Strangly I have to set the lower-left texturecoordinates to 0 (instead of half-texel) in order to get it working. So I still don´t know if its all 100% correct, but still, it gives the best results. I´m using a R9800pro and float-pbuffers.
The whole thing now looks this way:
</font><blockquote><font size=“1” face=“Verdana, Arial”>code:</font><hr /><pre style=“font-size:x-small; font-family: monospace;”> do
{
if (width>xtexel)newwidth=width0.5;
if(height>ytexel)newheight=height
0.5;

	pong-&gt;makeCurrent();

	ping-&gt;bind(0);
	ping-&gt;bindPBufferImage();

	glBegin(GL_QUADS);

	glTexCoord2d(0, 0);	glVertex2d(0, 0);
	glTexCoord2d(width-xtexel+xhalftexel, 0); glVertex2d(newwidth, 0);
	glTexCoord2d(width-ytexel+xhalftexel, height-ytexel+yhalftexel); glVertex2d(newwidth, newheight);
	glTexCoord2d(0, height-ytexel+yhalftexel); glVertex2d(0, newheight);

	glEnd();

	width=newwidth;
	height=newheight;

	std::swap(ping, pong);
} while(width&gt;xtexel