Using sRGB colour in QGLFramebufferObject with multisampling

1.1k views Asked by At

For performance reasons I have separated my 2D and 3D rendering. I have two QGLFramebufferObjects for each type because QGLFramebuffer does not support multisampling with GL_TEXTURE_2D as a target, so once drawing is done into the multisampled buffer, it is blitted into a 'normal' QGLFramebufferObject where the pixel values are resolved. Once this has been done for one/both of the render types, the buffers are used as texture inputs to a shader that blends the 2D 'layer' onto the 3D one.

I should mention that I'm locked into using QGLFramebufferObjects instead of pure OpenGL objects because I use QPainter for all 2D work, and QPainter can only paint onto Qt types.

This process works fine, except the anti-aliasing is too dark, it almost looks like a dark outline:

Bad blending

After doing some research I discovered this was down to using linear colour space instead sRGB (here and here). So I enabled GL_FRAMEBUFFER_SRGB for my FBO blitting, set the texture target internal type for my all FBOs to GL_SRGB8_ALPHA8, and performed sRGB->Linear before the blending calculations in my shader (and back again before the final output).

But it is isn't working; it either looks too bright, too dark, or exactly the same. Whenever the whole frame is too dark/light, I know it's because I've missed a colour-space conversion. But when it looks exactly the same - what is going on!?

I could really do with someone explaining the order of operations for enabling the GL_FRAMEBUFFER_SRGB state, if blitting will affect the colour space, and which FBOs need to be in sRGB for the anti-aliasing to look correct. Or am I totally wrong, and is it something else entirely that is causing these multisampling artifacts?

1

There are 1 answers

2
Nicol Bolas On BEST ANSWER

So I enabled GL_FRAMEBUFFER_SRGB for my FBO blitting, set the texture target internal type for my all FBOs to GL_SRGB8_ALPHA8, and performed sRGB->Linear before the blending calculations in my shader (and back again before the final output).

That made sense right up until the final step.

An image format that uses the sRGB colorspace means that texture accesses from that texture will automatically be converted from the sRGB colorspace into a linear colorspace. This happens by itself when you fetch texels from the texture. It's free. So you shouldn't have to do any "sRGB->Linear" computations at all.

Similarly, when you have enabled GL_FRAMEBUFFER_SRGB when rendering to an image that uses the sRGB colorspace, values you write to that image are assumed to be linear. By enabling GL_FRAMEBUFFER_SRGB, what you are telling OpenGL to do is to convert the linear values you write to sRGB colorspace values. This is again free, and works just fine with blending and antialiasing. So again, you shouldn't have to do any manual conversion.

So really, what you need to do is ensure a properly linear color pipeline. Any textures of yours that were created in the sRGB colorspace should use image formats in the sRGB colorspace. This will ensure that values you get from them in the shader are linear, so lighting math actually works. When you write color values, you need to write them to an sRGB colorspace framebuffer, with GL_FRAMEBUFFER_SRGB enabled. This will ensure that the linear values you write from your shader are properly converted to sRGB for display.

The last part is that you need to make sure that your display is an sRGB image as well. I know nothing about Qt OpenGL context initialization, but unless they've been ignoring OpenGL for the last 4 years or so, there should be some setting you can use to force it to create a context with sRGB colorspace buffers.