Gamma space and in linear space unity3D

unity3D The two color space (color space). Gamma space and linear space.
And based on the workflow defined by the two color spaces, i.e., the linear gamma workflow and workflow.

The so-called gamma linear space and space, in addition to the aforementioned definition of the color space, but also can be roughly understood as follows:
Here Insert Picture Description
FIG. 2-11 reflectance abscissa objective (in this case called the source color). Selection index is a linear function of a transform them.
Obtained at this time was still varies linearly subjective reflectance (or called a target color).
These colors are called target color space composed of a linear space.
The index value is not selected as a power function of 1, when the source color becomes the target color, said color space these target color space configuration is called gamma.

As to why using a gamma space, because the subjective feeling of restriction and storage space.

unity3D allows developers to work in linear or gamma space Space parts of space. Although a long time, when the gamma space to develop a standard color space, but uses linear space will be able to get a more accurate rendering.

Here Insert Picture Description
If you choose to use a linear workflow, then stores the number of color texture, whether linear space or gamma space can work properly.
For historical reasons, to save a lot of colors in the image data have been converted into gamma space. And when rendering the renderer is required based on linear
color space data. Therefore, use these data in the color space, the gamma may lead to incorrect results.
Thus, when rendering calculations required sRGB sampler, which can sampled color data in the texture sampling process space from the gamma
converted to linear space.

Calculation engine unity3D lightmaps generation process is always done in linear space.
While the calculation result of the transformation is stored in the space after the gamma lightmaps file.
I.e., regardless of the current workflow linear workflow or gamma workflow, unity3D engine generated light texture is the same.

当使用线性工作流时,unity3D引擎会对这些存储在伽马空间中的颜色数据的光照贴图使用sRGB采样器,在采样阶段自动把它们转换到线性空间中。如果使用的是伽马工作流,则不做该操作。

因此,如果改变了工作流模式,需要重新烘焙光照贴图,引擎的光照处理系统会自动重新烘培光照贴图。

https://zhuanlan.zhihu.com/p/66558476

在物理世界中,如果光的强度增加一倍,那么亮度也会增加一倍,这是线性关系。
而历史上最早的显示器(CRT阴极射线管)显示图像的时候,输出亮度和电压并不是成线性关系的,而是亮度等于电压的2.2次幂的非线性关系:
Here Insert Picture Description
2.2也叫做该显示器的gamma值,现代显示器的gamma值也都大约是2.2

这种关系意味着当电压线性变化时,相对于真实世界来说,亮度的变化在暗处变化较慢,暗的数据占据范围更广,颜色整体会偏暗。

如图,直线代表物理世界的线性空间(Linear Space),下曲线是显示器输出的Gamma2.2空间(Gamma Space)。

Here Insert Picture Description

横坐标表示像素实际的亮度数值,纵坐标表示显示时像素对应的亮度数值

好了,正常情况下,人眼看世界感知到了正常的亮度。
而如果显示器输出一个颜色后再被你看到,即相当于走了一次gamma 2.2曲线的调整,这下子颜色就变暗了。

如果我们在显示器输出之前,做一个操作把显示器的gamma 2.2影响平衡掉,那就和人眼直接观察物理世界一样了。这个平衡的操作叫做伽马校正。

在数学上,伽马校正是一个约0.45的幂运算(如上面的2.2次幂互为逆运算)。
Here Insert Picture Description
Here Insert Picture Description
左(Gamma0.45) 中(Gamma2.2) 右(线性物理空间)
经过0.45幂运算,再由显示器经过2.2次幂输出,最后的颜色就和实际物理空间的一致了。

最后,什么是sRGB呢?1996年,微软和惠普一起开发了一种标准sRGB色彩空间。这种标准得到许多业界厂商的支持。sRGB对应的是Gamma0.45所在的空间。

为什么sRGB在Gamma0.45空间?

假设你用数码相机拍一张照片,你看了看照相机屏幕上显示的结果和物理世界是一样的。
可是照相机要怎么保存这张图片,使得它在所有显示器上都一样呢?
可别忘了所有显示器都带gamma 2.2
反推一下,那照片只能保存在gamma 0.45空间,经过显示器的gamma2.2调整后,才和你现在看到的一样。

换句话说,sRGB格式相当于对物理空间的颜色做了一次伽马校正。

还有另外一种解释,和人眼对暗的感知更加敏感的事实有关。
Here Insert Picture Description
如图,在真实世界中(下方),如果光的强度从0.0逐步增加到1.0,那么亮度应该是线性增加的。但是对于人眼来说(上方),感知到的亮度变化却不是线性的,而是在暗的地方有更多的细节。

换句话说,我们应该用更大的数据范围来存暗色,用较小的数据范围来存储亮色。这就是
sRGB格式做的,定义在gamma 0.45空间。而且还有一个好处就是,由于显示器自带gamma 2.2,所以我们不需要额外操作就能显示回正确的颜色。

以上内容,看完后还是不懂也没关系,在继续之前你可以先死记住以下几个知识点:

1、显示器的输出在Gamma2.2空间。
2、伽马校正会将颜色转换到Gamma0.45空间。
3、伽马校正和显示器输出平衡之后,结果就是Gamma1.0的线性空间。
4、sRGB对应Gamma0.45空间。

统一到线性空间
现在假设你对上文的概念有一定认识了,我们来讲重点吧。

在Gamma 或 Linear空间的渲染结果是不同的,从表现上说,在Gamma Space中渲染会偏暗,在Linear Space中渲染会更接近物理世界,更真实:

Here Insert Picture Description
左(Gamma Space),右(Linear Space)

为什么Linear Space更真实?

你可以这么想,物理世界中的颜色和光照规律都是在线性空间描述的对吧?(光强度增加了一倍,亮度也增加一倍)。 而计算机图形学是物理世界视觉的数学模型,shader中颜色插值,光照的计算自然也是在线性空间描述的。如果你用一个非线性空间的输入,又在线性空间中计算,那结果就会有一点“不自然”。

换句话说,如果所有的输入,计算,输出,都能统一到线性空间中,那么结果是最真实的。
玩家会说这个游戏画质很真实。事实上,因为计算这一步已经是线性空间描述了,所以只要保证输入输出是在线性空间就行了。

所以为什么你的游戏画面不真实呢?因为你可能对此混乱了,你的输入或输出在Gamma Space,又没搞清楚每个纹理应该在什么Space,甚至也不知道有没用伽马校正,渲染结果怎么会真实呢?

现在假设我们的目标是获得最真实的渲染,因此需要统一渲染过程在线性空间,怎么做呢?

注:统一在Linear空间是最真实的,但不代表不统一是错误的。一般来说,如果是画质要求高的作品,那么都是统一的。没这方面要求的则未必是统一的,还有些项目追求非真实的渲染,它们也未必需要统一。

统一到线性空间的过程看起来是这样的,用图中橙色的框表示:
Here Insert Picture Description
从橙色的框的左上角出发:
1、输入的纹理如果是sRGB(gamma0.45),那我们要进行一个操作转行到线性空间。这个操作叫做remove gamma correction,在数学上是一个2.2的幂运算Here Insert Picture Description
。如果输入不是sRGB,而是已经在线性空间的纹理了呢?那就可以跳过remove gamma correction了。

注:美术输出资源时都是在sRGB空间的,但Normal map等其他电脑计算出来的纹理则一般在线性空间,即linear texture。

  1. 现在输入已经在线性空间了,那么进行shader中光照,插值等计算后就是比较真实的结果了。如果不对sRGB进行remove gamma correction直接就进入shader计算,那算出来的就会不自然,就像前面那两张球的光照结果一样。

  2. shader计算完成后,需要进行gamma correction,从线性空间变换到gamma 0.45空间,在数学上是一个约为0.45的幂运算Here Insert Picture Description
    如果不进行gamma correction输出会怎么样?那显示器就会将颜色从线性空间转换到gamma2.2空间,接着再被你看到,结果会更暗。

  3. 经过了前面的gamma correction,显示器输出在了线性空间,这就和人眼看物理世界的过程是一样的了。
    我们再举个例子,我们取sRGB纹理里面的一个像素,假设其值为0.73,那么在统一线性空间的过程中,它的值是怎么变化的呢?

Here Insert Picture Description
第一步,0.73(上曲线) * [Remove Gamma Correction] = 0.5(直线)。( [公式] )

第二步,0.5(直线) * [Shader] = 0.5(直线)(假设我们的Shader啥也不干保持颜色不变)

第三步,0.5(直线) * [Gamma Correction] = 0.73(上曲线)。( [公式] )

第四步,0.73(上曲线) * [显示器] = 0.5(直线)。( [公式] )

如果不进行Gamma Correction,就会变暗,因为第三步不存在了,第四步就会变成:

0.5(直线) * [显示器] = 0.218(下曲线)。( [公式] )

再对照上面的图琢磨琢磨?

Unity中的Color Space
我们回到Unity,在ProjectSetting中,你可以选择Gamma 或 Linear作为Color Space:
Here Insert Picture Description
这两者有什么区别呢?

如果选择了Gamma,那Unity不会对输入和输出做任何处理,换句话说,Remove Gamma Correction 、Gamma Correction都不会发生,除非你自己手动实现。

如果选了Linear,那么就是上文提到的统一线性空间的流程了。对于sRGB纹理,Unity在进行纹理采样之前会自动进行Remove Gamma Correction,对于Linear纹理则没有这一步。而在输出前,Unity会自动进行Gamma Correction再让显示器输出。

怎么告诉Unity纹理是sRGB还是Linear呢?对于特定用途的纹理,你可以直接设置他们所属的类型:如Normal Map、Light Map等都是Linear,设置好类型Unity自己会处理他们。
Here Insert Picture Description
还有一些纹理不是上面的任何类型,但又已经在线性空间了(比如说Mask纹理、噪声图),那你需要取消sRGB这个选项让它跳过Remove Gamma Correction过程:
Here Insert Picture Description
到底什么纹理应该是sRGB,什么是Linear?

关于这一点,我个人有一个理解:所有需要人眼参与被创作出来的纹理,都应是sRGB(如美术画出来的图)。所有通过计算机计算出来的纹理(如噪声,Mask,LightMap)都应是Linear。

这很好解释,人眼看东西才需要考虑显示特性和校正的问题。而对计算机来说不需要,在计算机看来只是普通数据,自然直接选择Linear是最好的。

除了纹理外,在Linear Space下,Shaderlab中的颜色输入也会被认为是sRGB颜色,会自动进行Gamma Correction Removed。

有时候你可能需要想让一个Float变量也进行Gamma Correction Removed,那么就需要在ShaderLab中使用[Gamma]前缀:

[Gamma]_Metallic("Metallic",Range(0,1))=0

如上面的代码,来自官方的standard shader源代码,其中的_Metallic这一项就带了[Gamma]前缀,表示在.linear space下unity要将其认为在sRGB空间,进行gamma correction remove

扩展:为什么官方源代码中_Metallic项需要加[Gamma]?这和底层的光照计算中考虑能量守恒的部分有关,Metallic代表了物体的“金属度”,如果值越大则反射(高光)越强,漫反射会越弱。在实际的计算中,这个强弱的计算和Color Space有关,所以需要加上[Gamma]项。

虽然Linear是最真实的,但是Gamma毕竟少了中间处理,渲染开销会更低,效率会更高。上文也说过不真实不代表是错的,毕竟图形学第一定律:如果它看上去是对的,那么它就是对的。

注:在Android上,Linear只在OpenGL ES 3.0和Android 4.3以上支持,iOS则只有Metal才支持。

在早期移动端上不支持Linear Space流程,所以需要考虑更多。不过随着现在手机游戏的发展,越来越多追求真实的项目出现,很多项目都选择直接在Linear Space下工作。

一旦确定好Color Space,那么就需要渲染工程师、技术美术和美术商量和统一好工作流了。在中小团队或项目中,这些概念很容易被忽略,导致工作流混乱,渲染效果不尽人意。现在你懂了吗?

https://www.kinematicsoup.com/news/2016/6/15/gamma-and-linear-space-what-they-are-how-they-differ

Published 610 original articles · won praise 96 · views 330 000 +

Guess you like

Origin blog.csdn.net/wodownload2/article/details/104221445