Hello, gamma
Gamma的起源
关于gamma的起源通常有两种说法,一种是CRT历史遗留问题,另一种就是人眼感知论
CRT历史遗留问题
以前我们使用的都是CRT(cathode-ray tube,阴极射线管)显示器,这类设备的显示机制是,使用一个电压轰击它屏幕上的一种图层,这个图层就可以发亮,我们就可以看到图像了。CRT有一个特点,就是输入电压和显示亮度不是呈线性关系的。打个比方,输入电压是5v,显示器的显示亮度是20,我现在把输入电压加大到10v,结果一看,显示器的亮度并没有提高到40。那输入电压和显示亮度是什么关系呢?它们是幂律(power-law)曲线的关系:

由于这个问题的存在,当我们把线性空间的亮度值0.5输入到CRT显示器,最终显示的亮度值是


-
encoding gamma:即图像设备捕捉到的亮度值和编码的像素值之间的关系:
-
display gamma:编码的像素值和显示的亮度值之间的关系:
完整的图像系统
一般来说,encoding gamma * display gamma = 1,这样显示的亮度值就和捕捉到的亮度值完全一致了。
对于我们的图形渲染来说,我们需要一个encoding gamma,否则输出画面受display gamma的影响就会偏暗:
不同的display gamma对输出画面的影响 以上就是CRT历史遗留问题带来的gamma起源说
人眼感知论
人眼感知论的说法是人眼对亮度的感知是非线性的:


假设我们使用8位空间来存储像素值的话,那我们就可以表达

sRGB
上面提到,为了配合人眼对亮度的感知,我们通过gamma矫正来让暗部细节得到更多的色阶。当显示的时候,我们就需要把图像还原成正确的亮度值来显示。巧合的是,CRT显示器有2.5的display gamma,人们想:“太棒了,我们不需要做任何调整就可以把gamma矫正的图像放到CRT显示器上正确显示了”。但此时的encoding gamma * display gamma = 0.45 * 2.5 = 1.125,不等于1。
直到1996年,微软与惠普开发了sRGB色彩空间,sRGB最初的设计目的是作为生成在因特网以及万维网上浏览的图像的通用色彩空间,最后选择的是使用gamma校准系数为2.2的色彩空间。这样,配合0.45的encoding gamma,就是0.45 * 2.2 = 1了。当然,1这个值不是绝对的,根据《Real-Time Rendering 3rd》书中的描述:在电影院这种昏暗的环境中,encoding gamma * display gamma = 1.5会让我们看起来更舒服。而在明亮的办公室环境中,encoding gamma * display gamma = 1.125会更好。
关于渲染
长期以来gamma矫正一直是容易被忽略的部分。我们会在gamma空间中采样纹理,获取颜色,计算光照,然后将结果输出,最后通过显示器的display gamma显示出来:
这个过程虽然简单,但是物理上却是错误的,计算的结果并不准确,但由于显示器的display gamma的存在,因此最终的显示效果还不错,所以人们一直都没有关心gamma矫正。
随着人们对沉浸式、照片级真实感的渲染要求,在gamma空间中的计算结果已经无法达到要求了。特别是PBR渲染是要求一定要在linear空间中完成的。
光照
如果我们在场景中渲染一个球体,你也许认为下图的渲染效果是正确的:


纹理
我们在Photoshop中新建一张8位图,从左到右让它从黑到白渐变:



Mipmap
Mipmap的计算是一种线性计算,需要在linear空间下完成。因此在计算Mipmap的过程中,也需要注意gamma矫正,否则就会得到错误的结果。
纹理过滤
纹理过滤也要注意gamma矫正的问题。对于bilinear过滤,先将采样后的值还原到linear空间下,然后再取平均。对于trilinear过滤,由于已经保证了mipmap计算的正确性,因此在不同的mipmap之间执行bilinear过滤也一定是正确的。
Alpha
Alpha存储的是不透明度,所以从纹理中采样alpha值时,不需要做gamma decode。需要注意的是alpha混合,它很容易受gamma矫正的影响。




后处理
当我们将计算的值写入frame buffer/render texture时,会做gamma矫正以配合显示器的display gamma。如果有后处理,当从frame buffer/render texture中采样时,要做gamma decode,将采样的值还原到linear空间下,因为后处理也需要在linear空间下计算才是正确的。
渲染总结
对于渲染,如果输入的颜色和纹理是位于gamma空间下的,则需要先进行gamma decode,将它们转换到linear空间下,在linear空间下的计算才是物理正确的。对于后处理效果,计算仍然需要在linear空间下完成。在全部计算完成后,将最终结果输出之前,需要做gamma矫正,配合显示器的display gamma,最后的显示效果才是完全正确的:
参考资料:
- Gamma Correction(https://learnopengl.com/Advanced-Lighting/Gamma-Correction)
- Gamma & Linear Color Space(https://www.jianshu.com/p/321f39b7fa93)
- 【图形学】我理解的伽马校正(Gamma Correction)(https://blog.csdn.net/candycat1992/article/details/46228771/)
- CRT显示器输入电压和显示亮度的关系(https://www.youtube.com/watch?v=Qmbg2eUwkT4)
- 了解Gamma Correction(http://aboutdada.com/?p=2171)
- 调色名词浅析——Gamma(伽玛)校正(https://zhuanlan.zhihu.com/p/37800433)
- 聊聊Unity的Gamma校正以及线性工作流(https://www.cnblogs.com/murongxiaopifu/p/9001314.html)
- GAMMA AND LINEAR SPACE - WHAT THEY ARE AND HOW THEY DIFFER(http://www.kinematicsoup.com/news/2016/6/15/gamma-and-linear-space-what-they-are-how-they-differ)
- 从0开始的OpenGL学习(二十九)-Gamma校正(https://www.jianshu.com/p/399e71392306)
- 对 Gamma 校正的个人理解(https://zhuanlan.zhihu.com/p/36581276)
- Real-Time Rendering 3rd
- Real-Time Rendering 4th
本文固定链接: https://www.jianshu.com/p/85a8d11b5cce
转载请注明: EnigmaJJ 2018年12月23日 于 简书 发表