在渲染管线中正确使用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)的更多相关文章

  1. PBR(基于物理的渲染)学习笔记

    PBR基本介绍 PBR代表基于物理的渲染,本质上还是 gl_FragColor = Emssive + Ambient + Diffuse + Specular 可能高级一些在考虑下AO也就是环境光遮 ...

  2. 剖析Unreal Engine超真实人类的渲染技术Part 1 - 概述和皮肤渲染

    一.概述 1.1 数字人类的概要 数字人类(Digital Human)是利用计算机模拟真实人类的一种综合性的渲染技术.也被称为虚拟人类.超真实人类.照片级人类. 它是一种技术和艺术相结合的综合性模拟 ...

  3. Gamma校正与线性空间

    基础知识部分 为了方便理解,首先会对(Luminance)的相关概念做一个简单介绍.如果已经了解就跳到后面吧. 我们用Radiant energy(辐射能量)来描述光照的能量,单位是焦耳(J),因为光 ...

  4. 浅谈unity中gamma空间和线性空间

    转载请标明出处:http://www.cnblogs.com/zblade/ 一.概述 很久没有写文章了,今天写一篇对gamma空间和线性空间的个人理解总结,在查阅和学习了各个资料后,算是一个个人笔记 ...

  5. 【图形学】我理解的伽马校正(Gamma Correction)

    http://blog.csdn.net/candycat1992/article/details/46228771/ 写在前面 我相信几乎所有做图像处理方面的人都听过伽马校正(Gamma Corre ...

  6. 聊聊Unity的Gamma校正以及线性工作流

    0x00 前言的前言 这篇小文其实是在清明节前后起的头,不过后来一度搁笔.一直到这周末才又想起来起的这个头还没有写完,所以还是直接用一个月前的开头,再将过程和结尾补齐. 0x01 前言 结束了在南方一 ...

  7. OpenGL核心技术之Gamma校正

    笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:<手把手教你/2.2次幂.Gamma校正后的暗红色就会成为(0.5,0.0 ...

  8. LearnOpenGL.PBR.光照

    光源辐射率:      辐射率(radiance)表示光源在给定立体角ω下的辐射通量(或光源发射的能量).     那么假设立体角ω无限小时,辐射率就表示单束光线(或说某个单一方向)的辐射通量. 点光 ...

  9. 【视频开发】伽马校正(gamma correction)学习笔记

    我相信几乎所有做图像处理方面的人都听过伽马校正(Gamma Correction)这一个名词,但真正明白它是什么.为什么要有它.以及怎么用它的人其实不多.我也不例外.  最初我查过一些资料,但很多文章 ...

随机推荐

  1. PHP 错误与异常 笔记与总结(15 )使用观察者模式处理异常信息

    使异常处理变得更灵活.可观察,可以使用设计模式中的观察者模式. 文件 ① 定义观察者的接口 ExceptionObserver.php: <?php /* 给观察者定义的规范 */ interf ...

  2. UITableview cell 的多选

    利用NSMutableDictionary  key值 来改变cell的状态 -(void)createUI{ table = [[UITableView alloc]initWithFrame:CG ...

  3. php程序效率优化的一些策略小结

    php程序效率优化的一些策略小结   1.在可以用file_get_contents替代file.fopen.feof.fgets等系列方法的情况下,尽量用 file_get_contents,因为他 ...

  4. Yii源码阅读笔记(十三)

    Model类,集中整个应用的数据和业务逻辑: namespace yii\base; use Yii; use ArrayAccess; use ArrayObject; use ArrayItera ...

  5. mysql入门问题

    1.给列设置默认时间 在创建表的图形化界面,不能直接用函数sysdate()或者now()来设置默认当前时间,要用sql脚本创建的话就可以 IF NOT EXISTS `tb_1` THENBEGIN ...

  6. sql_action

    CREATE TABLE w SELECT * FROM existing_table 2 日期x idm valuexm 日期x idn  valuexn 日期y idm  valueym 日期y ...

  7. How Browsers Work: Behind the scenes of modern web browsers

    http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/#Parser_Lexer_combination Grammars ...

  8. Flink - Juggling with Bits and Bytes

    http://www.36dsj.com/archives/33650 http://flink.apache.org/news/2015/05/11/Juggling-with-Bits-and-B ...

  9. Machine Learning in Action -- Support Vector Machines

    虽然SVM本身算法理论,水比较深,很难懂 但是基本原理却非常直观易懂,就是找到与训练集中支持向量有最大间隔的超平面 形式化的描述: 其中需要满足m个约束条件,m为数据集大小,即数据集中的每个数据点fu ...

  10. 五 mybatis的SqlMapConfig.xml详解

    SqlMapConfig.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE conf ...