# Multiple render targets and gamma correctness in Direct3D9

Let's say in a deferred renderer when building your G-Buffer you're going to render texture color, normals, depth and whatever else to your multiple render targets at once.

Now if you want to have a gamma-correct rendering pipeline and you use regular sRGB textures as well as rendertargets, you'll need to apply some conversions along the way, because your filtering, sampling and calculations should happen in linear space, not sRGB space. Of course, you could store linear color in your textures and rendertargets, but this might very well introduce bad precision and banding issues.

Reading from sRGB textures is easy: just set SRGBTexture = true; in your texture sampler in your HLSL effect code and the hardware does the conversion sRGB->linear for you.

Writing to an sRGB rendertarget is theoretically easy, too: just set SRGBWriteEnable = true; in your effect pass in HLSL and your linear colors will be converted to sRGB space automatically.

But how does this work with multiple rendertargets? I only want to do these corrections to the color textures and rendertarget, not to the normals, depth, specularity or whatever else I'll be rendering to my G-Buffer. Ok, so I just don't apply SRGBTexture = true; to my non-color textures, but when using SRGBWriteEnable = true; I'll do a gamma correction to all the values I write out to my rendertargets, no matter what I actually store there.

I found some info on gamma over at Microsoft: http://msdn.microsoft.com/en-us/library/windows/desktop/bb173460%28v=vs.85%29.aspx

For hardware that supports Multiple Render Targets (Direct3D 9) or Multiple-element Textures (Direct3D 9), only the first render target or element is written.

If I understand correctly, SRGBWriteEnable should only be applied to the first rendertarget, but according to my tests it doesn't and is used for all rendertargets instead.

Now the only alternative seems to be to handle these corrections manually in my shader and only correct the actual color output, but I'm not totally sure, that this'll not have any negative impact on color correctness. E.g. if the GPU does any blending or filtering or multisampling after the Linear->sRGB conversion...

Do I even need gamma correction in this case, if I'm just writing texture color without lighting to my rendertarget? As far as I know, I DO need it because of the texture filtering and mip sampling happening in sRGB space instead, if I don't correct for it.

Sounds like a driver bug. A simple way around this is to write your diffuse and/or specular colors out in linear values. To maintain proper color resolution (the sRGB-to-linear conversion happens in floats, but storing linear values in 8-bit ints loses some precision), you should use 10-bit RGB images. Obviously this will not work if you need a real alpha component, but maybe you can hide that within your normal or something.

Can we see your tests? This is really aesthetic anyway - if you find you need to brighten/gamma correct the texture color, then do it, this is only an issue if your texture is stored in sRGB format,

Code for converting from sRGB is in GPU Gems 3

float3 diffuseCol = pow( f3tex2D( diffTex, texCoord ), 2.2 );
//Or (cheaper, but assuming gamma of 2.0 rather than 2.2)
float3 diffuseCol = f3tex2D( diffTex, texCoord );
fuseCol = diffuseCol * diffuseCol;


I can see a few possible solutions to this problem:

1. Leave the MRTs linear, and do the conversion later on in the rendering pipeline. The lights and alpha blended objects probably want blending in linear space anyway.

2. Turn off the render state, and do the conversion in the shader with result = pow(result, 1.0/2.2); (or more cheaply with sqrt() which is 2.0 and not 2.2).

3. Switch to D3D11. That lets you specify in the texture format if you want SRGB conversion to happen or not. For example there's DXGI_FORMAT_R8G8B8A8_UNORM_SRGB.

4. See if there's up to date drivers that do the right thing. However you're not the only one that's had trouble with that render state.