Monthly Archives: May 2016

高动态范围光

本篇文章谢绝转载,也禁止用于任何商业目的。文章中的源码实现的下载地址参见https://github.com/twinklingstar20/hdr_demo 1. 色阶重建 色阶重建(Tone Mapping)是什么样的一个技术呢,它与高动态光照渲染(High Dynamic Range, HDR)又有什么关系呢?,且听我娓娓道来。 假设你是一名摄影爱好者,一定听过HDR这个词,它的专业称法为高动态范围成像(High Dynamic Range Image, HDRI),是用来实现比普通图像(每个颜色通道占8位)表示的曝光范围更大的一种技术,高动态范围成像的目的就是要正确地表示真实世界中从太阳光直射到最暗的阴影这样大的范围亮度。举个例子来解释,大晴天出门去摄(zhuang)影(bi),拍摄了图1所示的一张图,一定会被人喷不专业,因为你这是想证明你对自然景色更感兴趣呢,还是对中国的文化遗产更感兴趣呢?照顾了天空的曝光,就使得暗部欠缺曝光。 图1. 想更准确的拍摄天空 你为了证明你是爱中国的文化遗产,你想重点关注建筑物,于是你拍摄了另外一张图,如图2所示,确实证明了你的爱,但是背景又是什么鬼,那是霾吗,大北京这么难能可贵的大晴天,却拍出了雾霾的效果。 分析下造成这个问题的原因,简单来说,照相机拍摄的亮度(Luminance)范围是有限的,如果想要亮部更加清晰,特别暗的区域就显得比较黑;相反,如果想要暗部更加清晰,特别亮的区域就会被截断(Clamp to white)成白色,显得特别的亮。我们想要的结果如图3所示,不仅能清楚的显现出建筑的模样,同时还有一个比较清晰的背景,这就是色阶重建技术达到的效果,它把一个高动态范围的亮度,变换为低动态范围(Low Dynamic Range)的亮度,使仅用低动态范围的亮度能清晰、有效地还原场景的效果,PERFECT!!! 图3. 高动态范围成像技术达到的效果图 再举个例子,如图4所示,我们可以用不同的曝光度,拍摄一组图片,由暗到亮,然后采用色阶重建技术把它变换为一组明暗协调的图片,如图5所示。色阶重建技术,又可以细分为全局色阶重建和局部色阶重建,想要了解各种色阶重建技术可以参见Reinhard etc。 图4. 不同曝光度的一组图像 图5. 全局和局部色阶重建后的图像 至此,介绍了高动态范围成像(HDRI)和色阶重建之间的关系,可以简单把色阶重建技术划分为全局色阶重建和局部色阶重建。接下来,又提出一个问题,在实时渲染中,特别在多光源的光照计算中,为什么存在无法渲染高动态范围的问题呢? 在计算机的颜色缓冲区中,存储的颜色范围是\(\),当多光源的光照计算,由于亮度的叠加,容易导致亮度超过1.0,此时,硬件设备(GPU)会把它截断至亮度1.0,大部分情况下做这样一种截断处理是没有问题的。但是如果一个场景中绝大部分的片断(像素)经过多光源的光照叠加,它的亮度都超过1.0,简单的把颜色截断至\(\)范围内,那么整个场景有会显得曝光过度,物体的细节无法有效的得到呈现,如图6所示,整块区域就显得白花花的。可以模仿摄影中的色阶重建技术,同时渲染多张不同曝光度的高动态范围的图像,再通过色阶重建技术得到低动态范围的图像。那么问题来了,在实时渲染中,颜色缓冲区会把亮度超过1.0的图像自动截断至1.0,换句话说,就是GL_RGB类型的颜色缓冲区是无法存储高动态范围的帧,此时又如何存储渲染出来的高动态范围的帧呢? 图6. 曝光过度的渲染效果 这就有了新的图像存储格式的诞生,最早由Greg Ward在1985年提出了RGBE 32位的图像存储格式,RGB表示颜色值,E(Exponent)表示RBG颜色的指数系数,通过指定不同的E,就能有效的存储不同高动态范围的图像。后续又出现了多种不同的HDR格式的标准,例如Pixar Log Encoding(TIFF)、Radiance RGBE

spacer

伽马校正

本篇文章谢绝转载,也禁止用于任何商业目的。 首先,解释下伽马(Gamma)是什么鬼。 以一个数字照相机为例,光子打到传感器上,显现出图像,如果有双倍的光子打到传感器,就会有双倍的电子信号,它是一个线性的关系。但是,人眼对光的感知并不是一个线性的关系,与照相机相比,人眼对暗色调会更加敏感些,使得人眼能感知的光照范围更加广(我也不知道为什么!!!)。如图1所示,图(1)人眼观测到的50%亮度的,实际上只有百分之二十多;图(2)是照相机捕获的50%亮度的颜色,明显比图(1)更亮。 图1. 人眼观测到的与照相机检测到的亮度对比 这与伽马又有什么关系呢?伽马建立起了一个照相机捕获的亮度与人眼观察到的亮度的对应关系,称之为伽马编码。设输入和输出的光照分别表示为\({{L}_{in}},{{L}_{out}}\),伽马系数为\(gamma\),得到关系等式(1),等式(1)的函数示意图如图2所示。\(gamma\succ 1\)的函数变化曲线如图中的红色曲线所示,\(gamma\prec 1\)的函数变化曲线如图中的蓝色曲线所示。 \({{L}_{out}}={{L}_{in}}^{gamma} \tag{1}\) 图2. 幂函数的示意图 因为人眼对暗色调(Dark Tone)更敏感,我们在存储图像文件的时候,对颜色进行伽马编码(Gamma Encoding),如图3所示,伽马编码产生的颜色带中暗色范围更宽。以相同的位数来表示颜色(例如8位),线性编码(Linear Encoding)可能需要更多的位数才能表现与伽马编码相同的效果。 图3. 以2.2伽马系数进行编码的颜色表对比图 伽马编码可以使得8位就能有效的描述绝大部分场景,但是它使得图像的存储和呈现过程变得更加复杂。(1)图像的存储过程,颜色通过系数为\(1/gamma\)的幂函数进行处理;(2)图像的显示,存储的颜色,需要通过显示器采用系数\(gamma\)的幂函数进行处理,即与图像存储相反的过程,它是显示设备内置的处理过程,我们可以把它理解为所有的显示设备都会行进伽马编码的逆处理。如图4所示,(1)图像伽马处理,把图像保存为JPEG或TIFF,(所有的JPEG格式的文件都会进行伽马编码)会采用系数为\(1/gamma\)的幂函数进行预校正,使得指定的位数有效的表示图像;(2)显示伽马处理,是显示设备对颜色进行的校正,两个过程中和后能有效的呈现的颜色。绝大部分显示设备采用的伽马系数为2.2。 图4. 伽马编码和显示 如果图像伽马处理与显示伽马处理不能达到中和,就会使得呈现出来的图像过暗或者过亮。如图5所示,蓝色表示图像伽马处理曲线,红色表示显示伽马处理曲线,紫色表示两者叠加处理后的函数曲线图,不同的中和结果就会产生不同的效果。 图5. 设图像伽马系数为2.2,与不同的显示伽马系数中和作后呈现出来的效果对比图 至此,我们知道,伽马是什么,为什么需要伽马编码,不同的伽马处理对呈现出来的图像的影响。前面阐述的是伽马编码在摄像机(照相机)从现实生活中捕获图像数据,再呈现到显示设备上影响。而在实时渲染的过程中,图像数据的捕获过程就相当于GPU的渲染过程,它可能没有涉及伽马编码,但是显示设备仍然会偷偷滴地帮我们完成伽马编码的逆处理,那么它会引发什么样的问题呢?很多时候,我们渲染出一帧的图像,都会通过显示设备调整渲染出的效果至到它符合我们预期,其实这个过程也考虑到了显示设备隐藏的逆处理,虽然它调试的显示器上显示正常,可能在不同的伽马系数的显示器就会有不同的呈现效果,而且有些情况,我们需要在渲染上对输出到显示设备的帧数据进行校正,特别是有大量动态范围光照的渲染中。这里引入一个概念,伽马校正(Gamma Correction),它是对显示设备对最终输出颜色逆处理的逆处理过程,没有写错,是逆处理的逆处理。 举个例子来阐述下伽马校正的原理,设颜色\(c=\left( 0.5,0,0 \right)\),显示设备的伽马系数为\(gamma=2.2\)经过显示设备的幂函数处理,实际上我们看到它呈现出来的颜色为\({{\left( 0.5,0,0 \right)}^{2.2}}=\left( 0.218,0,0 \right)\)。但是如果在输出到显示设备前,对它进行伽马校正,即\({{\left( 0.5,0,0 \right)}^{1/2.2}}=\left( 0.730,0,0 \right)\),那么,我们在显示设备上实际看到的颜色为 \({{\left( 0.730,0,0 \right)}^{2.2}}=\left(

spacer