为什么需要Gamma(伽马)校正
因为肉眼对于亮度的感知是非线性的,即对较暗的亮度感知灵敏,对于较亮的亮度感知度逐渐降低,当亮度达到一定值人眼才会感觉到有变化。而当年的CRT(阴极射线管显示器)与电压的对应关系恰好和肉眼相似。也就是说显示出来的图像亮度也是非线性的。如今的显示器依旧保留了这个特点。而自然界的亮度又是线性的,即光的粒子越多越亮。自然中的线性亮度输入到屏幕上就变暗了,原因在于肉眼基于自然亮度会进行非线性处理进行感知,而显示器在给显示给肉眼前也进行了一次非线性处理,这就是变暗的原因,这也是需要校正的原因之一,需要消除这一次非线性处理。
然而显示器上的亮度需要进行调整的,而我们只能进行线性的调整操作,这也会导致
问题出现:
当把正比的虚线(线性自然亮度)亮度0.5调整到1.0时,感知的亮度实际上是从0.218到1.0。很显然不对这导致了肉眼感知的亮度跨越了4.5倍,而实际上我们只想让他提升2倍。即自然亮度的2.2次方才是显示的亮度值。这样直接显示当前亮度也会导致0.5的暗红色输入到显示屏上变成0.218超级暗红,接近黑色了都。
解决:
为了解决这个问题就把自然亮度进行1/2.2次方再进行显示即(线性亮度值)的1/2.2次方再2.2次方。输入0.5屏幕上显示的还是0.5,这样就保证了屏幕上显示的是我们期望的亮度,且调整倍数也是我们期望的倍数。这种原理就是gamma校正。
需要校正的内容:
通常纹理都是在sRGB或者GL_SRGB_ALPHA标准下定义的,这个标准是已经gamma校正过的(1/2.2次方化),而少部分像specular贴图和法线贴图几乎都在线性空间中定义的,因为要获取光照参数,如果校正就会出错,因此只能部分校正,否则会导致两次校正使亮度过亮。也就是只对在非线性空间定义的纹理和应用程序中定义的颜色进行校正(漫反射贴图)。但这不好统一校正,为了能简单的进行统一校正就能得到正确的结果,可以把在sRGB或者GL_SRGB_ALPHA标准下定义的纹理进行线性化处理(重校)。
方法:
一、可以在着色器中通过如下代码重校,即纹理采样时重校。这种方法的麻烦之处就是如果有多个shader会增加工作量。
这里我还没学法线贴图因此没管,等以后学了加上就行了。
shader中
float gamma = 2.2;
vec3 diffuseColor = pow(texture(diffuse, texCoords).rgb, vec3(gamma));
二、创建纹理时处理,在参数2处写成sRGB或者GL_SRGB_ALPHA。当然之前判断一下如果不是镜面反射贴图或者法线贴图就进行如下重校设置。
shader中
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
这里我是用的是第二种方法。
效果:
校准前(所有颜色数据都是线性的)
校准后:
目录
VSC++2019+QT+OpenGL
QT+OpenGL一之绘制立方体(三角形图元)
QT+OpenGL二之纹理贴图
QT+OpenGL三之矩阵简解
QT+OpenGL四之相机的移动和旋转
QT+OpenGL五之绘制不同的模型(vao,vbo机制)
QT+OpenGL六之天空盒
QT+OpenGL七之使用EBO
QT+OPenGL八之模型准备
QT+OPenGL九之模型解码
QT+OPenGL十之光照模型
QT+OPenGL十一之漫反射和镜面反射贴图
QT+OPenGL十二之定向光
QT+OPenGL十三之真正的点光源和聚光灯
QT+OPenGL十四之多光源混合的问题
QT+OPenGL十五之深度缓冲区
QT+OPenGL十六之模板缓冲区
QT+OPenGL十七帧缓冲区(离屏渲染)
QT+OPenGL十八抗锯齿
QT+OPenGL十九镜面反射效率调整
QT+OPenGL二十Gamma校正