ガンマ空間と線形空間でunity3D

unity3D 2色空間(カラースペース)。ガンマ空間と線形空間。
そして、2つの色空間、即ち、リニアガンマワークフロー及びワークフローによって定義されたワークフローに基づきます。

色空間の前述の定義に加えて、空間および空間線形いわゆるガンマは、だけでなく、おおよそ次のように理解することができる:
ここに画像を挿入説明
図客観横2-11反射率(この場合はソースカラーと呼ばれます)。選択インデックスは、それらを変換の線形関数です。
それでも直線的に主観的な反射率を変化させる(または目標色と呼ばれる)は、この時点で得られました。
これらの色は、線形空間からなるターゲット色空間と呼ばれています。
インデックス値は、ソース色が目標色になったときに、1のパワー関数として選択されていない、色空間は、これらの目標色空間構成はガンマと呼ばれていると述べました。

ガンマ空間を使用する理由については、制限や収納スペースの主観的な感覚からです。

unity3Dはスペースの線状またはガンマ宇宙空間部に仕事に開発することができます。長い時間が、宇宙リニアガンマ標準色空間を開発するためのスペースが、用途は、より正確なレンダリングを取得することができますとき。

ここに画像を挿入説明
あなたはリニアワークフローを使用することを選択した場合は、線形空間またはガンマスペースが正常に動作できるかどうか、カラーテクスチャの数を格納します。
歴史的な理由により、ガンマ空間に変換された画像データの色の多くを保存します。レンダラをレンダリングするときと線形に基づいて必要とされる
色空間データ。そのため、色空間におけるこれらのデータを使用し、ガンマは誤った結果につながる可能性があります。
したがって、レンダリング計算はガンマから処理空間をサンプリングするテクスチャの色データをサンプリングすることができるのsRGBサンプラーを、必要に応じて
線形空間に変換します。

計算エンジンunity3Dライトマップ生成処理が常に線形空間で行われます。
変換の計算結果は、ガンマライトマップファイルの後のスペースに格納されています。
即ち、関係なく現在のワークフローリニアワークフローまたはガンマワークフローの、unity3Dエンジンが発生した光テクスチャが同じです。

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

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

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

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

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

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

ここに画像を挿入説明

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

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

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

在数学上,伽马校正是一个约0.45的幂运算(如上面的2.2次幂互为逆运算)。
ここに画像を挿入説明
ここに画像を挿入説明
左(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格式相当于对物理空间的颜色做了一次伽马校正。

还有另外一种解释,和人眼对暗的感知更加敏感的事实有关。
ここに画像を挿入説明
如图,在真实世界中(下方),如果光的强度从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中渲染会更接近物理世界,更真实:

ここに画像を挿入説明
左(Gamma Space),右(Linear Space)

为什么Linear Space更真实?

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

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

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

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

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

统一到线性空间的过程看起来是这样的,用图中橙色的框表示:
ここに画像を挿入説明
从橙色的框的左上角出发:
1、输入的纹理如果是sRGB(gamma0.45),那我们要进行一个操作转行到线性空间。这个操作叫做remove gamma correction,在数学上是一个2.2的幂运算ここに画像を挿入説明
。如果输入不是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的幂运算ここに画像を挿入説明
    如果不进行gamma correction输出会怎么样?那显示器就会将颜色从线性空间转换到gamma2.2空间,接着再被你看到,结果会更暗。

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

ここに画像を挿入説明
第一步,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:
ここに画像を挿入説明
这两者有什么区别呢?

如果选择了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自己会处理他们。
ここに画像を挿入説明
还有一些纹理不是上面的任何类型,但又已经在线性空间了(比如说Mask纹理、噪声图),那你需要取消sRGB这个选项让它跳过Remove Gamma Correction过程:
ここに画像を挿入説明
到底什么纹理应该是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

公開された610元の記事 ウォン称賛96 ビュー330 000 +

おすすめ

転載: blog.csdn.net/wodownload2/article/details/104221445