线性空间光照(即Gamma)
在渲染管线中正确使用Gamma校正是决定渲染效果的一个非常重要的因素,现在商业引擎包括很多国内引擎都已经是在线性空间计算光照。当然也包括我们之前公司设计的引擎。关于gamma校正的定义可以查看wikipedia或者看知乎的这篇文章。
原文地址:http://filmicgames.com/archives/299
对于图形程序员和技术美术来说,线性空间光照 是最重要的东西。它并不是看上去那么难,但是不知道是什么原因,没有人真正教过它。就我自己而言,我从Georgia Tech获得了本科和硕士学位,学习了基本上所有的图形学知识,但是并没有听到关于Gamma的知道,直到我从George Borshukov那里学到。这扁文章是我GDC演讲的一个简化版。你可以查看幻灯片来获取更多的细节。
我经常以下面这张图开头:
希望你的浏览器没有改变它的大小,左边和右边是黑白交替显示的水平线。如果你有一个正确校正的显示器,中上的方块应该会比较暗,中下部的方块应该和交替的线有一样的强度。在左右两边,你会得到差不多0~255中间的值。中间的值 是127/128,对吗?
不。实际上187差不多是0~255之间的中间值。128会比一半暗很多。怎么回事?
这里有一个0~255的灰阶,128并不是0~255的中间值,而是187。为什么?这就是所谓的gamma在搞鬼。
当把一个值显示到屏幕时,我自然地假设光子的数量跟发送的值是线性增长的关系。例如50发送光子的数量应该比25发送光子的数量多一倍。但是,现实中大多数显示器遵循一个gamma 2.2的曲线。如果 我们假设输出的值在0~1之间而不是0~255,那么光子的数量应该跟pow(x,2.2)成正比。
不同条件下可能会不同。如果你听说Mac有一个不同的gamma,那是因为gamma默认值为1.8(虽然最近我听说他们已经修改了)。sRGB曲线也非常相似,但是实际上跟gamma 2.2的有一些小差别。
上面是一张我穿着一个很小且很贵的貂皮外套的照片。左边的图是一个gamma 1/2.2 (接近0.45)的照片。中间是线性的,或者说是gamma 1.0。右边图片的gamma值为2.2。
我们实际上可以通过一个pow操作在gamma空间中转换。从左边的亮照片说起,我们可以通过 pow(x,2.2)来得到 蹭的图片,再通过一个pow(x,2.2)得到右边的图片。反过来,我们也可以通过 pow(x,1/2.2)来得到。
假设我们在不知道gamma的条件下制造了一个相机。我们会建造一个感光元件用来接收进入的光线(左边)并且把每个像素分到255个灰阶中。这个图片会存放到硬盘上(中间图片)。但是,用户会在它们的屏幕上来查看图片,显示器会应用一个gamma 2.2来校正 图片(右边图片)。如果我们这样做,用户会说我们的相机很烂,因为屏幕上的照片跟现实世界的不一样。
你可以尝试向他们解释相机是正常的,只是所有的显示器有问题(包括你相机背面的预览LCD)。祝你好运。相反,每一个消费级相机老师把照片存储在gamma 空间。当光线到达感光器时,相机 会应用一个pow(x,1/2.2)操作,然后再把它映射到255个灰阶上,并且把最后结果存储到硬盘上。傻瓜相机如此,网络摄像头也是如此,iPhone也是如此,单反也是如此 。唯一的例外可能是计算机视觉相机 。因此图片都存储在gamma空间,它看上去会比较亮且柔和。但是当用户在屏幕上看到图片时,它看上去是正确的。
这是关于gamma你需要了解的最重要的东西。每当你看到屏幕上的一张照片时(像右边的这张),存储在硬盘上的其实是更亮、更柔和,就像上图中左边的一样。那么 它怎么影响计算机图形学呢?
左边的图片所有的光照计算都是在gamma空间做的,而右边的是在线性空间计算的。如果是在PC/PS3/360平台上,那么光照计算应该是线性空间内。如果 是在Wii/PSP/iPhone平台上,你必须做你必须做的事。
左边的图片错在了几个地方。首先,它有一个非常柔软的衰减,这样看上去不正确。你也会看到很多的色调偏移,尤其是在高光处,白色到黄色和黑色到黄色的偏移。当你看右边的图片时,它看起来像一个漫反射表面加高光,它更符合光照模型描述的样子。最后,我不是在讨论什么看起来是好了,我是在讨论什么是正确的。这是一个非常不同的讨论。
如果你不关心gamma,你基本上是遵循上面的光照流程。假设你有一个shader如下所示:
float specular = ...;
float3 color=tex2D(samp,uv.xy);
float diffuse = saturate(dot(N,L));
float3 finalColor = color * diffuse + specular;
return finalColor;
首先读取纹理,但是在你硬盘上的纹理是存储在gamma空间的。纹理看起来比较亮且饱和度比较低。所以你使用了这张太亮的纹理,快些 基础上应用你的光照模型得到蹭的图像。最后显示器应用了pow(2.2)减小了它的亮度信息得到最终的结果。如何纠正它得到正确的结果?
下面是修改后的shader:
float specular = ...;
float3 color=pow(tex2D(samp,uv.xy),2.2);
float diffuse = saturate(dot(N,L));
float3 finalColor = pow(color * diffuse + specular,/2.2);
return finalColor;
唯一的不同之处是上面的两个pow指令。当纹理由硬件读取时,它是在gamma空间的(第一张图)。但是pow(x,2.2)把这张纹理转换线性空间中(第二张图)。然后我们计算光照(第三张图)。现在我们得到了想要的结果,再应用pow(x,1/2.2)把它转换到gamma空间中。Gamma空间的图片传输到显示器,这时会对像素应用pow(x,2.2)来显示最终的图片。
当然,这些pow函数并不是没有开销的。但是它们实际上是免费的如果你使用硬件采样状态。对于纹理读取来说 ,你可以使用D3DSAMP_SRGBTEXTURE,对于要写入的帧缓冲区,可以使用D3DRS_SRGBWRITEENABLE。所以你的shader代码跟原来一样了,并且硬件状态来帮助你免费转换。
这里有一张排球的真实照片。上面的图片是在线性空间而下面是在gamma空间。注意线性的照片是如何在分明的衰减上与真实照片吻合的,但是gamma空间计算的就不是这个样子的。
那么这就是我们如何使用我们的图片看起来正确的。怎么让它变好看留给读者自己当练习解决。
线性空间光照(即Gamma)的更多相关文章
- PBR(基于物理的渲染)学习笔记
PBR基本介绍 PBR代表基于物理的渲染,本质上还是 gl_FragColor = Emssive + Ambient + Diffuse + Specular 可能高级一些在考虑下AO也就是环境光遮 ...
- 剖析Unreal Engine超真实人类的渲染技术Part 1 - 概述和皮肤渲染
一.概述 1.1 数字人类的概要 数字人类(Digital Human)是利用计算机模拟真实人类的一种综合性的渲染技术.也被称为虚拟人类.超真实人类.照片级人类. 它是一种技术和艺术相结合的综合性模拟 ...
- Gamma校正与线性空间
基础知识部分 为了方便理解,首先会对(Luminance)的相关概念做一个简单介绍.如果已经了解就跳到后面吧. 我们用Radiant energy(辐射能量)来描述光照的能量,单位是焦耳(J),因为光 ...
- 浅谈unity中gamma空间和线性空间
转载请标明出处:http://www.cnblogs.com/zblade/ 一.概述 很久没有写文章了,今天写一篇对gamma空间和线性空间的个人理解总结,在查阅和学习了各个资料后,算是一个个人笔记 ...
- 【图形学】我理解的伽马校正(Gamma Correction)
http://blog.csdn.net/candycat1992/article/details/46228771/ 写在前面 我相信几乎所有做图像处理方面的人都听过伽马校正(Gamma Corre ...
- 聊聊Unity的Gamma校正以及线性工作流
0x00 前言的前言 这篇小文其实是在清明节前后起的头,不过后来一度搁笔.一直到这周末才又想起来起的这个头还没有写完,所以还是直接用一个月前的开头,再将过程和结尾补齐. 0x01 前言 结束了在南方一 ...
- OpenGL核心技术之Gamma校正
笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:<手把手教你/2.2次幂.Gamma校正后的暗红色就会成为(0.5,0.0 ...
- LearnOpenGL.PBR.光照
光源辐射率: 辐射率(radiance)表示光源在给定立体角ω下的辐射通量(或光源发射的能量). 那么假设立体角ω无限小时,辐射率就表示单束光线(或说某个单一方向)的辐射通量. 点光 ...
- 【视频开发】伽马校正(gamma correction)学习笔记
我相信几乎所有做图像处理方面的人都听过伽马校正(Gamma Correction)这一个名词,但真正明白它是什么.为什么要有它.以及怎么用它的人其实不多.我也不例外. 最初我查过一些资料,但很多文章 ...
随机推荐
- Apache Spark源码走读之21 -- WEB UI和Metrics初始化及数据更新过程分析
欢迎转载,转载请注明出处,徽沪一郎. 概要 WEB UI和Metrics子系统为外部观察监测Spark内部运行情况提供了必要的窗口,本文将简略的过一下其内部代码实现. WEB UI 先上图感受一下sp ...
- Peeking into Apache Flink's Engine Room
http://flink.apache.org/news/2015/03/13/peeking-into-Apache-Flinks-Engine-Room.html Join Processin ...
- php读取memcahed java session
PHP 共享 JAVA 保存的session信息 情景: 1:现在有两个系统,一个是Java做的系统,一个是PHP的系统,现在要把两个系统弄成一个单点登录. 2:两个系统两个库,两个库的表结构完全不同 ...
- ant
condition逻辑判断: <project name="testCondition"> <target name="test"> & ...
- CLR调试报错“Visual Studio远程调试监视器 (MSVSMON.EXE) 的 64 位版本无法调试 32 位进程或 32 位转储。请改用 32 位版本”的解决
Win7 64位电脑上进行visual studio的数据库项目的CLR存储过程进行调试时,报错: ---------------------------Microsoft Visual Studio ...
- Bluetooth HFP介绍
目录 1. 介绍 1.1 目的 1.2 使用场景 1.3 依赖关系 1.4 协议栈 1.5 角色 2. 应用层 3. 空白章节 4. 互操作性要求 4.1 介绍 4.2 Service Level C ...
- java并发编程-线程池的使用
参考文章:http://www.cnblogs.com/dolphin0520/p/3932921.html 深入剖析线程池实现原理 将从下面几个方面讲解: 1.线程池状态 2.任务的执行 3.线程池 ...
- QProgressBar的使用例子
今天下午动手实践了一下QProgressBar,遇到的问题比较多,浪费了不少时间,但收获同样颇多... 程序界面如下: 1 // progressbar.h 2 3 #ifndef PROGR ...
- 改变当前shell工作目录
执行脚本时候,只是在当前的shell下开了一个子进程,切换目录的操作只对该进程中相关后续指令有效,但改变不了父进程的目录. 解决方法: 法一: 用 source a.sh就行了. 法二: [fedor ...
- 导出证书Cer文件为Pem格式的步骤
(1)先导出Push Services的证书,比如我们命名为“magic_cert.p12”,注意导出时会让你输入密码. (2)再导出Push Services证书的密钥(Private Key),比 ...