Gamma矫正的原理和实现

计算机目前由R、G、B三个维度来表示颜色,每个颜色可以由8个比特共256个灰度来描述,那么一共可以描述256256256=16777216种颜色。
过去大多数显示器都是阴极射线管显示器(CRT),这些显示器输出的亮度和存储的颜色值并不是线性的关系,因此在最终由显示器输出颜色的时候,其实会对颜色做一个gamma值的转换:
brightness = pow(color, gamma)
这个gamma值一般是2.2。
巧的是,这个转换刚好和人眼对光强的感知是一致的,如下图所示:

可以看到,当颜色值从0.1到0.2的时候,人眼感知到的亮度增加会更暗一些,这是因为人类的眼睛对比较暗的颜色变化更敏感。
如果是日常的显示图片,显示器的这种处理其实是没有问题的(因为它和人眼的感知是一致的)。但是在渲染的时候,我们一般假设所显示的亮度是和颜色值是线性关系(Gamma为1),因此我们在屏幕中看到的图像实际上并不是理想的图像。需要在最终输出像素颜色的时候处理一下:
brightness = pow(color, 1/gamma)
我们可以参照下图来理解:

假设我们想表达暗红色,那么如果我们直接输出颜色值0.5,那么最终所看到的亮度值其实只是0.5^2.2 = 0.218。想真正地让用户在屏幕中看到0.5的红色,需要(0.5(1/2.2))2.2=0.5。这样才能真正输出我们想要表达的亮度。这就是所谓的Gamma矫正。
我们将显示器Gamma处理过后的颜色空间称为sRGB空间。
另外需要注意的是,在程序中载入贴图的时候,因为艺术家在创作贴图的时候其实是在sRGB空间中创作的,因此在使用图片时,需要将采样出的颜色做一遍反gamma矫正(也就是pow(color, gamma)),才可以用于计算和显示。
举个例子,在艺术家创作图片的时候,他想表达0.5的红色,因为是在sRGB空间中创作的,因此实际存到图片中的颜色值为0.5^(1/gamma)。 所以在采样该图片时,我们必须重新做一遍(0.5^(1/gamma * gamma)), 才能得到真正艺术家想要表达的0.5的值,用于线性的颜色计算。这样才能真正保证计算的颜色不出偏差。

最后我用做了一下gamma矫正的试验,下图是不做gamma矫正和做gamma矫正的对比图,可以看到明显右图更亮了一些:

原文地址:https://www.cnblogs.com/wickedpriest/p/12389127.html