图形学基础 | Gamma校正

http://geekfaner.com/unity/blog1_gamma.html
https://gameinstitute.qq.com/community/detail/120396

随着真实性更高的基于物理渲染(PBR)的到来,线性空间(Linear space)光照计算也越来越被经常提及。
虽然线性空间和与之“对立”的伽马空间(gamma space)是简单而重要的概念。
但很多开发者对它们的真正意义并不了解。

PBR对gamma校正和HDR有很大需求

线性空间

线性空间是什么?
简单的说,在线性空间对数字化的颜色和光照强度进行相加相乘计算得到的结果依然能与真实的结果一致
不具备这条性质就被称为“非线性”空间。见下图:
在这里插入图片描述

Gamma空间

在这里插入图片描述

Gamma校正从何而来

开发gamma编码是用来抵消阴极射线管(CRT)显示器的输入和输出特性。
电子枪的电流,也就是光的亮度,与输入的正极电压的变化是非线性的。
通过gamma压缩来改变输入信号抵消了这个非线性,因此输出图像就能有预期的亮度。

所以,gamma校正和人眼特性无关,仅仅和CRT有关。更新的显示方法,比如LCD和等离子之类,为了保证兼容,也都选择了和当年CRT一样的非线性特性。(其实和系统有关,Mac OS X 10.6就用的1.8,其他系统,包括电视,都用的2.2)

Gamma计算很简单,只是个power而已,也就是:

C o l o r o u t = C o l o r i n g a m m a Color_{out} = Color_{in}^{gamma} Colorout=Coloringamma

其中的 g a m m a gamma gamma 就是用来校正的 gamma值.

输入与输出

现在让我们来看看一个输入输出的例子。假设相机是线性的,显示器也是线性的,那么输入和输出的关系就是:
在这里插入图片描述
也就是通过相机拍照后,在显示器上看到的和真实场景的色彩一样。
可惜,现实是残酷的,显示器的gamma为2.2,所以如果相机仍然是线性的,那么结果就会变成:
在这里插入图片描述
这样在显示器上看到的就会有明显的色彩失真。解决方法是把相机的gamma设成1/2.2,这样两次调整之后又能得到真实场景的色彩了:
在这里插入图片描述
其实从这个过程也可以看出,gamma校正是为了在输入和输出的环节中保证能和真实场景一致,而眼睛不在这个环节中,所以和眼睛对亮度的感受没有直接关系了。

对渲染的意义

前面讲的输入都是对相机拍的照片来说.

而对渲染来说,情况又如何呢?
渲染中用到的光照都是在线性空间的。
因为在设计光照的时候都是认为1的亮度是0.5的2倍。
光照如此,texture又如何呢?渲染中用到的 texture一般有两个来源,
一个是照片,一个是artist手工画的。前文提到了,照片是gamma = 1/2.2的。一般图象处理软件也都是在gamma空间工作的,
所以artist画的图一般也可以认为是gamma = 1/2.2的。
所以,我们在pixel shader常可以见到这样的代码:

float4 diff = tex2D(diffuse_texture, uv);
return diff * max(0, dot(light_dir, normal));

这样的代码对吗?不对也对。
说其不对,是因为这里没显式地做gamma校正。做校正的话应该是这样的:

float4 diff = pow(tex2D(diffuse_texture, uv), 2.2f);
return pow(diff * max(0, dot(light_dir, normal)), 1 / 2.2f);

也就是说,gamma校正的过程就是把输入的texture都转换到线性空间并把输出的调整到gamma = 1/2.2的空间。

说其对,是因为如果diffuse texture如果是sRGB格式的,那么再读取的时候硬件会把它自动转到线性空间。
如果render target的texture也是sRGB格式的,在输出的时候硬件也会把它自动转到线性空间。
所以,如果输入和输出纹理都是sRGB,那么原先那段shader就是正确的。对于不支持sRGB的老硬件,就必须自己做pow了。

除了渲染,另一个需要注意gamma的地方就是mipmap。如果原texture是gamma =1/2.2的,那么在建立mipmap chain的时候,每一层都必须和渲染一样,先转到线性空间,计算之后再转到gamma = 1/2.2的。否则,255和0混合得到的是51,而不是128。(Patrick:51怎么得到的,应该是55吧)

总结

总之,计算都要发生在线性空间,所以输入和输出需要进行gamma校正。
最佳选择是采用sRGB格式,这样pow是硬件内自动实现,速度更快,代码也简单。
鉴于目前很多texture的数据是gamma = 1/2.2的,
而纹理格式却被错误地标记成没有sRGB的,
所以需要修改它们的格式标记,并重新建立mipmap。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值