【翻译】伽玛和线性空间 - 它们是什么以及它们有什么不同

2018-05-07  本文已影响179人  诸葛飞

当游戏进入到下一个层级的真实性 - 基于物理的渲染(PBR)模型的时候,游戏开发者越来越多的听到线性空间这个术语。尽管线性空间和伽玛空间是理解起来非常简单和重要的概念,但很多开发者仍然没有学会这两个术语的真实含义。这便文章将会明确伽玛和线性空间,它们的不同,以及如何应用到 Unity 引擎中。

线性空间

首先我们需要知道什么是线性空间。简而言之,就是数值强度值,与感知强度是成比例对应的。这意味着颜色可以被正确的加和乘。一个没有这种属性的颜色空间称为“非线性”。下面是一个例子,演示在线性和非线性空间中强度值翻倍。在线性空间中对应的数值是正确的,但是在非线性空间中(gamma = 0.45,后面会继续讲这个)我们不能简单的将值翻倍来取得正确的强度。

在线性和非线性空间中将深灰色方块的强度翻倍

伽玛空间

之所以需要伽玛空间有两个主要的原因:一个是屏幕和强度的对应关系是非线性的。另一个是相对于浅色的渐变色,人眼更容易区分暗色的渐变色的不同。这意味着当图像压缩以节省空间的时候,我们希望牺牲一下浅色强度的精度,以让暗色的强度可以获得更高的精度,这两个问题都可以通过伽玛校正来解决,这就是说每个像素的强度都是通过一个幂函数存储到图像中。伽玛就是应用到图像上的幂的名字。

pow(x, gamma)的曲线图

看一下上面那个曲线图,显然除1之外的任何伽玛都是非线性空间。

两个常用的伽玛应用到中间的图像上

大多数图像都是以伽玛 0.45存储的,这将会有左侧图像显示的那样的效果。图像的暗色区域使用更大范围的数值存储,而浅色的范围被压缩了。被这样存储的图像就是在“伽玛空间”。比如,图像中一个中间的灰度不是数值0.5,而是0.73,然而纯黑和纯白仍然是一样的。这几乎是所有的数码相机的默认行为,图像编辑程序也是一样的。事实上,你电脑上几乎所有的图像都应用了这个伽玛。

你可能会好奇为什么图像显示是正确的呢,而且没有看起来更亮。这就要说到显示器的非线性对应关系了。阴极射线管(CRT)屏幕,因为他们的工作方式,应用了一个大约2.2的伽玛,现代的液晶显示器被设计来模仿这种行为。2.2的伽玛,是0.45的倒数,当应用到明亮的图像的时候,会使它们变暗,从而显示原始的图像。

比如,上面左图表示的是在你电脑中存储的样子,当被显示的时候,结果会像中间的图片。左图被“伽马校正”了,这意味着它有一个伽马值,这样当屏幕的伽马应用到它的时候,它会被正确的显示。如果电脑中存储的是中间的图片,它没有被伽玛校正,那么显示的结果会像右边的图片,这是明显不想要的结果。

颜色空间和渲染管线

当在渲染中使用伽玛管线的时候,传到着色器中的纹理是经过伽玛校正的。然后是光照计算。然后,最终的图像输出到显示器并应用显示器的伽玛值。这种做法很简单,却不是物理正确的。在现实生活中,光照的表现是线性的,这意味中多个光源的贡献被加在一起来获得正确的强度。正因如此,着色是在线性空间进行的。但是在伽玛管线中,输入的颜色和纹理仍然在伽玛空间中。这意味着着色的结果不是真的精确,但在应用了显示器的校正之后,结果往往也不错。然而随着对身临其境的、照片级真实渲染的需求增加,这不再是一个合适的方法。

因此,在PBR中,典型的做法是使用线性管线。这里,输入的颜色和纹理在着色之前就移除了伽玛校正,转换到了线性空间。当着色后,结果是物理正确的,因为着色处理和输入都是在同样的空间中。然后,所有的后处理都应该在帧仍然在线性空间中时进行,因为后处理基本上都是线性的,就像着色一样。最终图像会被伽玛校正,这样在显示器的伽玛调整后会有一个合适的强度。

伽玛和线性管线对比

上面你可以看到伽玛和线性管线在一个简单球体上的最终结果。注意光的镜面高光和强度在伽玛空间中的衰减。这是不真实表现的例子,很难取得照片级的真实性。

Unity 中的颜色空间

幸运的是,Unity 可以很容易的在颜色空间中切换,对大多数项目而言,都可以和你的渲染管线无缝整合。Unity 默认使用伽玛空间,仅在PC,Xbox,和游戏机平台上支持线性渲染。对这些平台来说,在线性和伽玛空间之间切换,去到:

 Edit -> Project Settings -> Player -> Other Settings

这里,有一个叫做Color Space的选项,你可以选择Linear或者Gamma。就这么简单。着色器现在应该接收非伽玛校正的纹理了。注意,现在渲染场景看起来和以前不怎么一样了,你可能需要重新调整一下光照和各种纹理来取得一个不错的结果。如果你有烘培的光照贴图的话,你需要重新烘培,以使其正确。

当线性空间和 HDR 都使用的时候,Unity 所做的所有的后处理都是在线性空间中进行的。然而,当只启用线性空间的时候,Unity 将会使用一个伽玛帧缓存,但幸运的是,当读写的时候,Unity 将会自动的转换到合适的颜色空间,这样图像效果仍然是在线性空间中进行的。

尽管 Unity 在一些平台上不支持默认的线性空间,比如手机。但你仍然可以在着色器中自己做。你可以对输入的纹理应用 pow() 函数来进行伽玛校正,将其转换到线性空间,然后在返回结果之前,再次应用 pow() 函数转换回伽玛空间。注意这种方法在计算上是昂贵的,所以请注意你目标设备的性能,并只在需要的时候使用。

结论

希望你现在对伽玛和线性空间有了一个更进一步的理解,并且知道在你的项目里如何应用这些原则。如果你对 Unity 的渲染管线、着色器和后处理做了很多自定义的修改,那么你一定要理解基本原理,来避免颜色管理上导致你图像质量下降的常见错误。当被正确的实现的时候,线性渲染管线是通向逼真世界的道路上的一个关键的部分。

原文地址

上一篇下一篇

猜你喜欢

热点阅读