Gamma 背景

在电视和图形监视器中,显像管发生的电子束和生成的图像与输入电压并不是线性关系,而是以指数曲线发生变化。造成的效果是图像整体变暗,其中低灰度区 的动态范围变小,高灰度动态范围变大。即降低了低灰度区图像对比度,提高了高灰度区的图像对比度。

通常,计算出物体的颜色之后需要显示在屏幕显示器上,显示器的输出亮度和输入电压有关:输出亮度=输入电压的Gamma次幂。 对于CRT(阴极摄像管显示器)来说,GAMMA=2.2。

Gamma 校正

伽马校正(Gamma Correction)是一种用来对视频和图像进行编码和解码亮度值的非线性操作。 思路是在最终图像上应用监视器gamma的倒数,它是监视器Gamma的 反转曲线,虽然监视器Gamma2.2会把图像整体变暗,但因为我们提前进行了指数级操作,最终显示结果为我们程序计算出的结果。

比如:FragColor = vec4(0.5, 0.0, 0.0, 1.0); 对输出颜色进行Gamma校正曲线,相当于颜色降低了2.2次幂的亮度。 FragColor^(2.2) = vec4(0.5, 0.0, 0.0, 1.0)^(1/2.2) = vec4(0.5, 0.0, 0.0, 1.0)^0.45 = vec4(0.73, 0.0, 0.0, 1.0); 然后校正的颜色发送到显示器,最终显示的颜色就是
vec4(0.73, 0.0, 0.0, 1.0)^2.2 = vec4(0.5, 0.0, 0.0, 1.0); 是应用设置中的线性颜色。

因为人眼的Gamma大概就是1/2.2,但是如果不进行Gamma校正的话,那么输出的亮度值并不是我们所期望的值。

注意:基于Gamma2.2的颜色空间叫sRGB空间

Gamma 方式

  • 使用OpenGL內建的sRGB空间,自动在像素着色器进行Gamma校正;开启GL_FRAMEBUFFER_SRGB告诉OpenGL后续颜色存储到颜色缓冲区前先进行Gamma校正:

    glEnable(GL_FRAMEBUFFER_SRGB);

  • 像素着色器手动控制

//lighting...

// apply gamma correction
    float gamma = 2.2;
    fragColor.rgb = pow(fragColor.rgb, vec3(1.0/gamma));

思考

  • 为什么CRT需要设计为输出亮度为输入电压的幂指数?

人眼对亮度的感知并不是线性变化,而是对黑暗区域更加敏感。而CRT主要为了增加低动态区域的表示范围,这样在人眼感知上 有更加好的体验效果。

  • 为什么Gamma值为2.2?

根据知乎中色彩校正中的gamma值是什么,此值是根据实践调整出来。微软和惠普在 特定条件上测试人眼观看显示器的感受,发现在2.2Gamma值,由黑到白的过渡过程看起来比较均匀。

参考资料

【1】:https://learnopengl-cn.github.io/05%20Advanced%20Lighting/02%20Gamma%20Correction/

【2】:http://www.cnblogs.com/qiqibaby/p/5325193.html

【3】:http://blog.csdn.net/lichengyu/article/details/20840135

【4】:https://www.zhihu.com/question/27467127

【5】:http://blog.csdn.net/candycat1992/article/details/46228771