1
\$\begingroup\$

I'm trying to implement SSAO together with deferred shading. The deferred shading works just fine, and so does calculating the AO as seen in the first image below. However, taking AO into consideration when combining the light with gbuffer data produces color banding as seen in the second figure.

Ambient occlusion map seems to work fine Color banding produced when applying AO Increasing color intensity somewhat alleviates the problem

The third image shows a result after messing around, ending up multiplying the final color by 3, increasing the overall light intensity. The light banding is somewhat alleviated, and further increasing the light intensity reduces banding even more. This is of course not a solution but perhaps a step in the right direction.

Nevertheless it got me thinking. For the resulting texture I'm using an RGBA8_UNORM format, which gives me 256 distinct values for the various channels, right? I store the AO in a single 8 bit channel texture. The only light affecting the mesh is ambient light, and the color of the mesh is a single shade of green. Doesn't this mean that the already low bits of the render target have to store even more shades when AO is applied? This makes sense considering that increasing the precision of the AO map doesn't fix the problem, but not so much when simply increasing the intensity of the final color reduces the banding. How is this generally handled when using deferred shading? I should note that attenuating the final color with the normalized horizontal pixel value in [0,1] produces similar banding, which is perfectly vertical instead.

Can it be something that has to do with gamma correction (I don't explicitly do it myself)? Is that something one must do when using deferred shading?

Edit:

It turned out that DXUT made the back buffer SRGB (meaning that it handles gamma correction), but gamma was never corrected at the beginning, resulting in incorrect gamma handling. Correcting gamma while sampling textures resulted in clearer, but darker colors. Increasing ambient light to compensate for the darkness resulted in less banding while still having dimmer surroundings. Another bonus is that the colors don't look as washed-out as before.

It's not completely perfect however, but maybe HDR rendering can take care of that (big maybe, I don't much about it at all). Also, colors of the mesh are constant, which probably contributes. I believe having high-frequency textures makes the little banding that's left less apparent.

\$\endgroup\$
  • \$\begingroup\$ I'm no expert into this, but I'm guessing it's about gamma. Try outcolor = pow(outcolor, 1/2.2); 1/2.2 makes darker colours richer. 2.2/1 (or just 2.2) would make dark colours less apparant. More info: geeks3d.com/20101001/… \$\endgroup\$ – Lasse Feb 8 '14 at 14:53
  • 1
    \$\begingroup\$ Examining the pixel values in your second image, it appears that the RGB values change in steps of 4 (e.g. the green channel goes 50, 46, 42, etc). So you're definitely losing precision in your calculations somewhere. Are you storing an intermediate result in an 8-bit render target, and then multiplying it by 4 later, or something along those lines? \$\endgroup\$ – Nathan Reed Feb 8 '14 at 19:05
  • \$\begingroup\$ BTW, in D3D11, gamma can be pretty much handled by setting all texture formats (both render targets and source textures) to UNORM_SRGB formats. Incorrect gamma handling could cause banding, but if you're not handling gamma at all (i.e. using UNORM formats everywhere) I wouldn't expect the lack of it to produce this banding. \$\endgroup\$ – Nathan Reed Feb 8 '14 at 19:08
  • \$\begingroup\$ I took a look at the link Lasse posted and now I compensate for gamma while sampling textures that goes into the gbuffer. I didn't need to correct the final color because the back buffer (from DXUT) turned out to already be SRGB. It definitely looks better now, but not perfect. Nathan: I'm using two render targets for the gbuffer, one to accumulate light, one half-res for AO, as well as two ping-pong render targets for post processing, all 8 bit per channel. The intermediate data would probably only be light and AO, and the only multiplication I can see is with AO, which is not constant. \$\endgroup\$ – Andreas Flöjt Feb 8 '14 at 20:14
  • \$\begingroup\$ As for your second comment: Thanks for that tip, I will keep it in mind. Right now I'm using DXUT to get the app running, which means gamma was indeed corrected at the end, but not at the beginning, resulting in incorrect gamma handling. I tried forcing the format to both regular UNORM as well as UNORM_SRGB when loading textures using D3DX (don't like it nor DXUT, but they do their jobs) in order to have everything in SRGB, but it made no difference. One day I could try with another framework, but for now it works with the finite bits of information I have available. Thanks guys! \$\endgroup\$ – Andreas Flöjt Feb 8 '14 at 20:40
0
\$\begingroup\$

The main problem in this case was that gamma was not handled correctly. As Nathan pointed out, textures using sRGB formats in D3D11 will handle gamma correction automatically. DXUT made the back buffer sRGB, which means that gamma was ultimately handled incorrectly. Manually correcting for gamma at the beginning of the pipeline when sampling textures fixed much of the banding (after increasing ambient light due to darker colors).

To anyone who experiences similar banding: make sure that gamma is properly handled. You can use sRGB formats on both render targets and textures to get free gamma correction when reading/writing. Note that this is only required for color data that is already "prehandled" (textures from photoshop etc) and not normal data and stuff like that.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.