转载请标明出处:http://www.cnblogs.com/zblade/

一、概述

很久没有写文章了,今天写一篇对gamma空间和线性空间的个人理解总结,在查阅和学习了各个资料后,算是一个个人笔记吧。

二、Gamma颜色空间和线性颜色空间

其实对于颜色空间的理解,我个人是这样理解的:所有的一切颜色空间,最终的目的,都是为了最终投入到人眼中,能够基本重现自然界的颜色。

记住这一个目的,对下面的一些理解就会更加的有依据了。

2.1 人眼的感知能力

既然目标是我们人自身,那么我们就需要对人自身的眼睛的感知能力有一个基本的认识:人眼对于光强度的感知是非线性的。

什么是线性,什么是非线性,从数学的角度说,就是自变量的变化和因变量的变化是否成固定比例(默认为1),如果成比例,即:y = kx, 那么这个变化就是线性的。

如果不成固定比例,那么这个变化就是非线性的,非线性是自然界最常见的变化关系。

人类对于很多环境因素的变化的感知能力,都是非线性的,例如对于音阶,就是基于等比关系,而不是线性关系;对于分贝,对于疼痛等级,等等。

回到对光强的感知,人眼对于光强度的变化的感知,是非线性的,这是通过实验得出的结论。如果在一个全黑的房间中,放入一根蜡烛,此时感知的光强变化比较明显;

如果房间中已经放入100根蜡烛,再次放入一根蜡烛,此时人眼对这新加入的一根蜡烛带来的光强度变化是没有最初从0到1的感知强的(默认每根蜡烛的光强度增量一样)。

可见,人眼对于高亮部分的感知能力,是没有暗部的感知能力强的。

2.2 存储空间的有限

上面说了人眼的感知能力特点,那么自然界的光又是如何?自然界的光强度,是和其对应的功率成正比的,对应的范围是极其大:日光下100000lux,  星光0.0003 lux...

如果要将这么大范围的光强度变化都表示存储起来,其对内存的占用以及传输带宽的占用是无法承受的。

业界目前主流的,对于颜色亮度的表示,用的是8位,也就是8bit,从0-255来进行表示。逐渐也有32位的真彩,当然不在这次的讨论中。

2.3 Gamma空间

基于1和2的论证,那么如何将自然采光的结果存储到实际的图片中,就有一个基本的思路:将自然光以接近人眼感知能力曲线的函数进行压缩到8位图像中,这时候得到的图形就是经过压缩后的颜色结果。

所谓Gamma压缩,其实质就是这个压缩的函数,是以Vout = VinGamma 来进行压缩的。

现在业界提到的Gamma = 2.2, 是业界经过反复测量,得到的一个数值,这样可以在256个灰度阶的范围内更多的保留暗部的细节:

上面的两个图两个图,就可以基本的解释Gamma = 2.2 的来源,人眼的感知能力和n = 1/2.2的幂函数比较靠近,当然不同环境下有不同的数值,大概范围在1.8-2.5之间。

2.4 线性空间

理解非线性空间-Gamma空间后,自然可以理解线性空间,就是上面图二中的 n = 1这条曲线,为什么要提线性空间?因为我们的相机对于光强的感知,是基于线性空间的。

举一个简单例子,两个光子投射到相机上,其得到的光强就是2倍光子光强,当然我们已经知道人眼并不是2倍光强。

而业界的图片都是Gamma空间中存储,那么相机到最终图片,就会经历一个编码过程,这就是所谓的Gamma编码,也就是: Vout = Vin(1/2.2) 这个过程。   

三、Gamma补偿

现在,我们通过相机拍摄的图片,最终是以gamma空间的格式存储(业界标准称为sRGB), 那么我们在显示器上查看图片,是否也是以sRGB的结果显示的?答案是否定的。

前面业界已经将原生自然界的光照进行了压缩,那么业界定然要通过一定的办法将压缩的图片重新转换回来,得到更接近自然界的图像,这个过程,就是Gamma补偿,也被叫做Gamma校正。

既然我们知道是以什么函数进行压缩,那么解压的过程,自然就是一个求逆的过程,可以得到:Vout = Vin2.2

这一步是业界的显示器自动默认执行的,所以我们在最终向显示器上提交的颜色,需要满足对应的关系。

用一张图表示整个采样到显示的过程:

一句话总结: 采样生成,使用了Gamma编码,这是业界标准,显示过程,使用了Gamma补偿,这也是业界标准,选取gamma = 2.2, 这是业界根据人眼进行测试得到的比较靠近人眼感知能力曲线的数值。

四、Unity中使用线性空间和Gamma空间

在图形学界,技术是不断进步和探索的,应用一直都是延迟更新的(为了向下兼容的需要)。

当然gamma空间的存在,以前都是忽视这部分的差异,直接基于gamma空间的存图进行光学计算的。

但是引擎中的光学计算(shader中),是基于线性空间的公式进行的,这样就会带来较大的差异,我们推算的公式基于线性空间得到的,但是输入的数据是基于gamma空间存储的格式,图像采集得到的结果

作为光学计算公式的输入,得到的输出自然是错误的。以前游戏行业对于这个一直处于忍受阶段,也可以通过美术进行调整,得到较为差异不大的计算结果。

最近几年逐渐推广的PBR技术,对于光照的计算更为苛刻,这推动了线性空间在游戏行业的逐步推广。

4.1 Gamma空间的处理过程

在gamma空间中,在shader进行光学计算的过程中,直接将图像采样得到结果带入公式中进行计算,得到的color存入colorbuff中,然后提交到显示器,进过一次gamma补偿,就得到最终的颜色。

4.2 线性空间的处理过程

线性空间中,对所有的图片,默认认为图片都是线性存储的方式。所以如果原图是Gamma空间的sRGB的存储方式,需要勾选sRGB的标志,这样在进行shader计算的时候,会首先进行一次gamma补偿,

将颜色从gamma空间转换到线性空间,然后进行正确的光照计算,得到结果最后再转换回到gamma空间(gamma压缩), 最后提交到显示器,进行一次gamma补偿,得到最终的颜色。

用一张图表示这两种处理的流程(直接用参考文章的图):

一句话总结:unity中的gamma空间和线性空间,其实质就是对存储sRGB格式图片,进行不同的光照计算,不同的光照计算进行不同的流程,得到精度不同的结果,最后都需要统一为gamma空间

的格式,提交到显示器上进行gamma补偿,得到最终的显示图片。

 参考文章:

 https://www.zhihu.com/question/27467127 该问题下的大部分回答

https://zhuanlan.zhihu.com/p/37679604 较为简易

https://www.cambridgeincolour.com/tutorials/gamma-correction.htm 非游戏向的解释gamma校正   

https://docs.unity3d.com/Manual/LinearRendering-LinearOrGammaWorkflow.html unity官网的gamma/线性介绍

浅谈unity中gamma空间和线性空间的更多相关文章

  1. 浅谈Unity中的GC以及优化

    介绍: 在游戏运行的时候,数据主要存储在内存中,当游戏的数据不在需要的时候,存储当前数据的内存就可以被回收再次使用.内存垃圾是指当前废弃数据所占用的内存,垃圾回收(GC)是指将废弃的内存重新回收再次使 ...

  2. 浅谈Unity的渲染优化(1): 性能分析和瓶颈判断(上篇)

    http://www.taidous.com/article-667-1.html 前言 首先,这个系列文章做个大致的介绍,题目"浅谈Unity",因为公司和国内大部分3D手游开发 ...

  3. 浅谈Java中的equals和==(转)

    浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str ...

  4. 浅谈Java中的equals和==

    浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: String str1 = new String("hello"); String str2 = ...

  5. 浅谈Java中的深拷贝和浅拷贝(转载)

    浅谈Java中的深拷贝和浅拷贝(转载) 原文链接: http://blog.csdn.net/tounaobun/article/details/8491392 假如说你想复制一个简单变量.很简单: ...

  6. 浅谈Java中的深拷贝和浅拷贝

    转载: 浅谈Java中的深拷贝和浅拷贝 假如说你想复制一个简单变量.很简单: int apples = 5; int pears = apples; 不仅仅是int类型,其它七种原始数据类型(bool ...

  7. 浅谈C中的malloc和free

    转自http://bbs.bccn.net/thread-82212-1-1.html非常感谢作者 浅谈C中的malloc和free 在C语言的学习中,对内存管理这部分的知识掌握尤其重要!之前对C中的 ...

  8. 浅谈JS中的闭包

    浅谈JS中的闭包 在介绍闭包之前,我先介绍点JS的基础知识,下面的基础知识会充分的帮助你理解闭包.那么接下来先看下变量的作用域. 变量的作用域 变量共有两种,一种为全局变量,一种为局部变量.那么全局变 ...

  9. 浅谈Java中的栈和堆

    人们常说堆栈堆栈,堆和栈是内存中两处不一样的地方,什么样的数据存在栈,又是什么样的数据存在堆中? 这里浅谈Java中的栈和堆 首先,将结论写在前面,后面再用例子加以验证. Java的栈中存储以下类型数 ...

随机推荐

  1. Java RandomAccessFile用法 【转】

    RandomAccessFile源地址:http://blog.csdn.net/akon_vm/article/details/7429245 RandomAccessFile是用来访问那些保存数据 ...

  2. android获取手机机型、厂商、deviceID基本信息

    /** * 系统工具类 */ public class SystemUtil { /** * 获取当前手机系统语言. * * @return 返回当前系统语言.例如:当前设置的是"中文-中国 ...

  3. C++相关:部分标准库特殊设施

    C++ tuple(元组) tuple是C++11新标准里的类型.它是一个类似pair类型的模板.pair类型是每个成员变量各自可以是任意类型,但是只能有俩个成员,而tuple与pair不同的是它可以 ...

  4. PAT1065: A+B and C (64bit)

    1065. A+B and C (64bit) (20) 时间限制 100 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 HOU, Qiming G ...

  5. JS/jquery实现鼠标控制页面元素显隐

    最近网站要上一个活动广告横幅,当用户鼠标划过时显隐二维码.像这种鼠标事件控制页面元素显隐的情况,码农们会经常遇到,可以通过javascript或jquery代码实现,下面就几种常见需求一起归纳一下. ...

  6. YII框架CGridView sql有条件分页实现

    $SQL="SELECT * FROM {{user}} WHERE `typeff`=2 order by create_time desc"; $SQL_count=" ...

  7. 【新手向】自用的tooltip小插件,前端插件知识科普~

    上面的tooltip就是成品图,为了和自己站点的风格保持一致所以自己写的. 第一部分:你绝对碰到过的匿名函数闭包问题 第二部分:写个tooltip demo 第三部分:源码地址 第一部分 你绝对碰到过 ...

  8. 你不知道的JavaScript--Item24 ES6新特性概览

    ES6新特性概览 本文基于lukehoban/es6features ,同时参考了大量博客资料,具体见文末引用. ES6(ECMAScript 6)是即将到来的新版本JavaScript语言的标准,代 ...

  9. 剑指offer(javascript实现)

    1.二维数组中的查找 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. funct ...

  10. 2. 网友对app后端写作系列文章的写作建议

    很感谢"app后端"qq群的网友,在发布消息后,就收到了大量网友的反馈 下面的建议会融入到写作当中: 1.还有,对版本升级很感兴趣,我们现在为了兼容旧版本,已经把工程代码搞的乱哄哄 ...