HDR

转载:https://www.cnblogs.com/wangchengfeng/p/3452035.html

1  HDR基本概念

      高动态范围光照(High Dynamic Range Imaging,简称HDRI或HDR),是一种表达超过了显示器所能表现的亮度范围的图像映射技术,已成为目前游戏应用不可或缺的一部分。通常,显示器能够显示R、G、B分量在[0, 255]之间的像素值。而256个不同的亮度级别显然不能表示自然界中光线的亮度情况。比如,太阳的亮度可能是一个白炽灯亮度的几千倍,是一个被白炽灯照亮的桌面的亮度的几十万倍,这远远超出了显示器的亮度表示能力。

  想象在一个房间中,刺眼的阳光从窗外照进来,若使用常规方法渲染这个房间,房间中白色的墙壁的颜色是(255, 255, 255),阳光的颜色也是(255, 255, 255),墙壁将表现得和窗外的阳光颜色一样。很明显,这和我们现实看到的差异很大,现实场景中阳光要比墙壁刺眼很多,我们需要使用某种技术对阳光的亮度和墙壁的亮度进行处理,让其在显示器上的效果接近现实效果。

  简单的将高范围的亮度按比例缩放后映射到[0, 255]是不可行的,比如将[0, 511]的范围按照2:1映射到[0, 255],虽然表示的亮度范围扩大了,但是将导致色带(Color Banding)问题,色带如图1所示。

  

图1 左图有色带问题,右图显示正常

  在有限的亮度范围内显示自然界中相当宽广的亮度范围,正是HDR技术所要解决的问题。 

2  HDR渲染步骤

  1)将整个场景渲染到一张浮点纹理上(16bit或32bit都可以);

  2)色调映射(Tone Mapping);

  3)渲染泛光(Bloom)效果;

  4)将泛光和色调映射的结果进行叠加。

  第一步很简单,只需要硬件支持浮点纹理即可,比较重要的是本文重点介绍的2、3两步,最后将2、3两步的结果进行叠加,形成最终效果图。

【显示设备上[0, 255]的亮度范围在着色器程序中使用[0, 1]的浮点数表示,下文的亮度和颜色值表示均使用着色器程序的标准。在算法中都用到了亮度的计算,每个像素的亮度的计算方法是L=0.27R+0.62G+0.06B】

3  色调映射

  色调映射是在有限动态范围媒介上近似显示高动态范围图像的技术。对于人眼来说也有类似的映射方式,因为人眼对亮度的感知范围远低于自然界的亮度范围,只能感知到某个范围内的光照。和显示设备不同的是,人眼对光的感知范围是动态变化的,例如从光亮的室外环境突然走入一个黑暗的室内环境,刚刚开始一片漆黑,过一会儿才可以看清周围环境,人眼的这个调节过程叫做光适应(Light Adaptation)。所以要模拟出真实的光照效果,除了表现出合适的光照,还需要模拟出人眼对光线的调节过程。

  最简单的色调映射是将亮度超过1的值置为1,这种做法会出现文章开始提到的墙壁和阳光一样亮的问题;另一种简单的色调映射是将每个像素的除以最高亮度像素的亮度值,可以很好的将所有像素的亮度映射到[0, 1]之间,这种方法会导致场景中某些特别亮的像素会导致场景中的其他部分特别暗。比较好的方式是采用平均亮度值进行调节,由于平均亮度值反映了场景中的整体亮度,所以受到场景中少部分过亮或过暗的像素影响不大。

3.1  计算平均亮度

  计算平均亮度的公式为:

该公式先对亮度取对数,平均后再进行幂运算。之所以不是直接对亮度平均,而是取了对数,是为了防止过亮的像素对整体造成的影响过大,该公式来源于参考文献1。

  计算平均亮度的最简单的方法是遍历所有像素,用上述公式求平均值。该方法需要CPU完成,效率不高,DirectX的“HDRLighting”例子采用了一种基于GPU加速的方法,利用像素着色器多次DownSampling,最后求得平均值,具体流程如下:

  1)首先将场景渲染到纹理中,在此基础上,对该纹理取样并计算相应像素亮度的ln()值并进行平均(相当于上述公式去掉exp),存入64*64的纹理中;

  2)对上一步的纹理4*4 DownSampling,生成16*16的纹理;

  3)对上一步的纹理4*4 DownSampling,生成4*4的纹理;

  4)对上一步的纹理4*4 DownSampling,生成1*1的纹理,并计算其exp()值。

最后生成的1*1的纹理中的像素为公式中要求的平均亮度Lavg。

3.2  光适应

  为了模拟人眼对于不同光强会自动调节适应范围的效果,只需要对这一帧求出的平均亮度Lavg与上一帧的平均亮度Lavg进行插值即可,当然这个插值不是线性插值,“HDRLighting”中的代码如下:

1

float fNewAdaptation = fAdaptedLum + (fCurrentLum - fAdaptedLum) * ( 1 - pow( 0.98f, 30 * g_fElapsedTime ) );

其中,fAdaptedLum为上一帧的Lavg,fCurrentLum为当前帧的Lavg,g_fElapsedTime为当前帧和上一帧的时间间隔,fNewAdaptation为最终Lavg的计算结果。

3.3 计算缩放因子

  场景的整体亮度可以通过缩放因子进行调节,公式如下:

其中Lscale(x,y)是当前像素的亮度值。Key是一个常数,Key 的大小决定了映射后场景的整体明暗程度,一般取0.18(在伽马校正理论中,0.18经过校正后大概是0.5,也就是我们感官上的中等灰度级)。Key值的选择可以看作摄像机的曝光程度,我们可以使用这个公式控制自由的摄像机的曝光程度,Key越大整个场景就显得越白。一般来说,高曝光的Key最高为0.72,低曝光的Key最低为0.045,一般程度的曝光Key选择0.18附近的值。

3.4  归一化处理

  到此为止,色调映射已经基本完成,剩下的只需要将Lscale(x,y)映射到[0, 1]范围内即可,公式如下:

其中其中Color(x,y)是当前像素的颜色值。

4  渲染泛光效果

  泛光是一种光学效应,它是指在来自于强光源的光线看起来像是影响到了周围物体。想象一间房间,窗户外面阳光明媚,若往窗外看去,感觉窗户光亮的边缘有一圈模糊,这就是泛光效果。在游戏中适当的增加泛光效果,能够增强画面的真实感。

  渲染泛光效果的思路很简单,主要分为两个步骤,第一步是使用bright-pass filter提取出场景中高亮部分,第二步对高亮部分进行模糊处理。"HDRLighting"中bright-pass filter的代码如下:

 

// Determine what the pixel's value will be after tone mapping occurs
vSample.rgb *= g_fMiddleGray/(fAdaptedLum + 0.001f);

// Subtract out dark pixels
vSample.rgb -= BRIGHT_PASS_THRESHOLD;

// Clamp to 0
vSample = max(vSample, 0.0f);

// Map the resulting value into the 0 to 1 range. Higher values for
// BRIGHT_PASS_OFFSET will isolate lights from illuminated scene 
// objects.
vSample.rgb /= (BRIGHT_PASS_OFFSET+vSample);

第二步则是对第一步的结果进行模糊,首先对第一步的结果进行2*2或4*4的downsampling,再使用2*2的高斯核心对其进行模糊。其中先对图像downsampling再模糊的做法是利用GPU进行图像模糊的一种提高性能的方法,因为downsampling后图像分辨率降低了,计算量自然就少了;而downsampling后的图片再放大,本身又是一种模糊,可以减少高斯模糊的采样数量。最终效果如图2所示

      

图2 图片来源于微软DirectX SDK中“HDRLighting”例子,左图为过度曝光的场景,中间图片为bright-pass filter处理后的结果,右图为模糊之后的效果

5  融合

  现在需要将色调映射的图像与泛光的图像进行融合。在融合操作中除了常见的α融合外,还有一种叠加(Additive Blending)操作,它可以将两种颜色的值进行加法操作,通常会使得颜色越变越白,这正是我们所要的效果。将色调映射的图像与泛光的图像进行叠加操作,即可生成最终图像。

6、最终效果

  “HDRLighting”中LDR和HDR的效果对比如图3所示。

    

图3 “HDRLighting”中LDR和HDR的效果对比,左图为LDR,右图为HDR

  其他的一些HDR渲染效果如图4-7所示。图4中很明显的观察到汽车窗户的镜面光,图5中蓝色的灯光显得比较亮,但是暗的地方也很清晰;图6和图7来自游戏“孤岛危机”,效果相当不错,其中就有HDR的功劳(当然要做到这样的画质,HDR只是冰山一角)。图6场景明暗得当,透过树叶可以看到天空略微模糊;图7中可以看到爆炸产生的碎片以及车上的显示屏有泛光效果,充分体现了亮度的差别,而这些细节让游戏更加逼真。

图4 Unity3D文档中的HDR渲染效果图1

图5 Unity3D文档中的HDR渲染效果图2

图6 “孤岛危机”截图1

图7 “孤岛危机”截图2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值