在渲染管线中正确使用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. Nginx 笔记与总结(12)Nginx URL Rewrite 实例(ecshop)

    访问项目地址:http://192.168.254.100/ecshop 某个商品的 URL:http://192.168.254.100/ecshop/goods.php?id=3 现在需要实现把以 ...

  2. Javascript 笔记与总结(2-18)正则验证与正则匹配

    ① 判断 String 是否符合正则要求 patt.test(String); [例]表单提交: a.用户名不能为空,只能是数字及字母,6-11位 b.email 不能为空且格式正确 <!DOC ...

  3. How To Ask Questions The Smart Way

    How To Ask Questions The Smart Way Eric Steven Raymond Thyrsus Enterprises <esr@thyrsus.com> R ...

  4. centos时间同步方法

    centos时间同步方法 电脑软硬件应用网 45IT.COM 时间:2012-12-08 18:09 作者:李本清 新装的服务器可能设置了错误的,需要调整时区并调整时间.如下是使用NTP来从一个时间服 ...

  5. redis 应用场景

    1.string类型 : 图片和视频文件,静态文件 2.list 双向链表:回帖ID,我的关注列表,消息队列 length = redis.lpush('users:newest', 'user:go ...

  6. the OS maintains a number of queues

    COMPUTER ORGANIZATION AND ARCHITECTURE DESIGNING FOR PERFORMANCE NINTH EDITION To do its job, the OS ...

  7. SecureCRT SSH VMware Ubuntu

    Ubuntu 10.4 装完后网络OK. NAT模式下, 可以上网. 宿主机和客户机可以彼此ping通. 主要是检查SSH服务, 和防火墙是否关闭(UBUNTU 默认没有安装SSH, 默认启动了防火墙 ...

  8. 20145319 《java程序设计》课程总结

    20145319 <Java程序设计>课程总结 读书笔记链接总结 - 20145319 第一周学习总结 - 20145319 第二周学习总结 - 20145319 第三周学习总结 - 20 ...

  9. 前端 JSer 装逼手册

    阅读 8143收藏 2352016-7-18 SegmentFault 分享:吉祥物 @ SegmentFault 在装逼成本越来越高的 JS 圈,是时候充值一下了 -- 题记. 作者:kenberk ...

  10. Android源码剖析之Framework层基础版(窗口、linux、token、Binder)

    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 关于Framework,就是应用层底下的控制层,离应用层最近,总想找个机会,写写WindowMang ...