理论支线:Gamma Correction 伽马校正
Gamma Correction
Gamma Correction 伽马校正,其实有两种意思,需要根据上下文判断 为了避免混淆,图形学领域倾向于使用:
- Gamma encoding(伽马编码):pow(color, 1/2.2) - 存储前做的
- Gamma decoding(伽马解码):pow(color, 2.2) - 显示器做的
- Display gamma:显示器的2.2伽马特性
一旦我们计算出场景的最终像素颜色,就必须将它们显示在显示器上。在数字成像的早期时代,大多数显示器都是阴极射线管(CRT)显示器。这些显示器具有物理特性:输入电压增加两倍并不意味着亮度翻倍。将输入电压加倍后,亮度等于显示器伽马值的指数关系约为 2.2。这(巧合地)也与人类测量亮度的方式非常相似,因为亮度也以类似的(反幂)关系显示。为了更好地理解这一切意味着什么,请看看以下图片
上线看起来是人眼正确的亮度比例,将亮度加倍(比如从0.1到0.2)确实看起来亮度翻了一倍,且差异很稳定。然而,当我们谈论光的物理亮度时,比如离开光源的光子数量,底部的刻度实际上显示的是正确的亮度。在最低光度下,将亮度加倍会恢复正确的物理亮度,但由于我们的眼睛对亮度的感知不同(更易受暗色变化影响),看起来很奇怪。
这里最下方是显示器输出亮度,中间虚线则是我们处理颜色的也就是线性颜色。如果将线性颜色增大2倍,那么显示器亮度会暴增4倍因为是pow(color,2.2)。这就会导致问题,为了抵消这个物理影响,所以我们需要进行反伽马或者叫Gamma encoding(伽马编码),也就是pow(color,1/2.2)这个操作 
sRGB textures
由于显示器在应用伽马后显示颜色,每当你在电脑上画画、编辑或绘画时,你是在根据显示器上看到的颜色来选择颜是在 sRGB 空间,也就是人眼感知空间,但是渲染器系统读取之后再渲染,会导致进行两次伽马pow(color,2.2)。当我们基于显示器上看到的图像创建图像时,我们实际上是伽马校正了图像的颜色值,使其在显示器上看起来正确。因为我们在渲染器中再次进行伽马校正,图像最终会变得过于明亮。
正常的图像创作流程:
- 美术在显示器上看到的图像(已经被显示器做了 gamma 2.2 解码)
- 美术保存图像时,图像编辑软件会做 gamma 1/2.2 编码在输出
- 保存的图像文件(如 PNG、JPG)已经是 gamma 编码过的 sRGB 数据
那么给到渲染器读取的是已经做 gamma 1/2.2 编码的图像数据,然后输出时又会做 gamma 1/2.2 编码,那么最终图像就会过曝。因此通常需要对图像进行预处理。 比如UE中就会对纹理进行预处理,对图像标记SRGB,先进行gamma 2.2 解码在进行使用,一般来说只针对颜色贴图会有这样操作,如果是参与计算的灰度数据图则不需要SRGB,因为肉眼不需要看到这个灰度,只是参与运算 
对光照衰减的影响
平方反比定律(真实物理)
现实世界中,光照遵循:
\[I = \frac{I_0}{d^2}\]其中:
- $I$ = 衰减后的光照强度
- $I_0$ = 光源初始强度
- $d$ = 距离光源的距离 也就是
1 2
float distanceSqr = distance * distance; float physicalAttenuation = 1.0 / max(distanceSqr, 0.01 * 0.01);
伽马空间下的衰减补偿调整
对于如果直接在Gamma Space下工作,直接使用物理衰减会导致过暗,因为Gamma Space相当于我们最终看到的效果,但是如果是渲染器渲染到屏幕最后还是会进行伽马矫正一次,那么渲染图像就会偏暗 正确的物理衰减是
伽马矫正就导致成了
\[I = (\frac{1.0}{d^{2}})^{2.2}\]为了补偿,可以使用不正确的线性衰减
\[I = \frac{1.0}{d}\]这样矫正后结果是
\[I = (\frac{1.0}{d})^{2.2}\approx\frac{1.0}{d^2}\]这和正确的物理衰减会更加接近
对于伽马空间的补偿方式其实可以忽略,因为现在游戏引擎都是使用线性空间工作,而不是伽马空间
线性空间处理
如果是在线性空间下工作,可以直接使用符合物理正确的衰减公式,不需要管其它,渲染到屏幕上最终会自动进行伽马矫正
Enjoy Reading This Article?
Here are some more articles you might like to read next: