伽马空间定义:通常用于描述图像在存储和显示时的颜色空间。在伽马空间中,图像的保存通常经过伽马转换,使图片看起来更亮。
gamma并不是色彩空间,它其实只是如何对色彩进行采样的一种方式
为什么需要Gamma:
在游戏业界长期以来都忽视一个伽马矫正的问题,这就导致渲染出来的效果要么过曝,要么过暗,总是和真实世界不同。
尽管是使用了同一shader渲染,但选择颜色空间不同和处理gamma矫正时做法不同,就导致结果不同
CRT的亮度和施加电压不成线性
人眼和响应不成线性
unity中可以在此处选择不同的颜色空间,如图
一、Gamma空间与线性空间
什么是线性颜色空间?
如图,横轴为输入给显卡进行渲染的的颜色,纵轴为显卡渲染后输出到显示器的颜色结果
所有线性空间就是输入等于输出的
输入的颜色不进行任何处理 Gamma值为1
但现实世界要做到输入颜色等于输出颜色是很难的
因为显示器的颜色输出不是线性的,不同显示器输出的也是不同的
人眼对光强的感知能力并不是线性的——对暗部颜色变化更敏感,但很难识别亮部的颜色差异。
RGB=0.5的灰色,但其物理亮度(光子数)却只有纯白(RGB=1)的22%。
这时为什么呢?为何不是线性的呢?
人们之所以不设定RGB值与亮度值线性相关,是因为人眼更善于发现暗部中亮度的细微差别。
人眼对光强的感知能力并不是线性的——对暗部颜色变化更敏感,但很难识别亮部的颜色差异。
在某些阴暗的环境下,点亮一盏灯,这时人眼就会觉得非常亮。如果同时点亮1000盏灯,反而觉得只是10倍的亮度,对亮度的认知相当于从0~1再从1~10.
因为人眼不易识别亮部的颜色差异,这就导致了在亮部,我们不需要那么多的空间去存储亮度值,因为人眼根本就难以区别它们,如果按照线性存储,就会导致亮部空间的浪费。
所以在电子图象存储技术中,人们降低亮部的采样数来节约磁盘空间——或者说,在不增加数据量的前提下,分配更多的采样数给暗部以提高可辨识精度。
相机捕获到现实世界真实的光的信息,经过处理后显示到屏幕上,我们希望物理世界中的真实值能够于显示器中的呈现值相同,那么怎么做呢?
先来看一下这个做法的演变过程
从最简单的模型演变
(1)图片真实值0.2->(亮部浪费)图片存储值0.2->提交值0.2->(pow 2.2)呈现值0.029 (缺点:浪费存储空间、早期计算机无法直接让呈现值等同于提交值)
(这个2.2是历史问题,但后来对我们是有益的,所有就沿用下来并未修复)
改善以上问题:
(2)图片真实值0.2->(pow 0.45)图片存储值0.48->提交值0.48->(pow 2.2)呈现值0.2
游戏业界带来新的问题:
(3)图片真实值0.2->(pow 0.45)图片存储值0.48->光照计算0.48*2->提交值0.96->(pow 2.2)呈现值0.914(严重偏离真实值0.2*2=0.4)
二、伽马矫正
最后我们的具体做法是:相机捕获到现实世界真实的光的信息,然后对这些数据开n次方根(n即gamma值,一般取1.8~2.2),,然后通过采样存储图像。这样存储的图像就是经过Gamma校正的图像。
线性的光信号变成了非线性的电信号。
而在显示器上显示图片时,则需要做一次逆Gamma校正:
将非线性的电信号转换成线性的光信号。
总结一下就是:
想要游戏的效果接近真实世界,那么就需要保证在光照计算之前处于线性空间!
= Gamma矫正=Gamma 0.45 提亮
Gamma 2.2 = Gamma 空间
Gamma 1 = 线性空间
Color * Gamma 1/2.2 * Gamma 2.2 = Color * Gamma 1 = Color
假如摄像机不做Gamma矫正,会是什么情况?
三、sRGB
unity在贴图的导入设置面板中有“sRGB(Color Texture)”选项,勾选它,告诉Unity这是一张sRGB贴图,需要对它做sRGB解码才可以获得用于光照计算的数据;不勾选,即表示这是一张线性贴图,这张贴图的RGB数据可直接用于光照计算。
sRGB是一种颜色空间,它的定义包括White Point、Primaries、OETF、EOTF
Color Space | White Point | Primaries | OETF | EOTF |
sRGB | D65 | R(0.640,0.330), G(0.300,0.600), B(0.150,0.060) | δ=12.92, β=0.0031308, α=1.055, γ=12/5 | 2.2 |
Rec.709 | 同 sRGB | 同 sRGB | δ=4.5, β=0.004, α=1.099, γ=20/9 | ~2.4 |
sRGB标准和Rec.709标准所定义的白点、三元色相同,只是传递函数的定义不同;
下图的马蹄形区域表示人眼能够感知到的最大色域,三角形围起来的区域即 sRGB / Rec.709 的色域:
1.色域: sRGB首先设定了RGB三个基色的坐标
2.白点: sRGB也规定了白点位置
3.gamma: sRGB的gamma设定为≈2.2也就是说从外而向内切,先切的很细,然后逐渐变粗
sRGB的编码伽马值实际是12/5=1/2.4,但我们常听说的是1/2.2,这是因为完整版本的伽马函数【1.055*x ^(1/2.4)-0.055】在形状上更接近【x ^(1/2.2)】,如下图的黑线更接近红线,而不是绿线,红线【x ^(1/2.2)】就被大家当做是简化版本的伽马矫正啦:
渲染时的图片来源,不论是照片还是DCC软件,还是手绘和渲染,都是在sRGB颜色空间下工作的
Substance Painter导出贴图时,会对提供色彩信息的贴图(比如Albedo和Specular Color)做sRGB编码(目的是为了让贴图看起来更舒服,匹配人眼感知,同时优化数据存储),所以我们应把它们当作sRGB贴图来使用;而对法线贴图/金属度贴图这类用以存储数值信息的贴图,是不做sRGB编码的,所以我们应把它们当作线性贴图来使用。
对sRGB编码的图片是直接使用还是转换使用?
线性空间:直接用(系统替我们转换)
Gamma空间:手动转换
四、Unity线性空间流程图
关于 Gamma 与线性的混淆
色彩空间为什么那么空?色度系统、色度图、色域、伽马、白点...