日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

HDR (automatic exposure control + Tonemapping + Bloom)

發布時間:2025/7/25 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HDR (automatic exposure control + Tonemapping + Bloom) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
<div class="markdown_views"><!-- flowchart 箭頭圖標 勿刪 --><svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path></svg><p>前段時間在VR項目里實現了PCF+VSM之后,為了讓陽光照射的區域看起來亮一些,降低了陰影區域的亮度,明暗區域的對比度確實高了,但是室內就有些過于暗了,于是尋思著得把HDR模塊加上了。</p>

一般我們說的HDR其實是代指,automatic exposure control + Tonemapping + Bloom, 先根據場景的一幀計算出平均亮度,如果偏暗就加亮一些,反之亦然,調整好亮度之后再調整灰度,讓明部跟暗部保持更多的細節,最后對高光部分做個Bloom,看起來更真實。

在現實世界中 ,很亮的燈周圍都會有一圈模糊的光暈,Bloom就是指這個光暈。我的HDR處理是在計算完光照跟陰影之后進行的,光照著色的輸出為一張RGBA16F格式的紋理,注意這里不能再用RGBA8了,用這張紋理作為HDR的輸入。

首先進行計算的是平均亮度,這里有兩種計算方式,一是利用RTT的方式,渲染到一張一半長寬的紋理里去,循環這個操作,直到紋理只剩1x1像素,這個像素就是場景所有像素的平均了,這里要注意的一點是,RTT的時候,原紋理的過濾方式一定要設為Linear,這樣渲染出來的半尺寸紋理才是原紋理的平均。方式二是利用ComputeShader,這個實現跟RTT類似,但是速度會快上一倍左右,因為VR程序非常非常考驗性能,我選擇的就是這種方式,這里重點說下這個。

第一個問題就是原紋理的尺寸不是2的N次冪,而且會根據用戶設定變化,這種無規律的尺寸在ComputeShader里面處理起來比較麻煩,我們需要先把它規格化一下,一般的方案是RTT到一張長寬分別是 離原紋理的一半最近的2的N次冪的紋理上,比如1733*1733就應該規格化到 1024*1024的紋理上。尺寸規范化之后,就可以很方便的用ComputeShader計算平均亮度了,這里面有個技巧,就是可以利用GPU的并行操作進一步加快計算過程,具體參見這里:《AVERAGE LUMINANCE CALCULATION USING A COMPUTE SHADER》
使用sRGB計算亮度的公式是這個:

float lum = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
  • 1

這個公式其實是色彩空間轉換的一部分,在CIE XYZ色彩空間中,Y分量代表的就是亮度,而完整的sRGB轉換成CIE XYZ的公式是張這樣的:

ComputeShader中的第一步,就是計算出這個亮度,然后再對這個亮度做平均。其實亮度并不是線性的,對非線性的值直接做平均,最后得到的平均值并不準確,可以通過ln(lum)把亮度轉化成線性的,然后對最后的全場景平均值再用exp(lum)轉化回來,結果會更精確,不過我實際測試的結果來說,做不做這一步并沒有明顯的區別。另外一點需要特別提出來的,是場景畫面中央的部分應該具有高權重,而邊緣應該低些,總權重保持為1.我是在場景計算成16*16的時候引入的權重,中央4*4個像素具有更高的權重。

上一步執行完畢之后,我們會擁有一張1*1像素的平均亮度紋理,這個值別讀到CPU端來,因為讀取會造成渲染循環阻塞,非常的影響性能。只需要在需要使用的時候,用采樣器采樣就行了,采樣坐標注意要設為(0.5,0.5)。有了當前場景的平均亮度,我們要計算一下適配亮度,人眼適應光線變化是一個漸進的過程,所以我們不能單純的根據當幀的平均亮度來決定場景的亮度,需要引入上一幀的平均亮度,在兩個亮度之間做一個插值。計算公式如下:

float adaptedLum = lastLum + (currentLum - lastLum) * (1.0-pow(0.98, 30*elapesdTime));
  • 1

其中,elapesdTime是上一幀的耗時, 30是個經驗參數,希望亮度變化更快可以加大這個值。另外一個就是需要加上一句類似如下功能的代碼防止極端情況發生:

adaptedLum = clamp(adaptedLum, 0.3, 0.7);
  • 1

同樣,0.3跟0.7是經驗參數;有了適配亮度之后,就可以進行場景的亮度校正了,我們希望場景不過暗,也不過亮,也就是希望亮度剛好在0.5左右,因此我們可以利用如下公式來計算曝光度:

float exposure = 0.5 / adaptedLum;
  • 1

公式內的0.5可以弄成一個傳入參數,以便根據用戶期望來調整場景的亮度。將輸入紋理的每一個像素都乘上exposure,自動曝光也就完成了。一般在這個shader里面,還會同時進行ToneMapping,這個比較簡單,業內用的最多的就是Filmic Tone Mapping,效果是很不錯的,計算方式如下:

float3 F(float3 x) {const float A = 0.15f;const float B = 0.50f;const float C = 0.10f;const float D = 0.20f;const float E = 0.02f;const float F = 0.30f;return ((x * (A*x + C*B) + D*E) / (x * (A*x + B) + D*F)) - E/F; }float3 FilmicToneMapping(float3 color, float exposure) {const float WHITE = 11.2f;return F(exposure * color) / F(WHITE); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

好了,只剩下Bloom了。傳統的Bloom的流程是先利用場景的原始圖片作為輸入,提取出高光部分,然后做多次高斯模糊,再把模糊了的紋理疊加回去。這里有個示范:https://learnopengl.com/#!Advanced-Lighting/Bloom,這么做效果是很好的,不足之處就是性能影響有些大了,想要Bloom效果明顯,需要以不同的Kernel尺寸進行多次高斯模糊,這個過程非常非常耗性能。在VR里面每一點性能都不能浪費,那么有沒有效果不錯,性能也好的方案呢? 有的:《How to do good bloom for HDR rendering》一文中,作者提到在以5×5, 11×11, 21×21, 41×41為Kernel的高斯模糊之后,Bloom才有了一個不錯的效果,但是41*41的高斯模糊,想想都覺得可怕,于是提出了另一種方案,降低原圖的分辨率,降低一倍原圖分辨率就相當于Kernel加大一倍,由于分辨率降低,速度更是進一步提高,最后多張分辨率不一的圖片疊加起來,達到的效果跟在原圖上以不同Kernel模糊出來的效果是差不多的!

我這里以5*5為核心,sigma為4作為高斯模糊參數,對原始圖片的1/2長寬的level0, 1/4的level1, 1/8的level2,1/16的level3,四層圖片進行了模糊,然后用加法直接疊加在了原圖上,實現了Bloom效果。最后這一步我有了些改進,直接加上去,高光區域的顏色會過飽和,看起來油油的,而且會導致亮部細節丟失,白茫茫一片什么也看不清。于是我引入了一個機制,亮度越高的像素,加入的Bloom分量越少。最后完美解決了這個問題,計算過程如下:

// vec3 sceneColor:做過自動曝光和ToneMapping之后的像素顏色 // vec3 bloomColor : Bloom顏色 float linsetp(float _min, float _max, float v) {return clamp((v - _min) / (_max - _min), 0.0, 1.0); }vec3 LUMINANCE_VECTOR = vec3(0.2126, 0.7152 , 0.0722); float lum = dot(sceneColor, LUMINANCE_VECTOR); sceneColor += bloomColor * (1 - linstep(0.4, 0.8, lum));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

高斯模糊的代碼參見:《Fast and beautiful blur filter / shader recommendations?》中,JobLeonard 的回答,類似代碼很多,畢竟高斯模糊是很通用的算法了。

我機器是I7-6700+GTX960的顯卡,整個HDR過程耗時在1.6ms左右,場景的平均亮度我并不需要每幀都更新,每秒更新20次就足夠了,就算是10次也沒什么問題,這樣的話亮度計算耗時基本可以忽略,進一步提高了性能。


原圖↑


自動曝光+ToneMapping↑


HDR↑

從前往后數第二張桌子的Bloom效果最明顯了,有種在發光的感覺,其實黑色環境里的燈的Bloom效果是最明顯的,可惜外網就只有這個場景。

總結

以上是生活随笔為你收集整理的HDR (automatic exposure control + Tonemapping + Bloom)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。