线性空间光照(即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)这一个名词,但真正明白它是什么.为什么要有它.以及怎么用它的人其实不多.我也不例外. 最初我查过一些资料,但很多文章 ...
随机推荐
- PHP 开发 APP 接口 学习笔记与总结 - APP 接口实例 [6] 版本升级接口开发
判定 app 是否需要加密:通过 app 表中的 status 字段来判定,加密的字符串为 app 表中的 key 字段. 在获取的客户端和服务器端(数据库表中相应字段)的版本号不一致时,返回 dat ...
- PHP OO 编程笔记
1. 类中的方法不是全局方法,可以和外部的普通方法重名,例如: <?php function time(); 则会报错:不能重新声明方法 Fatal error: Cannot redeclar ...
- 【翻译】Kinect v2程序设计(C++) BodyIndex篇
通过Kinect SDK v2预览版,取得BodyIndex(人体区域)的方法和示例代码. 上一节,介绍了从Kinect v2预览版用Kinect SDK v2预览版获取Depth数据的方法. 这 ...
- PHP不能创建csv中文名文件
使用iconv 将utf-8转换成gb2312即可$name = iconv("utf-8","gb2312",'分销商下载课件记录');
- Rails--%w用法[转]
%Q 用于替代双引号的字符串. 当你需要在字符串里放入很多引号时候, 可以直接用下面方法而不需要在引号前逐个添加反斜杠 (\") >> %Q(Joe said: "Fr ...
- php 通过exec 创建git分支失败
今天给我们自己的发布系统增加一个新建分支的功能,操作比较简单,但是使用php执行shell命令的时候总是无法push分支到远程,但是登陆服务器执行却是可以的 新建分支命令如下 git fetch -- ...
- HTML5的新事件
HTML 元素可拥有事件属性,这些属性在浏览器中触发行为,比如当用户单击一个 HTML 元素时启动一段 JavaScript. HTML 元素可拥有事件属性,这些属性在浏览器中触发行为,比如当用户单击 ...
- sp.net2.0中的新增控件BulletedList的一些高级用法
asp.net2.0新增了一个BulletedList控件,通过它可以以列表形式显示数据,而不必再用Repeater,Datalist等实现相同的效果.今天做程序的时候正好用到了这个控件,就把它的一些 ...
- 文件批量上传的工具,要实现暂停继续、断点续传等功能(使用QtNetwork和QHttpMultiPart,和定时器检查超时)
最近在做一个文件批量上传的工具,要实现暂停继续.断点续传等功能.利用Qt自带的QtNetwork模块,完成这些需求并没有费多少周章,主要思路就是将文件分块,然后用while循环依次传输.具体实现代码比 ...
- SVN命令详解
在开发中,除了在本机文件夹上进行svn更新外,在命令行中进行svn操作也非常关键,下面列举下网站摘抄的一些文档:1.将文件checkout到本地目录 svn checkout path(path是服务 ...