這是 視覺專題 的第一篇,將分爲:git
這幾個小模塊來一一介紹,閱讀完本篇內容你將收穫:github
友情提示:該篇文字較多,比較適合對 OpenGL 知之甚少的同窗閱讀,已經有相關經驗的大佬能夠溜了😝,閒言少敘,直入正題。編程
注: 該專題如未特殊說明,默認使用核心模式,OpenGL 的版本在3.3以上便可。數組
通常它被認爲是一個API (Application Programming Interface, 應用程序編程接口),包含了一系列能夠操做圖形、圖像的函數(經過直接訪問圖形硬件設備的特性來實現)。事實上,OpenGL 自己並非一個API,它僅僅是一個由 Khronos 組織制定並維護的規範(Specification)。緩存
OpenGL 規範嚴格規定了每一個函數該如何執行,以及它們的輸出值。至於內部具體每一個函數是如何實現(Implement)的,將由 OpenGL 庫的開發者自行決定(這裏開發者一般是顯卡的生產商)。bash
由於 OpenGL 規範並無規定實現的細節,具體的 OpenGL 庫容許使用不一樣的實現,只要其功能和結果與規範相匹配便可。因此,當你使用 Apple 系統的時候,OpenGL 庫是由 Apple 自身維護的。在 Linux 下,有顯卡生產商提供的 OpenGL 庫,也有一些愛好者改編的版本。這也意味着任什麼時候候 OpenGL 庫表現的行爲與規範規定的不一致時,基本都是庫的開發者留下的bug。函數
OpenGL 是使用客戶端 - 服務端的形式實現的,咱們編寫的應用程序能夠看作客戶端,而計算機圖形硬件廠商所提供的 OpenGL 實現能夠看作服務端。咱們編寫的 OpenGL 命令,最終會被轉換爲相關的協議提交給服務端,而後被執行併產生圖像內容。post
OpenGL 庫中全部的函數都會以字符「gl」做爲前綴,而後是個或多個大寫字母開頭的詞組,以此來命名一個完成的函數(如 glBinVertexArray())。除此以外你還會看到「glfw」開頭的函數,它們來自第三方庫 GLFW,這是一個抽象化窗口管理和其餘系統任務的開發庫。相似的,還有「gl3w」開頭的函數,它們來自三方庫 GL3W。後續會進一步展開講這兩個庫的內容。學習
與函數命名約定相似,OpenGL 庫中定義的常量採用「GL_」開頭,經過#define
來完成常量的定義。爲了方便在不一樣的操做系統之間移植 OpenGL 程序,OpenGL 還爲函數定義了不一樣的數據類型,如GLfloat
,因此最好統一使用 OpenGL 定義的數據類型,這樣就不須要關心繫統兼容性問題了。動畫
因爲 OpenGL 是一個 C 語言形式的庫,所以它不能使用函數的重載來處理不一樣類型的數據,它經過函數名稱的細微變化來實現同一類功能函數集的管理。舉個例子:glUniform2f()
與glUniform3fv()
,前者的後綴2f
表示這個函數須要兩個GLfloat
類型的參數(以此類推,目前一共定義了24種不一樣的glUniform*
()函數),後者的後綴多出的一個v
是 vector 的縮寫,即表示它須要傳入一個包含三個 GLfloat
類型元素的一維數組做爲參數。
全部能夠做爲後綴的字母,以及它們所對應的數據類型:
早期的(3.3版本之前) OpenGL 使用當即渲染模式(Immediate mode,即固定渲染管線):OpenGL的大多數功能都被庫隱藏起來,開發者不多能控制 OpenGL 計算的過程。固定渲染管線較容易使用和理解,可是效率過低且不夠靈活。
當使用OpenGL的核心模式時,OpenGL 迫使咱們使用現代的函數,現代函數具備更高的靈活性和效率性,也能讓人更容易清楚 OpenGL 是如何運做的,更好的理解圖形編程。但入門門檻也稍有增長。
咱們一般所說的渲染管線(rendering pipeline),它包含了兩個部分:第一部分把你的3D座標轉換爲2D座標,第二部分是把2D座標轉變爲實際的有顏色的像素。下圖是 OpenGL4.5 版本的管線示意圖:
OpenGL 首先接收用戶提供的幾何數據(頂點和幾何圖元),而且將它輸入到一系列着色器階段中進行處理,而後將處理後的數據送入光柵化單元(rasterizer)。光柵化單元負責對全部剪切區域內的圖元生成片元數據,咱們能夠將一個片元視爲一個「候選的像素」,而後對每一個生成的片元都執行一個片元着色器,這一步會計算出這個片元的最終顏色。注意,即便在片元着色器中計算出來了一個像素輸出的顏色,在渲染多個三角形的時候最後的像素顏色也可能徹底不一樣,還會受 深度和 混合的影響,這個後面會詳細講。咱們能夠經過控制咱們須要的着色器來實現本身所需的功能,事實上,只有頂點着色器和片元着色器是必需的,細分和幾何是可選的步驟。爲了更好理解頂點着色器和片元着色器的分工和區別,能夠總結爲:頂點着色(包括細分和幾何着色)決定了一個圖元應該位於屏幕的什麼位置,而片元着色使用這些信息來決定某個片元的顏色應該是什麼。
不管 OpenGL 的程序寫的有多麼龐大與複雜,它的基本結構一般都是相似的:
在上代碼以前,我們須要對必要的圖形學名詞有基本的理解。
一個簡單頂點着色器的源代碼示例:
void main(){
gl_Position = ftransform();
}
複製代碼
一個簡單片元着色器的源代碼示例:
void main() {
gl_FragColor = vec4(1.0,0.5,0.2,1.0);
}
複製代碼
它們都須要用 GLSL (OpenGL Shading Language,着色器語言) 。
標準化設備座標(Normalized Device Coordinates, NDC):一旦你的頂點座標已經在頂點着色器中處理過,它們就應該是標準化設備座標了,標準化設備座標是一個x、y和z值在-1.0到1.0的一小段空間。任何落在範圍外的座標都會被丟棄/裁剪,不會顯示在你的屏幕上。
像素:顯示器上最小的可見單元。
幀緩存:保存着全部計算機生成的圖像的像素點,它是由圖形硬件設備管理的一塊獨立內存區域,能夠直接映射到最終的顯示設備上。
頂點緩衝對象:Vertex Buffer Object,VBO. 管理着在GPU內存(一般被稱爲顯存)中,一塊儲存着大量頂點數據的內存。由於從CPU把數據發送到顯卡相對較慢,因此只要可能咱們都要嘗試儘可能一次性發送儘量多的數據。
頂點數組對象:Vertex Array Object,VAO. 能夠像頂點緩衝對象那樣被綁定,任何隨後的頂點屬性調用都會儲存在這個 VAO 中。這樣的好處就是,當配置頂點屬性指針時,你只須要將那些調用執行一次,以後再繪製物體的時候只須要綁定相應的 VAO 就好了。這使在不一樣頂點數據和屬性配置之間切換變得很是簡單。
當你打算繪製多個物體時,你首先要生成/配置全部的VAO(和必須的VBO及屬性指針),而後儲存它們供後面使用。當咱們打算繪製物體的時候就拿出相應的VAO,綁定它,繪製完物體後,再解綁VAO。索引緩衝對象:Element Buffer Object,EBO或Index Buffer. Object,IBO. 專門儲存頂點繪製的索引,OpenGL調用這些頂點的索引來決定該繪製哪一個頂點。因爲三角形是繪製的基本圖形,對於一些複雜圖形會存在不少三角形(共用邊)的頂點重合問題,該索引就是爲了解決此問題,避免頂點數據重複形成的資源浪費。
我結合學習資源寫了一份簡單的 OpenGL 示例程序 給你做參考,附有比較詳細的渲染流程分析,須要的同窗自取,這裏就不佔篇幅粘代碼了。用 Xcode 打開程序直接 Run, 就能夠看到該效果:
這份 Demo 使用了可編程渲染管線,自定義了簡單的着色器,用兩個着色器程序、兩個VAO和VBO,畫兩個顏色不同的三角形。初學者不須要搞清楚每一步的原理,先對 OpenGL 的語法和基本結構有個初步的瞭解就行。枯燥的概念和晦澀的專業術語很容易讓初學者望而生畏,因此此篇我只選了一些必要的概念,作了簡要的介紹。建議結合文中提供的源碼,把上面涉及到的步驟一一拆解,進而加深對相關概念以及 OpenGL 工做流程的理解。
近來因工做和身體緣由,文章更新滯後了兩週有餘,現已恢復正常。
下一篇文章將使用固定管線來完成一些動畫效果,順帶介紹涉及到的 OpenGL 知識,這樣更便於理解。