亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

OpenGL核心技術之Gamma校正

2019-11-08 00:20:35
字體:
來源:轉載
供稿:網友

筆者介紹:姜雪偉,IT公司技術合伙人,IT高級講師,CSDN社區專家,特邀編輯,暢銷書作者,國家專利發明人;已出版書籍:《手把手教你架構3D游戲引擎》電子工業出版社和《Unity3D實戰核心技術詳解》電子工業出版社等。

CSDN視頻網址:http://edu.csdn.net/lecturer/144

先介紹一下關于Gamma產生的原因,原因有二:一是,人眼對自然亮度感知是非線性的(韋伯定律);二是,我們用來記錄/展示畫面的媒介上,灰階預算是有限的(無論紙張還是屏幕)。為了在灰階預算有限的前提下,協調自然亮度和主觀灰階感受這二者的映射關系,Gamma就產生了。

拿游戲舉例,游戲制作的場景最終要顯示在屏幕上,顯示器有一個物理特性就是兩倍的輸入電壓產生的不是兩倍的亮度。輸入電壓產生約為輸入電壓的2.2次冪的亮度,這叫做監視器Gamma。

Gamma也叫灰度系數,每種顯示設備都有自己的Gamma值,都不相同,有一個公式:設備輸出亮度 = 電壓的Gamma次冪,任何設備Gamma基本上都不會等于1,等于1是一種理想的線性狀態,這種理想狀態是:如果電壓和亮度都是在0到1的區間,那么多少電壓就等于多少亮度。對于CRT,Gamma通常為2.2,因而,輸出亮度 = 輸入電壓的2.2次冪,你可以從本節第二張圖中看到Gamma2.2實際顯示出來的總會比預期暗,相反Gamma0.45就會比理想預期亮,如果你講Gamma0.45疊加到Gamma2.2的顯示設備上,便會對偏暗的顯示效果做到校正,這個簡單的思路就是本節的核心。

人類所感知的亮度恰好和CRT所顯示出來相似的指數關系非常匹配。為了更好的理解所有含義,請看下面的圖片:

第一行是人眼所感知到的正常的灰階,亮度要增加一倍(比如從0.1到0.2)你才會感覺比原來變亮了一倍(注:這里的意思是說比如一個東西的亮度0.3,讓人感覺它比原來變亮一倍,那么現在這個亮度應該成為0.6,而不是0.4,也就是說人眼感知到的亮度的變化并非線性均勻分布的。問題的關鍵在于這樣的一倍相當于一個亮度級,例如假設0.1、0.2、0.4、0.8是我們定義的四個亮度級別,在0.1和0.2之間人眼只能識別出0.15這個中間級,而雖然0.4到0.8之間的差距更大,這個區間人眼也只能識別出一個顏色)。然而,當我們談論光的物理亮度,比如光源發射光子的數量的時候,底部(第二行)的灰階顯示出的才是物理世界真實的亮度。如底部的灰階顯示,亮度加倍時返回的也是真實的物理亮度(注:這里亮度是指光子數量和正相關的亮度,即物理亮度,前面討論的是人的感知亮度;物理亮度和感知亮度的區別在于,物理亮度基于光子數量,感知亮度基于人的感覺,比如第二個灰階里亮度0.1的光子數量是0.2的二分之一),但是由于這與我們的眼睛感知亮度不完全一致(對比較暗的顏色變化更敏感),所以它看起來有差異。

因為人眼看到顏色的亮度更傾向于頂部的灰階,監視器使用的也是一種指數關系(電壓的2.2次冪),所以物理亮度通過監視器能夠被映射到頂部的非線性亮度;因此看起來效果不錯(注:CRT亮度是是電壓的2.2次冪而人眼相當于2次冪,因此CRT這個缺陷正好能滿足人的需要)。

監視器的這個非線性映射的確可以讓亮度在我們眼中看起來更好,但當渲染圖像時,會產生一個問題:我們在應用中配置的亮度和顏色是基于監視器所看到的,這樣所有的配置實際上是非線性的亮度/顏色配置。請看下圖:

點線代表線性顏色/亮度值(注:這表示的是理想狀態,Gamma為1),實線代表監視器顯示的顏色。如果我們把一個點線線性的顏色翻一倍,結果就是這個值的兩倍。比如,光的顏色向量代表的是暗紅色。如果我們在線性空間中把它翻倍,就會變成,就像你在圖中看到的那樣。然而,由于我們定義的顏色仍然需要輸出的監視器上,監視器上顯示的實際顏色就會是。在這兒問題就出現了:當我們將理想中直線上的那個暗紅色翻一倍時,在監視器上實際上亮度翻了4.5倍以上!

直到現在,我們還一直假設我們所有的工作都是在線性空間中進行的(注:Gamma為1),但最終還是要把所喲的顏色輸出到監視器上,所以我們配置的所有顏色和光照變量從物理角度來看都是不正確的,在我們的監視器上很少能夠正確地顯示。出于這個原因,我們(以及藝術家)通常將光照值設置得比本來更亮一些(由于監視器會將其亮度顯示的更暗一些),如果不是這樣,在線性空間里計算出來的光照就會不正確。同時,還要記住,監視器所顯示出來的圖像和線性圖像的最小亮度是相同的,它們最大的亮度也是相同的;只是中間亮度部分會被壓暗。

因為所有中間亮度都是線性空間計算出來的(注:計算的時候假設Gamma為1)監視器顯以后,實際上都會不正確。當使用更高級的光照算法時,這個問題會變得越來越明顯,你可以看看下圖:

Gamma校正(Gamma Correction)的思路是在最終的顏色輸出上應用監視器Gamma的倒數?;仡^看前面的Gamma曲線圖,你會有一個短劃線,它是監視器Gamma曲線的翻轉曲線。我們在顏色顯示到監視器的時候把每個顏色輸出都加上這個翻轉的Gamma曲線,這樣應用了監視器Gamma以后最終的顏色將會變為線性的。我們所得到的中間色調就會更亮,所以雖然監視器使它們變暗,但是我們又將其平衡回來了。

我們來看另一個例子。還是那個暗紅色。在將顏色顯示到監視器之前,我們先對顏色應用Gamma校正曲線。線性的顏色顯示在監視器上相當于降低了次冪的亮度,所以倒數就是次冪。Gamma校正后的暗紅色就會成為。校正后的顏色接著被發送給監視器,最終顯示出來的顏色是。你會發現使用了Gamma校正,監視器最終會顯示出我們在應用中設置的那種線性的顏色。

2.2通常是是大多數顯示設備的大概平均gamma值?;趃amma2.2的顏色空間叫做sRGB顏色空間。每個監視器的gamma曲線都有所不同,但是gamma2.2在大多數監視器上表現都不錯。出于這個原因,游戲經常都會為玩家提供改變游戲gamma設置的選項,以適應每個監視器(譯注:現在Gamma2.2相當于一個標準,后文中你會看到。但現在你可能會問,前面不是說Gamma2.2看起來不是正好適合人眼么,為何還需要校正。這是因為你在程序中設置的顏色,比如光照都是基于線性Gamma,即Gamma1,所以你理想中的亮度和實際表達出的不一樣,如果要表達出你理想中的亮度就要對這個光照進行校正)。

有兩種在你的場景中應用gamma校正的方式:

使用OpenGL內建的sRGB幀緩沖。 自己在像素著色器中進行gamma校正。 第一個選項也許是最簡單的方式,但是我們也會喪失一些控制權。開啟GL_FRAMEBUFFER_SRGB,可以告訴OpenGL每個后續的繪制命令里,在顏色儲存到顏色緩沖之前先校正sRGB顏色。sRGB這個顏色空間大致對應于gamma2.2,它也是家用設備的一個標準。開啟GL_FRAMEBUFFER_SRGB以后,每次像素著色器運行后續幀緩沖,OpenGL將自動執行gamma校正,包括默認幀緩沖。

開啟GL_FRAMEBUFFER_SRGB簡單的調用glEnable就行:

glEnable(GL_FRAMEBUFFER_SRGB);

自此,你渲染的圖像就被進行gamma校正處理,你不需要做任何事情硬件就幫你處理了。有時候,你應該記得這個建議:gamma校正將把線性顏色空間轉變為非線性空間,所以在最后一步進行gamma校正是極其重要的。如果你在最后輸出之前就進行gamma校正,所有的后續操作都是在操作不正確的顏色值。例如,如果你使用多個怎還沖,你可能打算讓兩個幀緩沖之間傳遞的中間結果仍然保持線性空間顏色,只是給發送給監視器的最后的那個幀緩沖應用gamma校正。

第二個方法稍微復雜點,但同時也是我們對gamma操作有完全的控制權。我們在每個相關像素著色器運行的最后應用gamma校正,所以在發送到幀緩沖前,顏色就被校正了。

void main(){    // do super fancy lighting     [...]    // apply gamma correction    float gamma = 2.2;    fragColor.rgb = pow(fragColor.rgb, vec3(1.0/gamma));}

最后一行代碼,將fragColor的每個顏色元素應用有一個1.0/gamma的冪運算,校正像素著色器的顏色輸出。

這個方法有個問題就是為了保持一致,你必須在像素著色器里加上這個gamma校正,所以如果你有很多像素著色器,它們可能分別用于不同物體,那么你就必須在每個著色器里都加上gamma校正了。一個更簡單的方案是在你的渲染循環中引入后處理階段,在后處理四邊形上應用gamma校正,這樣你只要做一次就好了。

這些單行代碼代表了gamma校正的實現。不太令人印象深刻,但當你進行gamma校正的時候有一些額外的事情別忘了考慮。

因為監視器總是在sRGB空間中顯示應用了gamma的顏色,無論什么時候當你在計算機上繪制、編輯或者畫出一個圖片的時候,你所選的顏色都是根據你在監視器上看到的那種。這實際意味著所有你創建或編輯的圖片并不是在線性空間,而是在sRGB空間中(注:sRGB空間定義的gamma接近于2.2),假如在你的屏幕上對暗紅色翻一倍,便是根據你所感知到的亮度進行的,并不等于將紅色元素加倍。

結果就是紋理編輯者,所創建的所有紋理都是在sRGB空間中的紋理,所以如果我們在渲染應用中使用這些紋理,我們必須考慮到這點。在我們應用gamma校正之前,這不是個問題,因為紋理在sRGB空間創建和展示,同樣我們還是在sRGB空間中使用,從而不必gamma校正紋理顯示也沒問題。然而,現在我們是把所有東西都放在線性空間中展示的,紋理顏色就會變壞,如下圖展示的那樣:

紋理圖像實在太亮了,發生這種情況是因為,它們實際上進行了兩次gamma校正!想一想,當我們基于監視器上看到的情況創建一個圖像,我們就已經對顏色值進行了gamma校正,所以再次顯示在監視器上就沒錯。由于我們在渲染中又進行了一次gamma校正,圖片就實在太亮了。

為了修復這個問題,我們得確保紋理制作者是在線性空間中進行創作的。但是,由于大多數紋理制作者并不知道什么是gamma校正,并且在sRGB空間中進行創作更簡單,這也許不是一個好辦法。

另一個解決方案是重校,或把這些sRGB紋理在進行任何顏色值的計算前變回線性空間。我們可以這樣做:

float gamma = 2.2;vec3 diffuseColor = pow(texture(diffuse, texCoords).rgb, vec3(gamma));

為每個sRGB空間的紋理做這件事非常煩人。幸好,OpenGL給我們提供了另一個方案來解決我們的麻煩,這就是GL_SRGB和GL_SRGB_ALPHA內部紋理格式。

如果我們在OpenGL中創建了一個紋理,把它指定為以上兩種sRGB紋理格式其中之一,OpenGL將自動把顏色校正到線性空間中,這樣我們所使用的所有顏色值都是在線性空間中的了。我們可以這樣把一個紋理指定為一個sRGB紋理:

glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);

如果你還打算在你的紋理中引入alpha元素,必究必須將紋理的內部格式指定為GL_SRGB_ALPHA。

因為不是所有紋理都是在sRGB空間中的所以當你把紋理指定為sRGB紋理時要格外小心。比如diffuse紋理,這種為物體上色的紋理幾乎都是在sRGB空間中的。而為了獲取光照參數的紋理,像specular貼圖和法線貼圖幾乎都在線性空間中,所以如果你把它們也配置為sRGB紋理的話,光照就壞掉了。指定sRGB紋理時要當心。

將diffuse紋理定義為sRGB紋理之后,你將獲得你所期望的視覺輸出,但這次每個物體都會只進行一次gamma校正。

在使用了gamma校正之后,另一個不同之處是光照衰減(Attenuation)。真實的物理世界中,光照的衰減和光源的距離的平方成反比。

float attenuation = 1.0 / (distance * distance);	然而,當我們使用這個衰減公式的時候,衰減效果總是過于強烈,光只能照亮一小圈,看起來并不真實。出于這個原因,我們使用在基本光照教程中所討論的那種衰減方程,它給了我們更大的控制權,此外我們還可以使用雙曲線函數:

float attenuation = 1.0 / distance;雙曲線比使用二次函數變體在不用gamma校正的時候看起來更真實,不過但我們開啟gamma校正以后線性衰減看起來太弱了,符合物理的二次函數突然出現了更好的效果。下圖顯示了其中的不同:

這種差異產生的原因是,光的衰減方程改變了亮度值,而且屏幕上顯示出來的也不是線性空間,在監視器上效果最好的衰減方程,并不是符合物理的。想想平方衰減方程,如果我們使用這個方程,而且不進行gamma校正,顯示在監視器上的衰減方程實際上將變成如下所示:

若不進行gamma校正,將產生更強烈的衰減。這也解釋了為什么雙曲線不用gamma校正時看起來更真實,因為它實際變成了這和物理公式是很相似的。

總而言之,gamma校正使你可以在線性空間中進行操作。因為線性空間更符合物理世界,大多數物理公式現在都可以獲得較好效果,比如真實的光的衰減。你的光照越真實,使用gamma校正獲得漂亮的效果就越容易。

最后,把gamma校正的Shader源代碼奉上,先給讀者展示的是頂點著色器源代碼:

#version 330 corelayout (location = 0) in vec3 position;layout (location = 1) in vec3 normal;layout (location = 2) in vec2 texCoords;out VS_OUT {vec3 FragPos;vec3 Normal;vec2 TexCoords;} vs_out;uniform mat4 PRojection;uniform mat4 view;void main(){gl_Position = projection * view * vec4(position, 1.0f);vs_out.FragPos = position;vs_out.Normal = normal;vs_out.TexCoords = texCoords;}片段著色器源代碼如下所示:

#version 330 coreout vec4 FragColor;in VS_OUT {vec3 FragPos;vec3 Normal;vec2 TexCoords;} fs_in;uniform sampler2D floorTexture;uniform vec3 lightPositions[4];uniform vec3 lightColors[4];uniform vec3 viewPos;uniform bool gamma;vec3 BlinnPhong(vec3 normal, vec3 fragPos, vec3 lightPos, vec3 lightColor){// Diffusevec3 lightDir = normalize(lightPos - fragPos);float diff = max(dot(lightDir, normal), 0.0);vec3 diffuse = diff * lightColor;// Specularvec3 viewDir = normalize(viewPos - fragPos);vec3 reflectDir = reflect(-lightDir, normal);float spec = 0.0;vec3 halfwayDir = normalize(lightDir + viewDir);spec = pow(max(dot(normal, halfwayDir), 0.0), 64.0);vec3 specular = spec * lightColor;// Simple attenuationfloat max_distance = 1.5;float distance = length(lightPos - fragPos);float attenuation = 1.0 / (gamma ? distance * distance : distance);diffuse *= attenuation;specular *= attenuation;return diffuse + specular;}void main(){vec3 color = texture(floorTexture, fs_in.TexCoords).rgb;vec3 lighting = vec3(0.0);for(int i = 0; i < 4; ++i)lighting += BlinnPhong(normalize(fs_in.Normal), fs_in.FragPos, lightPositions[i], lightColors[i]);color *= lighting;if(gamma)color = pow(color, vec3(1.0/2.2));FragColor = vec4(color, 1.0f);}C++核心代碼如下所示:

// Clear the colorbufferglClearColor(0.1f, 0.1f, 0.1f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Draw objectsshader.Use();glm::mat4 view = camera.GetViewMatrix();glm::mat4 projection = glm::perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);glUniformMatrix4fv(glGetUniformLocation(shader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));glUniformMatrix4fv(glGetUniformLocation(shader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));// Set light uniformsglUniform3fv(glGetUniformLocation(shader.Program, "lightPositions"), 4, &lightPositions[0][0]);glUniform3fv(glGetUniformLocation(shader.Program, "lightColors"), 4, &lightColors[0][0]);glUniform3fv(glGetUniformLocation(shader.Program, "viewPos"), 1, &camera.Position[0]);glUniform1i(glGetUniformLocation(shader.Program, "gamma"), gammaEnabled);// FloorglBindVertexArray(planeVAO);glBindTexture(GL_TEXTURE_2D, gammaEnabled ? floorTextureGammaCorrected : floorTexture);glDrawArrays(GL_TRIANGLES, 0, 6);glBindVertexArray(0);std::cout << (gammaEnabled ? "Gamma enabled" : "Gamma disabled") << std::endl;// Swap the buffersglfwSwapBuffers(window);


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲精品电影网在线观看| 在线国产精品播放| 久久久精品久久久| 亚洲欧美中文另类| 色视频www在线播放国产成人| 欧美激情三级免费| 亚洲精品白浆高清久久久久久| 岛国精品视频在线播放| 美女久久久久久久| 欧美激情在线狂野欧美精品| 久久亚洲国产成人| 成人有码在线视频| 日韩视频免费观看| 日韩欧美精品中文字幕| 国产免费一区二区三区香蕉精| 亚洲精品日韩av| 亚洲美女福利视频网站| 日韩av一区在线| 久久亚洲影音av资源网| 欧美精品在线观看| 97视频com| 亚洲日韩中文字幕在线播放| 精品福利视频导航| 97热在线精品视频在线观看| 亚洲精品资源在线| 欧美大片在线看免费观看| 中文字幕精品影院| 95av在线视频| 亚洲性生活视频| 91九色蝌蚪国产| 动漫精品一区二区| 4388成人网| 欧美极品美女视频网站在线观看免费| 欧美亚洲免费电影| 91精品国产综合久久香蕉| 夜夜躁日日躁狠狠久久88av| 国产精品视频精品视频| 国产精品久久电影观看| 久久男人资源视频| 97精品久久久中文字幕免费| 777午夜精品福利在线观看| 欧美在线视频一区二区| www高清在线视频日韩欧美| 91久久国产婷婷一区二区| 日韩精品在线视频观看| 精品爽片免费看久久| 日韩成人在线电影网| 亚洲成人av在线| 韩国美女主播一区| 在线视频国产日韩| 深夜成人在线观看| 久久视频国产精品免费视频在线| 亚洲精品一区二区网址| 欧美激情精品久久久久久黑人| 日韩欧美一区视频| 九九热这里只有在线精品视| 国产精品久久久久久久天堂| 91精品视频观看| 国产网站欧美日韩免费精品在线观看| 精品国内亚洲在观看18黄| 欧美整片在线观看| 久久亚洲综合国产精品99麻豆精品福利| 欧美激情一区二区三区在线视频观看| 久久国产精品偷| 久久国产精品久久精品| 国产一区av在线| 午夜欧美不卡精品aaaaa| 欧美成人午夜激情| 97精品国产97久久久久久免费| 日韩成人在线免费观看| 91嫩草在线视频| 国产精品电影网站| 国产精品久久久久久久一区探花| 成人羞羞国产免费| 97久久久免费福利网址| 久精品免费视频| 久久亚洲电影天堂| 精品国产欧美一区二区三区成人| 日韩成人免费视频| 欧美激情中文字幕乱码免费| 国产精品久久999| 亚洲色图激情小说| 久久久精品影院| 成人久久18免费网站图片| 91影院在线免费观看视频| 成人免费福利在线| 成人黄色在线观看| 欧美性一区二区三区| 91免费国产网站| 欧美在线中文字幕| 久久久久久久影院| 成人写真福利网| 欧美另类在线观看| 精品久久在线播放| 成人啪啪免费看| 成人网在线视频| 日韩美女av在线| 国产精品久久久久久亚洲调教| 高清欧美一区二区三区| 136fldh精品导航福利| 欧美精品福利视频| 国产成人av在线播放| 久久精品视频在线| 亚洲福利视频专区| 久久这里只有精品99| 蜜臀久久99精品久久久久久宅男| 日韩精品免费在线播放| 亚洲a在线播放| 在线电影欧美日韩一区二区私密| 98精品国产高清在线xxxx天堂| 国产美女久久精品香蕉69| 97人人做人人爱| 日韩视频在线观看免费| 日韩在线中文字幕| 九九热精品在线| 亚洲人成在线免费观看| 亚洲影院高清在线| 国产精品久久久久久久久久久新郎| 亚洲人成电影网站色www| 狠狠色狠狠色综合日日五| 国产精品视频自拍| 亚洲偷欧美偷国内偷| 欧美在线视频导航| 欧洲午夜精品久久久| 精品国产视频在线| 日韩有码在线播放| 国产精品视频不卡| 97涩涩爰在线观看亚洲| 一区二区在线视频| 日韩视频在线免费| 色综合导航网站| 日韩av片免费在线观看| 欧美中在线观看| 亚洲情综合五月天| 欧美精品国产精品日韩精品| 亚洲精品日韩激情在线电影| 97在线精品国自产拍中文| 一区二区成人av| 不用播放器成人网| 亚洲www在线观看| 中文字幕在线视频日韩| 亚洲电影在线看| 日韩欧美亚洲综合| 久久久久久国产免费| 色一情一乱一区二区| 日韩欧美综合在线视频| 国产日本欧美在线观看| 992tv成人免费视频| zzijzzij亚洲日本成熟少妇| 亚洲精品456在线播放狼人| 国内精品免费午夜毛片| 亚洲成人免费在线视频| 日韩av网址在线观看| 国产在线98福利播放视频| 97视频在线观看网址| 亚洲伊人成综合成人网| 欧美亚洲一区在线| 91免费电影网站| 亚洲美女精品久久| 岛国视频午夜一区免费在线观看| 日本精品视频在线| 日韩av手机在线| 91探花福利精品国产自产在线| 亚洲第一偷拍网|