<p>Your understanding is correct, and I think the image on the right looks like the more correct one. I think the root of your confusion is that the constant <code>objectColor</code> you've defined in your shader is being interpreted as a gamma-corrected color in the left image, and the same values are being interpreted as a linear color in the right image. To be more consistent, in the latter case you should apply <code>srgbToLinear</code> to the <code>objectColor</code> as well. Then, I expect, the two images will look more like the same dark reddish color.</p>
<p>Note that if I interpret <code>objectColor</code> as linear, then apply the 1/2.2 gamma to it, it comes out as this color:</p>
<img src="https://i.sstatic.net/OG8MR.png" width="100" height="100" alt="light brown color">
<p>which looks more plausibly like the underlying color of the rabbit on the right.</p>
<p>BTW, a point of terminology quibble. Gamma 2.2 is not the same thing as the sRGB nonlinear curve. The sRGB curve is the more complicated one given in by <span class="math-container">$C_\text{linear}$</span> and <span class="math-container">$C_\text{srgb}$</span> in <a href="https://en.wikipedia.org/wiki/SRGB#Transformation" rel="nofollow noreferrer">the formulas here</a>, with the 2.4 power and the 0.055s. Now it's fine to use gamma 2.2 instead if you prefer, but I think you shouldn't call it sRGB, as it's not the curve defined by the sRGB standard and it will cause confusion.</p>

CC BY-SA 4.0
2022-01-11T04:42:06.143Z
2022-01-11T04:42:06.143Z
Nathan Reed
<p>I have some confusion regarding gamma correction and the output of my shaders. I'll start with the setup, then explain my current understanding of the matter, and finally the question.</p>
<hr />
<p><strong>Setup</strong></p>
<p>I'm using OpenGL ES 3.2 on Android.</p>
<p>I have a texture (skybox) and an object in the scene. Both are rendered to an off-screen framebuffer that has a texture as color attachment. This texture is then sampled for on-screen rendering.</p>
<p>Skybox shader:</p>
<pre class="lang-cpp prettyprint-override"><code>#version 320 es

precision mediump float;

in vec3 o_textureCoords;

out vec4 o_fragColor;

uniform samplerCube u_skyboxTexture;

vec4 srgbToLinear(vec4 srgbColor) {
    return vec4(pow(srgbColor.rgb, vec3(2.2)), srgbColor.a);
}

void main() {
    o_fragColor = texture(u_skyboxTexture, o_textureCoords);               // Left
    o_fragColor = srgbToLinear(texture(u_skyboxTexture, o_textureCoords)); // Right
}
</code></pre>
<p>Object shader (relevant parts):</p>
<pre class="lang-cpp prettyprint-override"><code>#version 320 es

precision mediump float;

const vec3 lightColor = vec3(1.0, 1.0, 1.0);
const vec3 objectColor = vec3(0.4, 0.2, 0.1);

out vec4 o_fragColor;

void main() {
    // ...
    vec3 light = (ambient + diffuse + specular) * objectColor;
    o_fragColor = vec4(light, 1.0);
}
</code></pre>
<p>Final shader:</p>
<pre class="lang-cpp prettyprint-override"><code>#version 320 es

precision mediump float;

in vec2 o_texCoords;

out vec4 o_fragColor;

uniform sampler2D u_texture;

void main() {
    vec4 color = texture(u_texture, o_texCoords);
    o_fragColor = color;                                           // Left
    o_fragColor = vec4(pow(color.rgb, vec3(1.0 / 2.2)), color.a);  // Right
}
</code></pre>
<p>Let's consider two cases:</p>
<p><a href="https://i.sstatic.net/hCdo6m.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/hCdo6m.png" alt="No Gamma" /></a> <a href="https://i.sstatic.net/q3nUZm.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/q3nUZm.png" alt="Yes Gamma" /></a></p>
<ul>
<li>Left: not concerning myself with colorspace and gamma correction at all</li>
<li>Right: transforming the texture into linear colorspace and gamma correction at the end</li>
</ul>
<p>The object color is <code>(0.4, 0.2, 0.1)</code>. When I type these values into a color picker I get <a href="https://i.sstatic.net/Uf8wjs.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/Uf8wjs.png" alt="Color from color picker" /></a>, which looks similar to the rabbit-color on the left.</p>
<hr />
<p><strong>My Understanding</strong></p>
<ol>
<li>Assume images (like the skybox) to be in sRGB space most of the time</li>
<li>Try to work in linear colorspace as long as possible, and gamma correct at the end</li>
</ol>
<p>If <code>c</code> is my color, then because of (1) I should assume that when I retrieve a texel it actually is gamma-corrected (<code>c ^ (1/gamma)</code>). So to get <code>c</code> I do <code>(c ^ (1/gamma)) ^ gamma</code> as in <code>srgbToLinear()</code>.</p>
<p>Because of (2) I leave my object color as is when shading the object and only apply gamma correction at the very end.</p>
<p>Also, because of gamma correction we can expect the resulting images to look a bit brighter.</p>
<hr />
<p><strong>Question</strong></p>
<p>With my current understanding I would expect the image on the right to be the correct one. I get that the rabbit should look brighter, but especially when comparing it to what the color picker outputs, I can't help but feel confused about it. Is it simply my expectations that fail me or am I doing something wrong here?</p>

<gamma>
Gamma Correction vs Color Picker
Beko
# Gamma Correction vs Color Picker

(Asked by: username_0 on 2022-01-10)

I have some confusion regarding gamma correction and the output of my shaders. I'll start with the setup, then explain my current understanding of the matter, and finally the question.

---

**Setup**

I'm using OpenGL ES 3.2 on Android.

I have a texture (skybox) and an object in the scene. Both are rendered to an off-screen framebuffer that has a texture as color attachment. This texture is then sampled for on-screen rendering.

Skybox shader:

```cpp
#version 320 es

precision mediump float;

in vec3 o_textureCoords;

out vec4 o_fragColor;

uniform samplerCube u_skyboxTexture;

vec4 srgbToLinear(vec4 srgbColor) {
    return vec4(pow(srgbColor.rgb, vec3(2.2)), srgbColor.a);
}

void main() {
    o_fragColor = texture(u_skyboxTexture, o_textureCoords);               // Left
    o_fragColor = srgbToLinear(texture(u_skyboxTexture, o_textureCoords)); // Right
}

```

Object shader (relevant parts):

```cpp
#version 320 es

precision mediump float;

const vec3 lightColor = vec3(1.0, 1.0, 1.0);
const vec3 objectColor = vec3(0.4, 0.2, 0.1);

out vec4 o_fragColor;

void main() {
    // ...
    vec3 light = (ambient + diffuse + specular) * objectColor;
    o_fragColor = vec4(light, 1.0);
}

```

Final shader:

```cpp
#version 320 es

precision mediump float;

in vec2 o_texCoords;

out vec4 o_fragColor;

uniform sampler2D u_texture;

void main() {
    vec4 color = texture(u_texture, o_texCoords);
    o_fragColor = color;                                           // Left
    o_fragColor = vec4(pow(color.rgb, vec3(1.0 / 2.2)), color.a);  // Right
}

```

Let's consider two cases:

[![No Gamma](https://i.sstatic.net/hCdo6m.png)](https://i.sstatic.net/hCdo6m.png) [![Yes Gamma](https://i.sstatic.net/q3nUZm.png)](https://i.sstatic.net/q3nUZm.png)

* Left: not concerning myself with colorspace and gamma correction at all
* Right: transforming the texture into linear colorspace and gamma correction at the end

The object color is `(0.4, 0.2, 0.1)`. When I type these values into a color picker I get [![Color from color picker](https://i.sstatic.net/Uf8wjs.png)](https://i.sstatic.net/Uf8wjs.png), which looks similar to the rabbit-color on the left.

---

**My Understanding**

1. Assume images (like the skybox) to be in sRGB space most of the time
2. Try to work in linear colorspace as long as possible, and gamma correct at the end

If `c` is my color, then because of (1) I should assume that when I retrieve a texel it actually is gamma-corrected (`c ^ (1/gamma)`). So to get `c` I do `(c ^ (1/gamma)) ^ gamma` as in `srgbToLinear()`.

Because of (2) I leave my object color as is when shading the object and only apply gamma correction at the very end.

Also, because of gamma correction we can expect the resulting images to look a bit brighter.

---

**Question**

With my current understanding I would expect the image on the right to be the correct one. I get that the rabbit should look brighter, but especially when comparing it to what the color picker outputs, I can't help but feel confused about it. Is it simply my expectations that fail me or am I doing something wrong here?

**Tags:** gamma


---

## 1 answers

### Answer by username_1 on 2022-01-11. Score: 2 | Accepted answer

Your understanding is correct, and I think the image on the right looks like the more correct one. I think the root of your confusion is that the constant `objectColor` you've defined in your shader is being interpreted as a gamma-corrected color in the left image, and the same values are being interpreted as a linear color in the right image. To be more consistent, in the latter case you should apply `srgbToLinear` to the `objectColor` as well. Then, I expect, the two images will look more like the same dark reddish color.

Note that if I interpret `objectColor` as linear, then apply the 1/2.2 gamma to it, it comes out as this color:

![light brown color](https://i.sstatic.net/OG8MR.png)

which looks more plausibly like the underlying color of the rabbit on the right.

BTW, a point of terminology quibble. Gamma 2.2 is not the same thing as the sRGB nonlinear curve. The sRGB curve is the more complicated one given in by $C\_\text{linear}$ and $C\_\text{srgb}$ in [the formulas here](https://en.wikipedia.org/wiki/SRGB#Transformation), with the 2.4 power and the 0.055s. Now it's fine to use gamma 2.2 instead if you prefer, but I think you shouldn't call it sRGB, as it's not the curve defined by the sRGB standard and it will cause confusion.


---

