My understanding of gamma correction is as follows: We want to do colour math in linear space so we can lerp etc. between colour values and get the results we expect. But human eyes don't have a linear response to light, so we need to modify the colours just before displaying them. sRGB is then commonly used colour space for this. Furthermore, since paint programs and colour pickers talk about colour in sRGB, the colours typically need to be linearized before math is performed on them.
EDIT 2: Turns out I had the following conversions backwards. The rest of the question is left unchanged for context, but I don't want to lead someone else down the same incorrect path.
The actual curve mapping sRGB to linear space is an exponential curve over the range 0 to 1. Linear to sRGB is raising the colour to the power of 2.2. sRGB to linear is taking the 2.2th root of the colour. So since we are in the range 0 to 1 Linear to sRGB make the colours less bright, and sRGB to linear does the opposite.
In my program I'm pulling values out of an GL_R8 texture, interpreting them as alpha values and taking a vec4 as a vertex attribute we'll call colour
and multiplying it by vec4(1.0, 1.0, 1.0, alpha)
to produce the final colour in the fragment shader. If I set the colour
attribute to a known colour and enable the GL_FRAMEBUFFER_SRGB
flag, then the colour I get is too bright, and it is exactly as bright as it would be if the sRGB to linear conversion was not applied.
For example if I pass in the colour #222222 I get #666666 actually rendered to the screen according to an external colour picking tool. If I convert the colour #222222 to three floats by dividing by 255.0 I get three copies of 0.13333333. 0x22 = 34 in base 10, 34.0 / 255.0 = 0.13333333. The 2.2th root of 0.13333333 is 0.40017032. 0.40017032 * 255.0 gives us 102. 102 in hex is the 0x66 we were expecting.
At first I thought that the problem was that it wasn't applying the linear to sRGB conversion to the output of the shader for some reason, since things were too bright. So I did some searching and found this answer but that states that the only way that the linear to sRGB conversion will only be applied if GL_FRAMEBUFFER_SRGB is enabled. I'm getting the opposite behavior: the colours are too bright only if GL_FRAMEBUFFER_SRGB is enabled!
I then realized that openGL can't magically know what the semantics of my colour
vertex attribute are. So it can't be automatically converting my colour
to linear space, unless it's doing something weird and error prone like checking the name of the attribute or just converting every vec3
or vec4
. If that was a desired feature, a shader keyword or something would have been a better way to do that.
So I'm confused as to how I'm getting this result. I'm not currently doing any colour math in the shader besides what I've already mentioned, besides alpha blending. But I might want to do more of that later, including loading more textures in the fragment shader so I would like to sort out this weirdness, rather than just disabling GL_FRAMEBUFFER_SRGB and leaving the same mess for myself if I do end up doing more complicated stuff later.
EDIT 1: My windowing library says that my pixel format is sRGB so I don't think that is the issue. If I run glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_FRONT_LEFT, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, &result)
, result
gets set to GL_SRGB
. If I check GL_FRONT_RIGHT
, GL_BACK_LEFT
, and GL_BACK_RIGHT
I get GL_LINEAR
, GL_SRGB
, and GL_LINEAR
respectively. But according to this page since I didn't set any stereo rendering settings, then the RIGHT
buffers shouldn't matter.
GL_R8
since that's what I pass as the third param toglTexImage2D
. \$\endgroup\$