1 HDR基本概念
高動(dòng)態(tài)范圍光照(High Dynamic Range Imaging,簡(jiǎn)稱HDRI或HDR),是一種表達(dá)超過(guò)了顯示器所能表現(xiàn)的亮度范圍的圖像映射技術(shù),已成為目前游戲應(yīng)用不可或缺的一部分。通常,顯示器能夠顯示R、G、B分量在[0, 255]之間的像素值。而256個(gè)不同的亮度級(jí)別顯然不能表示自然界中光線的亮度情況。比如,太陽(yáng)的亮度可能是一個(gè)白熾燈亮度的幾千倍,是一個(gè)被白熾燈照亮的桌面的亮度的幾十萬(wàn)倍,這遠(yuǎn)遠(yuǎn)超出了顯示器的亮度表示能力。
想象在一個(gè)房間中,刺眼的陽(yáng)光從窗外照進(jìn)來(lái),若使用常規(guī)方法渲染這個(gè)房間,房間中白色的墻壁的顏色是(255, 255, 255),陽(yáng)光的顏色也是(255, 255, 255),墻壁將表現(xiàn)得和窗外的陽(yáng)光顏色一樣。很明顯,這和我們現(xiàn)實(shí)看到的差異很大,現(xiàn)實(shí)場(chǎng)景中陽(yáng)光要比墻壁刺眼很多,我們需要使用某種技術(shù)對(duì)陽(yáng)光的亮度和墻壁的亮度進(jìn)行處理,讓其在顯示器上的效果接近現(xiàn)實(shí)效果。
簡(jiǎn)單的將高范圍的亮度按比例縮放后映射到[0, 255]是不可行的,比如將[0, 511]的范圍按照2:1映射到[0, 255],雖然表示的亮度范圍擴(kuò)大了,但是將導(dǎo)致色帶(Color Banding)問(wèn)題,色帶如圖1所示。


圖1 左圖有色帶問(wèn)題,右圖顯示正常
在有限的亮度范圍內(nèi)顯示自然界中相當(dāng)寬廣的亮度范圍,正是HDR技術(shù)所要解決的問(wèn)題。
2 HDR渲染步驟
1)將整個(gè)場(chǎng)景渲染到一張浮點(diǎn)紋理上(16bit或32bit都可以);
2)色調(diào)映射(Tone Mapping);
3)渲染泛光(Bloom)效果;
4)將泛光和色調(diào)映射的結(jié)果進(jìn)行疊加。
第一步很簡(jiǎn)單,只需要硬件支持浮點(diǎn)紋理即可,比較重要的是本文重點(diǎn)介紹的2、3兩步,最后將2、3兩步的結(jié)果進(jìn)行疊加,形成最終效果圖。
【顯示設(shè)備上[0, 255]的亮度范圍在著色器程序中使用[0, 1]的浮點(diǎn)數(shù)表示,下文的亮度和顏色值表示均使用著色器程序的標(biāo)準(zhǔn)。在算法中都用到了亮度的計(jì)算,每個(gè)像素的亮度的計(jì)算方法是L=0.27R+0.62G+0.06B】
3 色調(diào)映射
色調(diào)映射是在有限動(dòng)態(tài)范圍媒介上近似顯示高動(dòng)態(tài)范圍圖像的技術(shù)。對(duì)于人眼來(lái)說(shuō)也有類似的映射方式,因?yàn)槿搜蹖?duì)亮度的感知范圍遠(yuǎn)低于自然界的亮度范圍,只能感知到某個(gè)范圍內(nèi)的光照。和顯示設(shè)備不同的是,人眼對(duì)光的感知范圍是動(dòng)態(tài)變化的,例如從光亮的室外環(huán)境突然走入一個(gè)黑暗的室內(nèi)環(huán)境,剛剛開(kāi)始一片漆黑,過(guò)一會(huì)兒才可以看清周圍環(huán)境,人眼的這個(gè)調(diào)節(jié)過(guò)程叫做光適應(yīng)(Light Adaptation)。所以要模擬出真實(shí)的光照效果,除了表現(xiàn)出合適的光照,還需要模擬出人眼對(duì)光線的調(diào)節(jié)過(guò)程。
最簡(jiǎn)單的色調(diào)映射是將亮度超過(guò)1的值置為1,這種做法會(huì)出現(xiàn)文章開(kāi)始提到的墻壁和陽(yáng)光一樣亮的問(wèn)題;另一種簡(jiǎn)單的色調(diào)映射是將每個(gè)像素的除以最高亮度像素的亮度值,可以很好的將所有像素的亮度映射到[0, 1]之間,這種方法會(huì)導(dǎo)致場(chǎng)景中某些特別亮的像素會(huì)導(dǎo)致場(chǎng)景中的其他部分特別暗。比較好的方式是采用平均亮度值進(jìn)行調(diào)節(jié),由于平均亮度值反映了場(chǎng)景中的整體亮度,所以受到場(chǎng)景中少部分過(guò)亮或過(guò)暗的像素影響不大。
3.1 計(jì)算平均亮度
計(jì)算平均亮度的公式為:

該公式先對(duì)亮度取對(duì)數(shù),平均后再進(jìn)行冪運(yùn)算。之所以不是直接對(duì)亮度平均,而是取了對(duì)數(shù),是為了防止過(guò)亮的像素對(duì)整體造成的影響過(guò)大,該公式來(lái)源于參考文獻(xiàn)1。
計(jì)算平均亮度的最簡(jiǎn)單的方法是遍歷所有像素,用上述公式求平均值。該方法需要CPU完成,效率不高,DirectX的“HDRLighting”例子采用了一種基于GPU加速的方法,利用像素著色器多次DownSampling,最后求得平均值,具體流程如下:
1)首先將場(chǎng)景渲染到紋理中,在此基礎(chǔ)上,對(duì)該紋理取樣并計(jì)算相應(yīng)像素亮度的ln()值并進(jìn)行平均(相當(dāng)于上述公式去掉exp),存入64*64的紋理中;
2)對(duì)上一步的紋理44 DownSampling,生成1616的紋理;
3)對(duì)上一步的紋理44 DownSampling,生成44的紋理;
4)對(duì)上一步的紋理44 DownSampling,生成11的紋理,并計(jì)算其exp()值。
最后生成的11的紋理中的像素為公式中要求的平均亮度Lavg。*
3.2 光適應(yīng)
為了模擬人眼對(duì)于不同光強(qiáng)會(huì)自動(dòng)調(diào)節(jié)適應(yīng)范圍的效果,只需要對(duì)這一幀求出的平均亮度Lavg與上一幀的平均亮度Lavg進(jìn)行插值即可,當(dāng)然這個(gè)插值不是線性插值,“HDRLighting”中的代碼如下:
|
1
|
float fNewAdaptation = fAdaptedLum + (fCurrentLum - fAdaptedLum) * ( 1 - ``pow``( 0.98f, 30 * g_fElapsedTime ) );
|
其中,fAdaptedLum為上一幀的Lavg,fCurrentLum為當(dāng)前幀的Lavg,g_fElapsedTime為當(dāng)前幀和上一幀的時(shí)間間隔,fNewAdaptation為最終Lavg的計(jì)算結(jié)果。
3.3 計(jì)算縮放因子
場(chǎng)景的整體亮度可以通過(guò)縮放因子進(jìn)行調(diào)節(jié),公式如下:

其中Lscale(x,y)是當(dāng)前像素的亮度值。Key是一個(gè)常數(shù),Key 的大小決定了映射后場(chǎng)景的整體明暗程度,一般取0.18(在伽馬校正理論中,0.18經(jīng)過(guò)校正后大概是0.5,也就是我們感官上的中等灰度級(jí))。Key值的選擇可以看作攝像機(jī)的曝光程度,我們可以使用這個(gè)公式控制自由的攝像機(jī)的曝光程度,Key越大整個(gè)場(chǎng)景就顯得越白。一般來(lái)說(shuō),高曝光的Key最高為0.72,低曝光的Key最低為0.045,一般程度的曝光Key選擇0.18附近的值。
3.4 歸一化處理
到此為止,色調(diào)映射已經(jīng)基本完成,剩下的只需要將Lscale(x,y)映射到[0, 1]范圍內(nèi)即可,公式如下:

其中其中Color(x,y)是當(dāng)前像素的顏色值。
4 渲染泛光效果
泛光是一種光學(xué)效應(yīng),它是指在來(lái)自于強(qiáng)光源的光線看起來(lái)像是影響到了周圍物體。想象一間房間,窗戶外面陽(yáng)光明媚,若往窗外看去,感覺(jué)窗戶光亮的邊緣有一圈模糊,這就是泛光效果。在游戲中適當(dāng)?shù)脑黾臃汗庑Ч?,能夠增?qiáng)畫面的真實(shí)感。
渲染泛光效果的思路很簡(jiǎn)單,主要分為兩個(gè)步驟,第一步是使用bright-pass filter提取出場(chǎng)景中高亮部分,第二步對(duì)高亮部分進(jìn)行模糊處理。"HDRLighting"中bright-pass filter的代碼如下:
[
](javascript:void(0); "復(fù)制代碼")
<pre style="margin: 0px 0px 0px 22px; white-space: pre-wrap; overflow-wrap: break-word; font-size: 12px !important; font-family: "Courier New" !important;"> 1 // Determine what the pixel's value will be after tone mapping occurs
2 vSample.rgb *= g_fMiddleGray/(fAdaptedLum + 0.001f);
3
4 // Subtract out dark pixels
5 vSample.rgb -= BRIGHT_PASS_THRESHOLD; 6
7 // Clamp to 0
8 vSample = max(vSample, 0.0f);
9
10 // Map the resulting value into the 0 to 1 range. Higher values for 11 // BRIGHT_PASS_OFFSET will isolate lights from illuminated scene 12 // objects.
13 vSample.rgb /= (BRIGHT_PASS_OFFSET+vSample); </pre>

](javascript:void(0); "復(fù)制代碼")
第二步則是對(duì)第一步的結(jié)果進(jìn)行模糊,首先對(duì)第一步的結(jié)果進(jìn)行22或44的downsampling,再使用2*2的高斯核心對(duì)其進(jìn)行模糊。其中先對(duì)圖像downsampling再模糊的做法是利用GPU進(jìn)行圖像模糊的一種提高性能的方法,因?yàn)閐ownsampling后圖像分辨率降低了,計(jì)算量自然就少了;而downsampling后的圖片再放大,本身又是一種模糊,可以減少高斯模糊的采樣數(shù)量。最終效果如圖2所示



圖2 圖片來(lái)源于微軟DirectX SDK中“HDRLighting”例子,左圖為過(guò)度曝光的場(chǎng)景,中間圖片為bright-pass filter處理后的結(jié)果,右圖為模糊之后的效果
5 融合
現(xiàn)在需要將色調(diào)映射的圖像與泛光的圖像進(jìn)行融合。在融合操作中除了常見(jiàn)的α融合外,還有一種疊加(Additive Blending)操作,它可以將兩種顏色的值進(jìn)行加法操作,通常會(huì)使得顏色越變?cè)桨?,這正是我們所要的效果。將色調(diào)映射的圖像與泛光的圖像進(jìn)行疊加操作,即可生成最終圖像。
6、最終效果
“HDRLighting”中LDR和HDR的效果對(duì)比如圖3所示。


圖3 “HDRLighting”中LDR和HDR的效果對(duì)比,左圖為L(zhǎng)DR,右圖為HDR
其他的一些HDR渲染效果如圖4-7所示。圖4中很明顯的觀察到汽車窗戶的鏡面光,圖5中藍(lán)色的燈光顯得比較亮,但是暗的地方也很清晰;圖6和圖7來(lái)自游戲“孤島危機(jī)”,效果相當(dāng)不錯(cuò),其中就有HDR的功勞(當(dāng)然要做到這樣的畫質(zhì),HDR只是冰山一角)。圖6場(chǎng)景明暗得當(dāng),透過(guò)樹(shù)葉可以看到天空略微模糊;圖7中可以看到爆炸產(chǎn)生的碎片以及車上的顯示屏有泛光效果,充分體現(xiàn)了亮度的差別,而這些細(xì)節(jié)讓游戲更加逼真。

圖4 Unity3D文檔中的HDR渲染效果圖1

圖5 Unity3D文檔中的HDR渲染效果圖2

圖6 “孤島危機(jī)”截圖1

圖7 “孤島危機(jī)”截圖2
參考文獻(xiàn):
[1]Reinhard, Erik, Mike Stark, Peter Shirley, and James Ferwerda. "Photographic Tone Reproduction for Digital Images". ACM Transactions on Graphics (TOG), Proceedings of the 29th Annual Conference on Computer Graphics and Interactive Techniques (SIGGRAPH), pp. 267-276. New York, NY: ACM Press, 2002.
[2]Akenine-M?ller T, Haines E, Hoffman N. Real-time rendering 3 [M].
[3]DirectX 9.0c SDK
[5]http://wenku.baidu.com/view/fe31607ea26925c52cc5bf97.html
[6]http://www.zwqxin.com/archives/shaderglsl/review-high-dynamic-range.html
[7]http://zh.wikipedia.org/wiki/%E9%AB%98%E5%8A%A8%E6%80%81%E8%8C%83%E5%9B%B4%E6%88%90%E5%83%8F