伽ga马校正
- 物理亮度 = 光子数量
- 线性空间:光子数(亮度)和颜色值的线性关系
- 人眼感知的亮度:对比较暗的颜色变化更敏感,感知亮度基于人的感觉
- 非线性空间:光子数(亮度)和 颜色值^2.2,
- 恰好符合屏幕显示的CRT2.2(显示设备都有自己的Gamma值,设备输出亮度 = 电压的Gamma次幂)符合人眼规律,也就是屏幕自动的为我们更改符合人眼规律的亮度
注意:
- 应用中配置的亮度和颜色是基于监视器所看到的,是非线性的配置
- 也就是:我们把物体颜色值翻倍,将颜色值改变为【0.5——1】,可是在监视器中,我们看到的颜色变化其实是【0.25——1】,亮度翻了4.5倍以上
- 通常将光照值设置得比本来更亮一些(由于监视器会将其亮度显示的更暗一些)
Gamma校正(最终颜色变回线性):
- 在最终的颜色输出上应用监视器Gamma的倒数(1/2.2次幂),这样应用了监视器Gamma以后最终的颜色将会变为线性的
- 方法一:
- glEnable(GL_FRAMEBUFFER_SRGB);在颜色储存到颜色缓冲之前先校正sRGB颜色。包括默认帧缓冲。
- 注意:如果你使用多个帧缓冲,应只将发送给监视器的最后的那个帧缓冲应用gamma校正。让两个帧缓冲之间传递的中间结果仍然保持线性空间颜色,
- 方法二:
float gamma = 2.2;
fragColor.rgb = pow(fragColor.rgb, vec3(1.0/gamma));
-
- 第二个方法有完全的控制权:很好了解决了上面多个帧缓冲的问题,但是同时有很多Fragment着色器的话,必须在像素着色器里加上这个gamma校正
gamma校正的问题:(针对外部引用的sRGB空间创作的资源)
- sRGB空间(Gamma == 2.2):创建或编辑的图片,是在非线性空间进行,比如将颜色翻倍,实际上是根据你所感知到的亮度进行的,并不等于将颜色值加倍。
- 在我们没有应用gamma校正,没有问题的,纹理在sRGB空间创建和展示,同样我们还是在sRGB空间中使用,
- 当应用gamma校正之后:纹理在sRGB空间创建和展示,在线性空间中使用,纹理图像会特别亮,
- 原因是:基于sRGB空间中显示的更暗一些,纹理制作者设置的颜色值就比本来更亮,当将这个颜色值应用gamma校正之后,转变为线性空间,颜色会比没有应用gamma校正看到的更亮
解决:
- 确保纹理制作者是在线性空间中进行创作的,这样颜色值是正确的,应用gamma校正之后,亮度也是相同的
- 但是:大多数纹理制作者并不知道什么是gamma校正,另一个解决方案是重校:
- 每个sRGB空间的纹理:在进行任何颜色值的计算前^2.2,变回监视器看到的颜色效果
- 但是:为每个sRGB空间的纹理做这件事非常烦人。OpenGL给我们提供了另一个方案,glTexImage2D(),GL_SRGB和GL_SRGB_ALPHA内部纹理格式。
另一个不同之处是光照衰减:
- 物理公式:光照的衰减和光源的距离的平方成反比。
- 二次函数:1.0 / (distance * distance);但是衰减效果总是过于强烈,光只能照亮一小圈,看起来并不真实
- 双曲线方程:1.0 / distance;
- 不用gamma校正的时候,双曲线比使用二次函数变体更真实,开启gamma校正以后,符合物理的二次函数突然出现了更好的效果
- 不用gamma校正,为什么?双曲线比使用二次函数变体更真实?
- 二次函数:(1.0/distance2)2.2将产生更强烈的衰减,双曲线1.0/distance2.2,和物理公式是很相似
Gamma校正作用:
- 线性空间更符合物理世界,大多数物理公式现在都可以获得较好效果,
- 应用中配置的亮度和颜色,在不同的显示设备上呈现一致的效果,正确的设置物理颜色值