“为什么我渲染出来的场景,总是感觉和真实世界不像呢?”

游戏从业者或多或少都听过Linear、Gamma、sRGB和伽马校正这些术语,互联网上也有很多科普的资料,但是它们似乎又都没有讲很"清楚"。

游戏界(特别是中小团队)很容易忽略这些概念造成的影响。长远来看,作为游戏从业者的你应该理解这些术语的含义,理解它们的本质联系,理解选择Linear或Gamma 空间带来的工作流变化。

本文将会简单介绍Gamma、Linear、sRGB和伽马校正的概念。接着通过实例解析统一到线性空间的步骤,最后介绍如何在Unity中实施相应的工作流。

什么是Linear、Gamma、sRGB和伽马校正

在物理世界中,如果光的强度增加一倍,那么亮度也会增加一倍,这是线性关系。

而历史上最早的显示器(阴极射线管)显示图像的时候,输出亮度和电压并不是成线性关系的,而是亮度等于电压的2.2次幂的非线性关系:

2.2也叫做该显示器的Gamma值,现代显示器的Gamma值也都大约是2.2。

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

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

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

好了,正常情况下,人眼看物理世界感知到了正常的亮度。而如果显示器输出一个颜色后再被你看到,即相当于走了一次Gamma2.2曲线的调整,这下子颜色就变暗了。如果我们在显示器输出之前,做一个操作把显示器的Gamma2.2影响平衡掉,那就和人眼直接观察物理世界一样了!这个平衡的操作就叫做伽马校正。

在数学上,伽马校正是一个约0.45的幂运算(和上面的2.2次幂互为逆运算):

左(Gamma0.45) 中(Gamma2.2) 右(线性物理空间)

经过0.45幂运算,再由显示器经过2.2次幂输出,最后的颜色就和实际物理空间的一致了。

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

为什么sRGB在Gamma0.45空间?

假设你用数码相机拍一张照片,你看了看照相机屏幕上显示的结果和物理世界是一样的。可是照相机要怎么保存这张图片,使得它在所有显示器上都一样呢? 可别忘了所有显示器都带Gamma2.2。反推一下,那照片只能保存在Gamma0.45空间,经过显示器的Gamma2.2调整后,才和你现在看到的一样。换句话说,sRGB格式相当于对物理空间的颜色做了一次伽马校正

还有另外一种解释,和人眼对暗的感知更加敏感的事实有关。

如图,在真实世界中(下方),如果光的强度从0.0逐步增加到1.0,那么亮度应该是线性增加的。但是对于人眼来说(上方),感知到的亮度变化却不是线性的,而是在暗的地方有更多的细节。换句话说,我们应该用更大的数据范围来存暗色,用较小的数据范围来存亮色。这就是sRGB格式做的,定义在Gamma0.45空间。而且还有一个好处就是,由于显示器自带Gamma2.2,所以我们不需要额外操作显示器就能显示回正确的颜色。

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

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

统一到线性空间

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

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

左(Gamma Space),右(Linear Space)

为什么Linear Space更真实?

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

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

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

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

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

统一到线性空间的过程是看起来是这样的,用图中橙色的框表示(现在看不懂图没关系,跟着后面的步骤来一步步看):

我们从橙色框的左上角出发。

第一步,输入的纹理如果是sRGB(Gamma0.45),那我们要进行一个操作转换到线性空间。这个操作叫做Remove Gamma Correction,在数学上是一个2.2的幂运算  。如果输入不是sRGB,而是已经在线性空间的纹理了呢?那就可以跳过Remove Gamma Correction了。

:美术输出资源时都是在sRGB空间的,但Normal Map等其他生成出来的纹理则一般在线性空间。所以开发大型项目,弄清楚区别并统一工作流是首要任务!

第二步,现在输入已经在线性空间了,那么进行Shader中光照、插值等计算后就是比较真实的结果了(上文解释了哦~),如果不对sRGB进行Remove Gamma Correction直接就进入Shader计算,那算出来的就会不自然,就像前面那两张球的光照结果一样。

第三步,Shader计算完成后,需要进行Gamma Correction,从线性空间变换到Gamma0.45空间,在数学上是一个约为0.45的幂运算  。如果不进行Gamma Correction输出会怎么样?那显示器就会将颜色从线性空间转换到Gamma2.2空间,接着再被你看到,结果会更暗。

第四步,经过了前面的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。而在输出前,Unity会自动进行Gamma Correction再让显示器输出。

你还需要小心格式的问题,在Linear下Unity会将纹理默认为sRGB格式,对于Normal Map、Light Map、UI等纹理,它们都不是sRGB,Unity可以让你直接修改成相应的类型:

还有一些纹理不是上面的任何类型,但又已经在线性空间了(比如说Mask纹理、噪声图),那你需要取消这个选项让它跳过Remove Gamma Correction过程:

还有,在Linear Space下,Shaderlab中的颜色输入也会被认为是sRGB颜色,会自动进行Gamma Correction Removed。

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

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

如上面的代码,来自官方的Standard Shader源代码,其中的_Metallic这一项就带了[Gamma]前缀,表示在Lienar Space下Unity要将其认为在sRGB空间,进行Gamma Correction Removed。

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

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

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

所以到底选什么,要看渲染要求,而一旦选好了Color Space,那么就需要渲染工程师、技术美术和美术商量和统一好工作流了。在中小团队或项目中,这些概念很容易被忽略,导致工作流混乱,渲染效果不尽人意。如果你曾经有疑惑,现在理解了吗?

Gamma、Linear、sRGB 和Unity Color Space,你真懂了吗?相关推荐

  1. Unity Gamma Linear Color Space

    转载文章,出自http://www.manew.com/thread-105872-1-1.html,作者 alphatt Gamma & Linear Color Space 一.真实?感觉 ...

  2. 关于Color Space是Gamma还是Linear的一些问题

    这个问题源自于我们的UI发现自己在FGUI下制作的东西,在Unity中显示的效果不对.例如90%透明度的黑底图片导出到Unity中的效果非常的透,可能只有70%左右的效果. 然后我们绞尽脑汁的找了半天 ...

  3. Gamma Linear Color Space

    http://www.manew.com/forum.php?mod=viewthread&tid=105872 一.真实?感觉?    1.你相信你的眼睛吗 (蓝黑or白金?) (A和B的颜 ...

  4. Gamma Correction sRGB texture

    文章目录 显示器gamma值的由来 gamma encode 图形渲染中的gamma问题 gamma correction sRGB texture 效果对比 正确的Linear/SRGB转换公式 参 ...

  5. 彩色空间(Color Space)

    背景 学习openCV-Python Tutorial,在Image Processing in OpenCV这一节里有提到彩色空间的转换,结合其他的一些资料对彩色空间(Color Space),彩色 ...

  6. java.lang.IllegalArgumentException: Numbers of source Raster bands and source color space components

    项目在文件压缩的时候报错如下: Exception in thread "main" java.lang.IllegalArgumentException: Numbers of ...

  7. Unity Color对比Color32、对应PS的通道解释

    1,先说Unity Color与Color32: 官网文档: Unity - Scripting API: Color Unity - Scripting API: Color32 Color和Col ...

  8. sRGB Color Space

    转自:http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/ With consumer-level hardware now cap ...

  9. Linear Color Space 渲染时几点注意

    https://chengkehan.github.io/LinearColorSpace.html 转载于:https://www.cnblogs.com/jim-game-dev/p/612436 ...

最新文章

  1. Yii2 使用 Beanstalk 队列系统
  2. 【转】【CUBE】Oracle分组函数之CUBE魅力
  3. python学多久可以做项目-怎么自学python,大概要多久?
  4. 【Android 异步操作】线程池 ( 线程池 reject 拒绝任务 | 线程池 addWorker 添加任务 )
  5. 改善医疗营运效率 哈佛医学中心与 AWS 共同合作
  6. CVE-2010-0249 极光
  7. c语言用数组实现栈的插入,用数组实现栈的功能的C语言代码?
  8. WordPress主题制作常用代码集合
  9. Battle Encoder Shirase一款能限制进程CPU占有率的小东西
  10. python get请求 url传参_requests的get请求url参数、url重定向处理及cookies
  11. GAN能生成3D图像啦!朱俊彦团队公布最新研究成果
  12. 【Kettle】如何输出固定格式报表
  13. 浏览器工作原理(一):浏览器的进程与线程
  14. unity创建一个简单对象的开销_SpringBoot第一篇:创建一个简单的SpringBoot
  15. SPSS基础数据操作之变量的计算及个案计数
  16. div3 C. Dominant Piranha
  17. 模拟IP转发分组实验 C++ Vector
  18. Linux命令查看Linux服务器内存、CPU、显卡、硬盘使用情况
  19. 3.2 语音和语言处理简史
  20. Python 生成双峰分布的概率密度函数并画图

热门文章

  1. ThinkPHP拼团拼购h5单商户商城[可对接公众号]非常棒的一款h5拼团商城源码
  2. 【C语言】[其他] :code, data, idata, xdata, bdata, edata, hdata等,代表的意思
  3. WebStorm开发应用——前端页面
  4. 最新版的PDF转图片软件
  5. pandas的数据拼接与合并(concat、join、merge)
  6. 微信发朋友圈如何只发文字?
  7. Win-mac版 AE 2018安装附教程
  8. 同事能力比你强怎么办
  9. 2022苹果CMS 全新绿豆二开影视源码app源码完整版带安装教程
  10. 20189320《网络攻防》第六周作业