技術文章您現在的位置:首頁 > 技術文章 > HDR技術詳解
HDR技術詳解
更新時間:2021-10-18   點擊次數:1363次

OpenGL核心技術之HDR

一般來說,當存儲(chu) 在幀緩衝(chong) (Framebuffer)中時,亮度和顏色的值是默認被限製在0.0到1.0之間的。這個(ge) 看起來無辜的語句使我們(men) 一直將亮度與(yu) 顏色的值設置在這個(ge) 範圍內(nei) ,嚐試著與(yu) 場景契合。這樣是能夠運行的,也能給出還不錯的效果。但是如果我們(men) 遇上了一個(ge) 特定的區域,其中有多個(ge) 亮光源使這些數值總和超過了1.0,又會(hui) 發生什麽(me) 呢?答案是這些片段中超過1.0的亮度或者顏色值會(hui) 被約束在1.0,從(cong) 而導致場景混成一片,難以分辨:

 

這是由於(yu) 大量片段的顏色值都非常接近1.0,在很大一個(ge) 區域內(nei) 每一個(ge) 亮的片段都有相同的白色。這損失了很多的細節,使場景看起來非常假。

解決(jue) 這個(ge) 問題的一個(ge) 方案是減小光源的強度從(cong) 而保證場景內(nei) 沒有一個(ge) 片段亮於(yu) 1.0。然而這並不是一個(ge) 好的方案,因為(wei) 你需要使用不切實際的光照參數。一個(ge) 更好的方案是讓顏色暫時超過1.0,然後將其轉換至0.0到1.0的區間內(nei) ,從(cong) 而防止損失細節。

顯示器被限製為(wei) 隻能顯示值為(wei) 0.0到1.0間的顏色,但是在光照方程中卻沒有這個(ge) 限製。通過使片段的顏色超過1.0,我們(men) 有了一個(ge) 更大的顏色範圍,這也被稱作HDR(High Dynamic Range, 高動態範圍)。有了HDR,亮的東(dong) 西可以變得非常亮,暗的東(dong) 西可以變得非常暗,而且充滿細節。

HDR原本隻是被運用在攝影上,攝影師對同一個(ge) 場景采取不同曝光拍多張照片,捕捉大範圍的色彩值。這些圖片被合成為(wei) HDR圖片,從(cong) 而綜合不同的曝光等級使得大範圍的細節可見。看下麵這個(ge) 例子,左邊這張圖片在被光照亮的區域充滿細節,但是在黑暗的區域就什麽(me) 都看不見了;但是右邊這張圖的高曝光卻可以讓之前看不出來的黑暗區域顯現出來。

 

這與(yu) 我們(men) 眼睛工作的原理非常相似,也是HDR渲染的基礎。當光線很弱的啥時候,人眼會(hui) 自動調整從(cong) 而使過暗和過亮的部分變得更清晰,就像人眼有一個(ge) 能自動根據場景亮度調整的自動曝光滑塊。

HDR渲染和其很相似,我們(men) 允許用更大範圍的顏色值渲染從(cong) 而獲取大範圍的黑暗與(yu) 明亮的場景細節,zui後將所有HDR值轉換成在[0.0, 1.0]範圍的LDR(Low Dynamic Range,低動態範圍)。轉換HDR值到LDR值得過程叫做色調映射(Tone Mapping),現在現存有很多的色調映射算法,這些算法致力於(yu) 在轉換過程中保留盡可能多的HDR細節。這些色調映射算法經常會(hui) 包含一個(ge) 選擇性傾(qing) 向黑暗或者明亮區域的參數。

在實時渲染中,HDR不僅(jin) 允許我們(men) 超過LDR的範圍[0.0, 1.0]與(yu) 保留更多的細節,同時還讓我們(men) 能夠根據光源的真實強度它的強度。比如太陽有比閃光燈之類的東(dong) 西更高的強度,那麽(me) 我們(men) 為(wei) 什麽(me) 不這樣子設置呢?(比如說設置一個(ge) 10.0的漫亮度) 這允許我們(men) 用更現實的光照參數恰當地配置一個(ge) 場景的光照,而這在LDR渲染中是不能實現的,因為(wei) 他們(men) 會(hui) 被上限約束在1.0。

因為(wei) 顯示器隻能顯示在0.0到1.0範圍之內(nei) 的顏色,我們(men) 肯定要做一些轉換從(cong) 而使得當前的HDR顏色值符合顯示器的範圍。簡單地取平均值重新轉換這些顏色值並不能很好的解決(jue) 這個(ge) 問題,因為(wei) 明亮的地方會(hui) 顯得更加顯著。我們(men) 能做的是用一個(ge) 不同的方程與(yu) /或曲線來轉換這些HDR值到LDR值,從(cong) 而給我們(men) 對於(yu) 場景的亮度*掌控,這就是之前說的色調變換,也是HDR渲染的zui終步驟。

在實現HDR渲染之前,我們(men) 首先需要一些防止顏色值在每一個(ge) 片段著色器運行後被限製約束的方法。當幀緩衝(chong) 使用了一個(ge) 標準化的定點格式(像GL_RGB)為(wei) 其顏色緩衝(chong) 的內(nei) 部格式,OpenGL會(hui) 在將這些值存入幀緩衝(chong) 前自動將其約束到0.0到1.0之間。這一操作對大部分幀緩衝(chong) 格式都是成立的,除了專(zhuan) 門用來存放被拓展範圍值的浮點格式。

當一個(ge) 幀緩衝(chong) 的顏色緩衝(chong) 的內(nei) 部格式被設定成了GL_RGB16F, GL_RGBA16F, GL_RGB32F 或者GL_RGBA32F時,這些幀緩衝(chong) 被叫做浮點幀緩衝(chong) (Floating Point Framebuffer),浮點幀緩衝(chong) 可以存儲(chu) 超過0.0到1.0範圍的浮點值,所以非常適合HDR渲染。

想要創建一個(ge) 浮點幀緩衝(chong) ,我們(men) 隻需要改變顏色緩衝(chong) 的內(nei) 部格式參數就行了(注意GL_FLOAT參數):

[cpp] view plain copy

  1. glBindTexture(GL_TEXTURE_2D, colorBuffer);  
  2. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_FLOAT, NULL);    

 

默認的幀緩衝(chong) 默認一個(ge) 顏色分量隻占用8位(bits)。當使用一個(ge) 使用32位每顏色分量的浮點幀緩衝(chong) 時(使用GL_RGB32F 或者GL_RGBA32F),我們(men) 需要四倍的內(nei) 存來存儲(chu) 這些顏色。所以除非你需要一個(ge) 非常高的度,32位不是必須的,使用GLRGB16F就足夠了。

有了一個(ge) 帶有浮點顏色緩衝(chong) 的幀緩衝(chong) ,我們(men) 可以放心渲染場景到這個(ge) 幀緩衝(chong) 中。在這個(ge) 教程的例子當中,我們(men) 先渲染一個(ge) 光照的場景到浮點幀緩衝(chong) 中,之後再在一個(ge) 鋪屏四邊形(Screen-filling Quad)上應用這個(ge) 幀緩衝(chong) 的顏色緩衝(chong) ,代碼會(hui) 是這樣子:

[cpp] view plain copy

  1. glBindFramebuffer(GL_FRAMEBUFFER, hdrFBO);  
  2.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    
  3.     // [...] 渲染(光照的)場景  
  4. glBindFramebuffer(GL_FRAMEBUFFER, 0);  
  5.   
  6. // 現在使用一個不同的著色器將HDR顏色緩衝渲染至2D鋪屏四邊形上  
  7. hdrShader.Use();  
  8. glActiveTexture(GL_TEXTURE0);  
  9. glBindTexture(GL_TEXTURE_2D, hdrColorBufferTexture);  
  10. RenderQuad();  


這裏場景的顏色值存在一個(ge) 可以包含任意顏色值的浮點顏色緩衝(chong) 中,值可能是超過1.0的。這個(ge) 簡單的演示中,場景被創建為(wei) 一個(ge) 被拉伸的立方體(ti) 通道和四個(ge) 點光源,其中一個(ge) 非常亮的在隧道的盡頭:

 

[cpp] view plain copy

  1. std::vector<glm::vec3> lightColors;  
  2. lightColors.push_back(glm::vec3(200.0f, 200.0f, 200.0f));  
  3. lightColors.push_back(glm::vec3(0.1f, 0.0f, 0.0f));  
  4. lightColors.push_back(glm::vec3(0.0f, 0.0f, 0.2f));  
  5. lightColors.push_back(glm::vec3(0.0f, 0.1f, 0.0f));    

渲染至浮點幀緩衝(chong) 和渲染至一個(ge) 普通的幀緩衝(chong) 是一樣的。新的東(dong) 西就是這個(ge) 的hdrShader的片段著色器,用來渲染zui終擁有浮點顏色緩衝(chong) 紋理的2D四邊形。我們(men) 來定義(yi) 一個(ge) 簡單的直通片段著色器(Pass-through Fragment Shader):

 

 

[cpp] view plain copy

  1. #version 330 core  
  2. out vec4 color;  
  3. in vec2 TexCoords;  
  4.   
  5. uniform sampler2D hdrBuffer;  
  6.   
  7. void main()  
  8. {               
  9.     vec3 hdrColor = texture(hdrBuffer, TexCoords).rgb;  
  10.     color = vec4(hdrColor, 1.0);  
  11. }    


這裏我們(men) 直接采樣了浮點顏色緩衝(chong) 並將其作為(wei) 片段著色器的輸出。然而,這個(ge) 2D四邊形的輸出是被直接渲染到默認的幀緩衝(chong) 中,導致所有片段著色器的輸出值被約束在0.0到1.0間,盡管我們(men) 已經有了一些存在浮點顏色紋理的值超過了1.0。

 

很明顯,在隧道盡頭的強光的值被約束在1.0,因為(wei) 一大塊區域都是白色的,過程中超過1.0的地方損失了所有細節。因為(wei) 我們(men) 直接轉換HDR值到LDR值,這就像我們(men) 根本就沒有應用HDR一樣。為(wei) 了修複這個(ge) 問題我們(men) 需要做的是無損轉化所有浮點顏色值回0.0-1.0範圍中。我們(men) 需要應用到色調映射。

 

色調映射(Tone Mapping)是一個(ge) 損失很小的轉換浮點顏色值至我們(men) 所需的LDR[0.0, 1.0]範圍內(nei) 的過程,通常會(hui) 伴有特定的風格的色平衡(Stylistic Color Balance)。

zui簡單的色調映射算法是Reinhard色調映射,它涉及到分散整個(ge) HDR顏色值到LDR顏色值上,所有的值都有對應。Reinhard色調映射算法平均得將所有亮度值分散到LDR上。我們(men) 將Reinhard色調映射應用到之前的片段著色器上,並且為(wei) 了更好的測量加上一個(ge) Gamma校正過濾(包括SRGB紋理的使用):

[cpp] view plain copy

  1. void main()  
  2. {               
  3.     const float gamma = 2.2;  
  4.     vec3 hdrColor = texture(hdrBuffer, TexCoords).rgb;  
  5.   
  6.     // Reinhard色調映射  
  7.     vec3 mapped = hdrColor / (hdrColor + vec3(1.0));  
  8.     // Gamma校正  
  9.     mapped = pow(mapped, vec3(1.0 / gamma));  
  10.   
  11.     color = vec4(mapped, 1.0);  
  12. }     

有了Reinhard色調映射的應用,我們(men) 不再會(hui) 在場景明亮的地方損失細節。當然,這個(ge) 算法是傾(qing) 向明亮的區域的,暗的區域會(hui) 不那麽(me) 精細也不那麽(me) 有區分度。


 

現在你可以看到在隧道的盡頭木頭紋理變得可見了。用了這個(ge) 非常簡單地色調映射算法,我們(men) 可以合適的看到存在浮點幀緩衝(chong) 中整個(ge) 範圍的HDR值,給我們(men) 對於(yu) 無損場景光照的控製。

另一個(ge) 有趣的色調映射應用是曝光(Exposure)參數的使用。你可能還記得之前我們(men) 在介紹裏講到的,HDR圖片包含在不同曝光等級的細節。如果我們(men) 有一個(ge) 場景要展現日夜交替,我們(men) 當然會(hui) 在白天使用低曝光,在夜間使用高曝光,就像人眼調節方式一樣。有了這個(ge) 曝光參數,我們(men) 可以去設置可以同時在白天和夜晚不同光照條件工作的光照參數,我們(men) 隻需要調整曝光參數就行了。

一個(ge) 簡單的曝光色調映射算法會(hui) 像這樣:

[cpp] view plain copy

  1. uniform float exposure;  
  2.   
  3. void main()  
  4. {               
  5.     const float gamma = 2.2;  
  6.     vec3 hdrColor = texture(hdrBuffer, TexCoords).rgb;  
  7.   
  8.     // 曝光色調映射  
  9.     vec3 mapped = vec3(1.0) - exp(-hdrColor * exposure);  
  10.     // Gamma校正   
  11.     mapped = pow(mapped, vec3(1.0 / gamma));  
  12.   
  13.     color = vec4(mapped, 1.0);  
  14. }    

 

在這裏我們(men) 將exposure定義(yi) 為(wei) 默認為(wei) 1.0的uniform,從(cong) 而允許我們(men) 更加設定我們(men) 是要注重黑暗還是明亮的區域的HDR顏色值。舉(ju) 例來說,高曝光值會(hui) 使隧道的黑暗部分顯示更多的細節,然而低曝光值會(hui) 顯著減少黑暗區域的細節,但允許我們(men) 看到更多明亮區域的細節。下麵這組圖片展示了在不同曝光值下的通道:

這個(ge) 圖片清晰地展示了HDR渲染的優(you) 點。通過改變曝光等級,我們(men) 可以看見場景的很多細節,而這些細節可能在LDR渲染中都被丟(diu) 失了。比如說隧道盡頭,在正常曝光下木頭結構隱約可見,但用低曝光木頭的花紋就可以清晰看見了。對於(yu) 近處的木頭花紋來說,在高曝光下會(hui) 能更好的看見。

zui後把實現的源代碼給讀者展示如下,首先展示的是頂點著色器代碼:

 

[cpp] view plain copy

  1. #version 330 core  
  2. layout (location = 0) in vec3 position;  
  3. layout (location = 1) in vec2 texCoords;  
  4.   
  5. out vec2 TexCoords;  
  6.   
  7. void main()  
  8. {  
  9.     gl_Position = vec4(position, 1.0f);  
  10.     TexCoords = texCoords;  
  11. }  


片段著色器代碼如下所示:

 

 

[cpp] view plain copy

  1. #version 330 core  
  2. out vec4 color;  
  3. in vec2 TexCoords;  
  4.   
  5. uniform sampler2D hdrBuffer;  
  6. uniform float exposure;  
  7. uniform bool hdr;  
  8.   
  9. void main()  
  10. {               
  11.     const float gamma = 2.2;  
  12.     vec3 hdrColor = texture(hdrBuffer, TexCoords).rgb;  
  13.   
  14.     // reinhard  
  15.     // vec3 result = hdrColor / (hdrColor + vec3(1.0));  
  16.     // exposure  
  17.     vec3 result = vec3(1.0) - exp(-hdrColor * exposure);  
  18.     // also gamma correct while we're at it         
  19.     result = pow(result, vec3(1.0 / gamma));  
  20.     color = vec4(result, 1.0f);  
  21. }  

 

 

在這裏展示的兩(liang) 個(ge) 色調映射算法僅(jin) 僅(jin) 是大量(更先進)的色調映射算法中的一小部分,這些算法各有長短.一些色調映射算法傾(qing) 向於(yu) 特定的某種顏色/強度,也有一些算法同時顯示低於(yu) 高曝光顏色從(cong) 而能夠顯示更加多彩和精細的圖像。也有一些技巧被稱作自動曝光調整(Automatic Exposure Adjustment)或者叫人眼適應(Eye Adaptation)技術,它能夠檢測前一幀場景的亮度並且緩慢調整曝光參數模仿人眼使得場景在黑暗區域逐漸變亮或者在明亮區域逐漸變暗,

HDR渲染的真正優(you) 點在龐大和複雜的場景中應用複雜光照算法會(hui) 被顯示出來,但是出於(yu) 教學目的創建這樣複雜的演示場景是很困難的,這個(ge) 教程用的場景是很小的,而且缺乏細節。但是如此簡單的演示也是能夠顯示出HDR渲染的一些優(you) 點:在明亮和黑暗區域無細節損失,因為(wei) 它們(men) 可以由色調映射重新獲取;多個(ge) 光照的疊加不會(hui) 導致亮度被約束的區域;光照可以被設定為(wei) 他們(men) 原來的亮度而不是被LDR值限定。而且,HDR渲染也使一些有趣的效果更加可行和真實; 其中一個(ge) 效果叫做泛光(Bloom)

     “文章為(wei) 轉載,如有版權爭(zheng) 議請管理員,我們(men) 將刪除文章!”

betway官网app

betway官网app

地址:深圳市龍華新區上油鬆尚遊公館1821-1822

主營產(chan) 品:模擬製式信號發生器TG39BX(54200),DVB-T2數字信號發生器SFU(MSD5000A),CA-410(CA310)色彩分析儀(yi)

版權所有:betway官网app  備案號:  總訪問量:316084  站點地圖  技術支持: