Gamma
设一个颜色的值为q(x∈[0,1]),设显示器展示出来的亮度值为w,设人眼看到的亮度值为e。
当显示器设备的gamma为2.2时,则w=q ^ 2.2
人眼相当于二次幂,即人眼看到的颜色亮度为e=q ^ 2。因此CRT这个缺陷正好能满足人的需要,即CRT上展示的颜色亮度,就为人眼中应该看到的颜色。
但有一个问题是:当我们对颜色值做变换的时候,在我们人眼中的颜色会发生意想不到的改变:
比如,光的颜色向量L=(0.5,0.0,0.0)代表的是暗红色。如果我们在线性空间中把它翻倍,就会变成(1.0,0.0,0.0),就像你在图中看到的那样。然而,由于我们定义的颜色仍然需要输出的监视器上,监视器上显示的实际颜色就会是(0.218,0.0,0.0)。在这儿问题就出现了:当我们将理想中直线上的那个暗红色翻一倍时,在监视器上实际上亮度翻了4.5倍以上!
知识点
启用OpenGL自带sRGB矫正:
glEnable(GL_FRAMEBUFFER_SRGB);
自定义gamma矫正:
void main()
{
// do super fancy lighting
[...]
// apply gamma correction
float gamma = 2.2;
fragColor.rgb = pow(fragColor.rgb, vec3(1.0/gamma));
}
颜色值的重矫:
float gamma = 2.2;
vec3 diffuseColor = pow(texture(diffuse, texCoords).rgb, vec3(gamma));
读取sRGB纹理:
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
衰减
float attenuation = 1.0 / distance;
我创建的这个简单的demo场景,你可以在这里找到源码以及顶点和像素着色器。按下空格就能在有gamma校正和无gamma校正的场景进行切换,两个场景使用的是相同的纹理和衰减。这不是效果最好的demo,不过它能展示出如何应用所有这些技术。
总而言之,gamma校正使你可以在线性空间中进行操作。因为线性空间更符合物理世界,大多数物理公式现在都可以获得较好效果,比如真实的光的衰减。你的光照越真实,使用gamma校正获得漂亮的效果就越容易。这也正是为什么当引进gamma校正时,建议只去调整光照参数的原因。