计算机图像学(OPENGL):伽马校正

本文同时发布在我的个人博客上:https://dragon_boy.gitee.io

  当我们计算好场景中的每个像素的颜色后我们需要将其显示在显示器上。在过去我们往往使用CRT显示器,这种显示器由于某些物理属性,增加电压的倍数并不会增加同种倍数的亮度。两倍的输入电压会造成2.2倍的亮度提升,这个2.2的数值被粗略的称为显示器的伽马值。这个数值也恰好和人类感知的亮度相关。下面给出物理线性亮度变化和人类察觉的线性亮度变化:



  上一行的人类感知线性亮度更像是我们眼睛看到的亮度变化。然而当我们考虑物理亮度数值(光子层面)时,下一行反而是正确的亮度变化。所以就会造成很奇怪的感知现象,真实的和我们看见的是不一样的(我们更容易察觉亮度不是很高的变化)。
  正是由于人的眼睛像上面那一行一样感知亮度,所以显示器使用一个幂运算来增加输入的物理亮度来非线性映射到上一行那样的亮度变化。
  这个非线性映射的确可以输出让我们眼睛更舒服的亮度,但对于渲染层面就有一个问题:所有的颜色和亮度选项我们需要使用我们从显示器感知的来配置,因此所有的配置是非线性配置。看下面的图表:



  中间的点线代表线性空间的的颜色变化,实线代表显示器的颜色空间。如果我们在线性空间将颜色值增加两倍,结果就一是两倍。例如,一个颜色向量(0.5, 0.0, 0.0),2倍后就是(1.0, 0.0, 0.0)。但问题在于,原颜色向量在显示器上为(0.218, 0.0, 0.0),如果就这么在线性空间增加两倍,在显示器上相当于增加了4、5倍亮度。
  到目前的章节为止,我们都是在假设我们在线性空间操作颜色,但实际上我们是在显示器的输出空间来操作颜色,所以我们配置的颜色在物理层面并不准确,只是在显示器上看起来正确(我们眼睛观察的)。针对这个理由,我们一般会将颜色设置的比它本应有的亮度高一些(显示器会降低亮度显示),这样会让大多数线性空间的计算不正确。注意上面的图表,CRT和线性空间的曲线从同一点出发并在同一位置结束,在这中间的值会降低亮度显示。

  因为颜色是基于显示的输出配置的,所有的在线性空间的中间值在物理层面上都是不正确的。下面的图片显示了这种区别:



  可以看到如果使用了伽马矫正,颜色显示的非常好,在亮度角度处会有比较丰富的细节。如果没有伽马校正,为了获得比较正确的亮度显示,会浪费许多时间在颜色调整上。

伽马校正

  说白了,伽马校正就是对线性空间的颜色值进行反向伽马值的幂运算,到时候在显示器上进行显示时,显示器会对进行伽马矫正过的颜色值进行针对自己伽马值的幂运算,这样的话,我们只需要在线性空间调整颜色即可,在显示器上显示的颜色也在线性空间。
  下面给出一个例子。我们有一个深红色的颜色(0.5, 0.0, 0.0),我们首先进行伽马校正,颜色为{(0.5, 0.0, 0.0)}^{1/2.2} = {(0.5, 0.0, 0.0)}^{0.45} = (0.73, 0.0, 0.0),最后显示在显示器上会再进行一次幂运算,颜色为{(0.73, 0.0, 0.0)}^{2.2} = (0.5, 0.0, 0.0),可以看到使用了伽马校正,最终显示的颜色和我们在线性空间设置的一样。
  注意:2.2是大多是显示器的平均估计伽马值,通过进行此伽马值的幂运算得到的颜色空间被称为sRGB颜色空间(不完全是,但很接近)。每个显示器都有自己的伽马值,但2.2适用于大多数显示器,并可以得到很好的效果。
  在OpenGL中有两种方式使用伽马校正:

  • 使用OpenGL的sRGB帧缓冲。
  • 在片元着色器中进行手动伽马校正。
      第一种方法可能最简单,但可控性更低。通过开启GL_FRAMEBUFFER_SRGB我们告知OpenGL在每个绘制命令在存储颜色进颜色缓冲前先进行伽马校正。sRGB是和2.2伽马值相关的颜色空间,对大多数设备都适用。在开启GL_FRAMEBUFFER_SRGB后,OpenGL会在片元着色器执行后对片元的自动进行伽马校正,包括所有帧缓冲。
      这么开启GL_FRAMEBUFFER_SRGB:
glEnable(GL_FRAMEBUFFER_SRGB); 

  使用这个命令我们需要记住,伽马校正会将线性空间的颜色转化到非线性空间,所以我们一定要在最后阶段进行伽马校正,以免影响其它重要的操作。
  第二个方法如下,我们在片元着色器的输出颜色的最后使用伽马校正:

void main()
{
    // 在线性空间进行的操作
    [...]
    // 使用伽马矫正
    float gamma = 2.2;
    FragColor.rgb = pow(fragColor.rgb, vec3(1.0/gamma));
}

  这一方法的问题在于我们需要对所有相关的片元着色器使用相同的操作。操作可能过于繁琐,但我们可以使用帧缓冲绘制一个屏幕大小的平面来进行后期处理,这样我们只需要在一个片元着色器中进行伽马校正。

sRGB纹理

  在我们绘制纹理,图片时,我们会根据自己在屏幕上看到的颜色来选择颜色,这也就表示我们绘制的图片的颜色处在非线性空间即sRGB空间。
  所以说,我们在使用这些纹理时我们也需要将纹理处在sRGB空间的问题考虑在内。同样,由于我们是在线性空间进行渲染相关计算,我们可能会想去进行伽马校正,但同样会造成视觉上的问题:



  可以看到,使用了伽马矫正的话,纹理太亮了,这是因为它其实进行了两次伽马校正。当我们创建纹理时,我们基于我们在显示器上看到的颜色创建,我们会对这张纹理进行伽马校正来保证在显示器上会看起来不错,接着我们又在渲染时进行了伽马校正,这样就会导致图片看起来太亮了。
  为了解决这一问题,我们需要保证我们是在线性空间创建纹理。然而,在sRGB空间创建纹理更为容易,而且大多数工具并不支持线性空间的纹理,所以,这不是一个好办法。
  另一个方法是在片元着色器中进行。我们在进行颜色计算前先手动将sRGB纹理转化到线性空间:

float gamma = 2.2;
vec3 diffuseColor = pow(texture(diffuse, texCoords).rgb, vec3(gamma));

  如果针对每次纹理计算都这么做,可是不小的工作量。幸运地是,OpenGL提供了另一种方式,GL_SRGB和GL_SRGB_ALPHA纹理格式。
  如果我们在创建纹理时将纹理格式设置为GL_SRGB,那么OpenGL会自动将颜色转化为线性空间:

glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);  

  我们需要注意的是并不是所有的纹理都在sRGB空间,用于计算颜色的纹理(像漫反射纹理)大多数都在sRGB空间,用于检索灯光参数的纹理(如高光纹理和法线纹理)大多数在线性空间。

衰减

  之前讲过,光的衰减根据距离可以有平方衰减:

float attenuation = 1.0 / (distance * distance);

  以及线性衰减:

float attenuation = 1.0 / distance; 

  我们对上面两种衰减使用伽马校正来进行对比:


  这种差异的原因时灯光衰减会改变亮度,由于我们不使用线性空间观察场景,我们会使用在显示器上观看的更好的方式来衰减,但这在物理层面并不正确。对于我们的平方衰减,如果我们不使用伽马矫正,那么公式就会变成这样:

,这样的话在显示器上衰减的程度就太大了,线性衰减也是一样的。
  伽马矫正允许我们在线性空间进行光照计算,因为在线性空间计算的值在物理层面更为正确,大多数物理等式也能给出很棒的结果。使用的光照越复杂,使用伽马校正就可以得到更真实的结果。
  最后,贴出原文地址供参考:https://learnopengl.com/Advanced-Lighting/Gamma-Correction

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342