惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
Attack and Defense Labs
Attack and Defense Labs
GbyAI
GbyAI
月光博客
月光博客
Recent Announcements
Recent Announcements
云风的 BLOG
云风的 BLOG
F
Full Disclosure
宝玉的分享
宝玉的分享
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
爱范儿
爱范儿
博客园 - Franky
V
V2EX
Recorded Future
Recorded Future
WordPress大学
WordPress大学
小众软件
小众软件
Webroot Blog
Webroot Blog
雷峰网
雷峰网
Vercel News
Vercel News
N
News and Events Feed by Topic
PCI Perspectives
PCI Perspectives
Cyberwarzone
Cyberwarzone
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
W
WeLiveSecurity
S
Schneier on Security
博客园 - 三生石上(FineUI控件)
K
Kaspersky official blog
F
Fortinet All Blogs
Security Archives - TechRepublic
Security Archives - TechRepublic
Spread Privacy
Spread Privacy
博客园 - 叶小钗
罗磊的独立博客
D
Docker
Forbes - Security
Forbes - Security
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The Register - Security
The Register - Security
A
About on SuperTechFans
B
Blog RSS Feed
I
InfoQ
T
Tailwind CSS Blog
G
Google Developers Blog
H
Help Net Security
V
Vulnerabilities – Threatpost
AWS News Blog
AWS News Blog
N
News and Events Feed by Topic
M
MIT News - Artificial intelligence
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
P
Proofpoint News Feed
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
MyScale Blog
MyScale Blog
Latest news
Latest news

博客园 - 明明1109

MBD笔记:基于滑模控制PMSM矢量控制 MBD笔记:三相永磁同步电机的矢量控制(四) 自动控制:采样系统理论 MBD笔记:IPMSM 速度控制模型解析 MBD笔记:SPWM技术(三) 自动控制:自动控制系统数学模型 自动控制:基本原理 MBD笔记:SVPWM技术(二) MBD笔记:三相PMSM数学模型(一) 旋转编码器 计算机图形:高级GLSL XMODEM协议解析 电机控制笔记:BLDC控制原理 STM32F407启动文件 MBD笔记:PWM与DC-DC变换器的模型设计 MBD笔记:放大器与滤波器模型设计 电机控制笔记:PMSM无位置传感器控制介绍 电机控制笔记:理解直流无刷电机矢量控制 拉普拉斯变换 FOC算法笔记 AUTOSAR笔记:模式管理 AUTOSAR笔记:通信 AUTOSAR笔记:SWC AUTOSAR笔记:IO系列驱动 AUTOSAR笔记:OS 电路基础:电感 电路基础:电容器 电路基础:交流电路 电路基础:戴维南定理、诺顿定理
计算机图形:gamma校正
明明1109 · 2026-03-25 · via 博客园 - 明明1109

Gamma校正是什么

Gamma校正:让显示器的亮度,和人眼感知的亮度保持一致的修正过程.

本质上,是因为人眼对光的感知是非线性的,数字设备(如显示器)显示光强也是非线性的.

当我们计算出场景中所有像素最终颜色后,需要将其显示在显示器上. 大部分显示器(如阴极射线管显示器,CRT)有个物理特性:2倍的输入电压 并不能产生 2倍的亮度. 将输入电压加倍产生的亮度,约为输入电压的2.2次幂,这叫显示器Gamma.

Gamma也叫灰度系数,每种显示设备都有自己的Gamma值,都不相同. 有个公式:

\[设备输出亮度 = 电压的Gamma次幂, \]

几乎任何设备Gamma都不会等于1,1是一种理想状态:如果电压和亮度\(∈[0,1]\),那么多少电压就等于多少亮度.
对于CRT,Gamma通常为2.2. 于是,

\[显示器的输出亮度 = 输入电压的2.2次幂 \]

人类感知的亮度,刚好跟CRT所显示的相似指数关系非常匹配. 如下图:

img

第一行(Perceived (lienar) brightness)是人眼感知亮度. 人眼所感知到的正常的灰阶,亮度增加1倍(如从0.1到0.2),我们才会感觉到比原来亮了1倍,才能感受到明显的颜色变化. 例如,颜色值从0.1到0.2,我们会感受到1倍的颜色变化,而从0.4到0.8 我们才能感受到相同程度的颜色变化(变亮1倍).

第二行是物理世界的真实亮度. 亮度加倍时,返回的也是真实的物理亮度(光子数量)
注:从0~1的过程中,亮度要增加1倍,我们才会感受到明显的颜色变化(即变亮1倍),即物理亮度.

也就是说,人眼看到颜色的亮度,并非线性亮度;而显示器使用的也是一种指数关系(电压的2.2次幂),所以物理亮度通过显示器能被映射到顶部的非线性亮度. 如此,显示器的亮度变化效果看起来还不错.

下图展示了显示器的非线性颜色、亮度的映射关系:

img

横轴代表我们定义的颜色(我们想输出的颜色),纵轴是亮度.

从右到左3条线描述的颜色、亮度关系,分别是:CRT显示器(gamma 2.2),理想状态(gamma 1),gamma校正(gamma 1/2.2)

例如,光的颜色\(L=(0.5,0.0,0.0)\)代表半暗红色,如果将其颜色翻倍,那么颜色变成\((1.0,0.0,0.0)\).

对于理想状态(中间的点线),颜色翻倍,则亮度翻倍;
对于显示器,显示的颜色0.5对应亮度0.218,颜色翻倍后,亮度翻倍4.59. 这显然不是我们想要的结果!

于是,我们引入Gamma校正,来修正这种非线性关系.

Gamma校正怎么做

流程

流程:

计算线性颜色(纹理采样、光照计算等)---> Gamma校正 ---> 显示颜色(显示器特性,应用Gamma 2.2曲线)

Gamma校正(Gamma Correction,伽马校正)思路:在最终的颜色输出到显示器之前,将Gamma的倒数作用到颜色上. 即上图最左边的那条虚线(称为逆伽马曲线),作用于显示器的Gamma 2.2曲线相反. 将每个线性输出颜色 乘以 逆伽马曲线,这样,显示器最终显示颜色时,就会变成线性的.

应用伽马校正前,显示器中间颜色偏暗;应用伽马校正后,中间颜色会变亮,整体会平衡.

例如,我们想显示暗红色\((0.5,0.0,0.0)\),在将颜色显示到显示器前,先对其应用Gamma校正曲线(逆Gamma曲线).

理想的亮度是0.5(中间点线),在显示器上显示相当于降低了2.2次幂的亮度(右边实线),即\((0.5,0.0,0.0)^{2.2}=(0.218,0.0,0.0)\)

因此,Gamma校正要将倒数\(1/2.2\)次幂作用到该颜色上,即\((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)\) (颜色0.5,亮度0.5)

具体方法

在我们的场景中,有2种应用Gamma校正的方式:

  1. 使用OpenGL内建的sRGB帧缓冲;
  2. 自己在像素着色器中进行Gamma校正.

方式1最简单,缺点是会丧失一些控制权. 开启 GL_FRAMEBUFFER_SRGB选项,告诉OpenGL每个后续的绘制命令,在颜色存储到颜色缓冲之前,先校正sRGB颜色. sRGB颜色空间大致对应Gamma 2.2,也是大多数设备的标准. 开启该选项后,每次像素着色器运行后续帧缓冲,OpenGL自动执行Gamma校正,包括默认帧缓冲.

开启 GL_FRAMEBUFFER_SRGB

glEnable(GL_FRAMEBUFFER_SRGB);

注意:所有的工作由OpenGL自动完成,我们无需添加额外的Gamma校正相关操作.

方式2灵活,但需要我们做更多工作. 我们在每个相关的像素着色器运行结束之前应用Gamma校正,最终颜色发送到显示器之前就结束了Gamma校正.

// 片段着色器
void main()
{
    // 计算光照等
    ...
    // 输出颜色前的最后一步
    // 应用Gamma校正
    float gamma = 2.2;
    fragColor.rgb = pow(fragColor.rgb, vec3(1.0/gamma));
}

注意:Gamma校正将颜色从线性空间转换为非线性空间. Gamma校正必须在输出颜色前的最后一步进行,如果在这之前进行,就不是在线性空间操作颜色值.

sRGB纹理

sRGB纹理 是指纹理图像的颜色数据被存储在 sRGB 色彩空间(而非 线性RGB色彩空间)中的纹理. sRGB空间就是经过 Gamma 校正后的颜色空间,是非线性的.

平时,我们看到的普通图片,如jbg、png等,几乎都是已经自带 Gamma 压缩的图片,即sRGB纹理

为什么会存在sRGB纹理?

为了在有限的 8bit 存储空间里,让人眼看到的颜色尽可能均匀、好看、不浪费精度.

具体来说,体现在这几点:

  1. 人眼对亮度感知并非线性
  • 亮度从 0 → 0.1:人眼觉得变化巨大
  • 亮度从 0.9 → 1.0:人眼几乎看不出区别

如果不用sRGB,而用线性RGB存储颜色,那么0–1 平均分配 256 级(0~255),结果就是:暗部只有很少几级,容易出现色带、断层;亮部一大堆精度,完全浪费.

  1. 显示器也不是线性输出的

CRT显示天然特性:电压 -> 亮度 呈现非线性曲线特性,\(γ≈2.2\)

电压0.5,显示亮度不是0.5,而是\(0.5^{2.2}≈0.218\),会更暗.

  1. 于是,专家设计了sRGB标准

提前将图片颜色做一次Gamma压缩(Gamma编码),即颜色的\(1/2.2\)次幂.

RGB空间和sRGB空间关系示意图:

                     ┌─────────────────────────────────────┐
                     │          物理/线性空间               │
                     │    (光照计算、渲染方程、物理模拟)     │
                     │    数值与光子数量成正比              │
                     └───────────────┬─────────────────────┘
                                     │
            ┌────────────────────────┼────────────────────────┐
            │                        │                        │
            ▼                        ▼                        ▼
   ┌────────────────┐      ┌────────────────┐      ┌────────────────┐
   │  相机传感器    │      │  渲染输出      │      │  GPU 采样时    │
   │  线性响应      │      │  (帧缓冲)      │      │  自动解码      │
   └───────┬────────┘      └────────────────┘      │  sRGB→Linear  │
           │                                        └───────┬────────┘
           │ 编码 (1/2.2)                                   │
           ▼                                                │
   ┌────────────────┐                                       │
   │  sRGB 图像文件 │ ◀─────────────────────────────────────┘
   │  (PNG/JPG)    │     采样时硬件自动转回线性
   │  存储优化     │
   │  显示器兼容   │
   └───────┬────────┘
           │
           │ 显示时,显示器天然 Gamma (~2.2)
           │ (sRGB 显示器已校准,输出正确亮度)
           ▼
   ┌────────────────┐
   │   人眼感知     │
   │   (非线性)    │
   └────────────────┘

在我们的OpenGL程序中,如果要计算高级光照,流程是这样的:

  1. 加载 sRGB 图片,stbi_load
  2. 转成线性空间(用 GL_SRGB 纹理格式),glTexImage2D
  3. 在线性空间计算光照,片段着色器中,用户自定义程序
  4. 最后输出时做 gamma 校正(^1/2.2),片段着色器输出颜色前的最后一步
  5. 显示器再 \(γ 2.2\) → 最终正确,显示器自动完成

注意:现代液晶(LCD / LED / OLED)显示器,全都在刻意模拟 CRT 那套~2.2 的 Gamma 曲线,因为标准强制要求,否则CRT Gamma 2.2条件下生成的内容都会过亮、发白、完全看不清.

参考

Gamma校正 | LearnOpenGL