Help implementing tone mapping

I need some help to implement tone mapping. I would like to try some different operators but most if not all require four things:

  1. Lum. Luminance at the current pixel (or fragment should I say?)
  2. Max_Lum. Maximum luminance of the whole image.
  3. Min_Lum. Minimum luminance of the whole image.
  4. World_Lum. Log averaged luminance of the whole image. To make it a bit more clear, you go through every pixel of the image and make World_Lum += log(2.3e-5+Lum); and then after you went through all the pixels World_Lum = exp(World_Lum/(Image_Height * Image_Width));
    Now the image I’m going to tone map is in fact my scene rendered to a FBO. How can I get those four values while doing the most things as possible in the GPU? I’m guessing I will need to render many FBOs but I really don’t know, got any clues?

Thanks.

No.1 should be easy, just sample the FBO.
2-4 can be extracted by downsampling the FBO to half the width and height in a number of steps until you’re down to 1x1. In the shader you just sample the pixels in a 2x2 area, extract the min, max, and that log(lum) stuff and store in R, G and B. Use this as the input for the next step and repeat until you’re at 1x1.

I have a few questions after reading your answers. First, I think downsampling to half the size of my PBO each time would be a bit too much since I can run in 1600x1200 that would mean I would have to downsample around 10 times (and as you can see, it wouldn’t downsample to exactly 1 pixel). So my idea with that is downsample to 128x<whatever value based on the ratio between the width and height of the FBO> and then downsample from there at the cost of some accuracy. Now there is another problem, my FBOs are not power of two, they have the same size of the window I render to (my app must support ARB_texture_non_power_of_two in other to run) so sampling in 2x2 area doesn’t work. How can I get around this problem?

Well, in a first step you can just downsample to an arbitrary “good enough” size, like perhaps 512x512 and then go from there. Sampling from 1600x1200 to 512x512 is no problem, just that you skip lots of pixels and stretch it a bit. It should give you good enough results in 99.9% of the cases.

You can use glGenerateMipmapEXT wchich is a part of FBO extension. Since you use FBO, then it should be OK.
You can use these mipmaps to create some simple effects like glow and dpeth of field very easily. Mipmap-based blur is not too good, but fast.

If you do not wish to use mipmaps, then you have to downsample it yourself. I think downsampling to 512x512 wass a good suggestion.

Just remember, that not all hardware supports texture filtering on floating point textures.
One more thing - if you are interested in luminance only, then downsample your image to luminance-only render targets - will work faster.

Now I’m ready to implement this, I have the proper FBOs that start from 256x256 to 1x1 each time executing a proper shader. Now what I need help with is the 2x2 sampling from the higher resolution FBO to get the value on the lower resolution FBO. Can someone tell me how to deal with that?

Thanks for the help so far guys.

Assuming you have a texture with 256x256 texels and render it full-screen to a 128x128 pixel framebuffer, so every pixel is exactly over 4 texels.

OpenGL samples at the center of the pixel, so the tex coord of a fragment is exactly at the intersection between the 4 texels. To get the correct texture coordinate for the center of the texels, you have to add/subtract half a texel to the texture coordinates.

When the texture is for example 256 texels wide, since texture coordinates range from 0 to 1, half a texel is 0.5/256.

So when (u,v) is the texture coordinate of the fragment, the coordinates of the four source texels are:
(u - 0.5/width, v - 0.5/height)
(u - 0.5/width, v + 0.5/height)
(u + 0.5/width, v - 0.5/height)
(u + 0.5/width, v + 0.5/height)

width and height are the dimensions of the source texture.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.