1. 程式人生 > >opengl渲染管線 不能再詳細了

opengl渲染管線 不能再詳細了

http://www.cnblogs.com/liangliangh/p/4116164.html

轉載自上面的網址,總有可以令人膜拜的文章

還有一個綜合的網址

http://www.programgo.com/tag/opengl/19551513/5/

自頂向下的思路來簡單總結OpenGL圖形管線,即從最高層開始,然後逐步細化到管線圖中的每個框,再進一步細化到OpenGL具體函式。注意,這裡用經典管線代說著色器內部,也就是OpenGL固定管線功能(Fixed-Function,相對於programmable也即可程式設計著色器),也會涉及著色器,但差不多僅限於“這些固定管線功能對應xx著色器”。以後有機會會單獨說著色器。

有人可能會說,固定管線功能在很多年前就過時啦,而且在最新的OpenGL標準裡都不支援了,不是相容支援,而是徹底刪除了。我是這麼認為的,首先,雖然OpenGL最新標準(4.0或更高)明確刪除了固定管線功能,但顯示卡廠商還在提供固定管線的驅動程式支援,因為還有很多人在用這些固定管線功能;其次,OpenGL本身僅是一個工具,在這個工具上設計巧妙的演算法,或者實現需要的效果才是重點,如果用固定管線就能實現這些,也就不必要用著色器,因為即使用著色器也可能只是在實現固定管線功能而已;再次,對於學習著色器來說,瞭解固定管線是一個很好的案例學習,我想好多學習著色器的人都是這麼開始的吧:用頂點著色器實現固定管線的變換功能,固定管線作為最通用功能的實現,必然經過了頂尖工程師的精心設計,很值得參考。

0.文獻綜述

鑑於OpenGL從標準4.0開始徹底刪除了固定管線部分,本文主要參考了OpenGL 3.x的最高版本:“OpenGL 3.3 Specification (Compatibility rofile) - March 11, 2010”,對照3系列裡最高版本的快速參考卡:“OpenGL 3.2 API Quick Reference Card”,請見參考文獻[1][2]。也少量參考了最新標準OpenGL 4.5,參見文獻[3][4]。

文獻[5][6]是世界頂尖大學的圖形學課程,都提供PPT下載。文獻[7]紅寶書其實不推薦看,因為它充斥著程式設計細節,對於程式設計參考可以,用來了解OpenGL管線有些難。

用Google圖片搜尋“OpenGL Pipeline”,搜到了好多很好的圖,追蹤到圖片原網站,又發現了好多資源。文獻[8] Lighthou3D 網站的教程不錯,其中的 GLSL Core Tutorial 和 GLSL 1.2 Tutorial 講了圖形管線。文獻[9],《OpenGL Insights》,就衝著那些管線圖想必也很值得看。

文獻[10][11]是CUDA的參考文獻,本文用CUDA作為例子來說GPU的大致程式設計模型。

最後別忘了OpenGL官方Wiki的OpenGL Pipeline,文獻[12]。

1.圖形硬體系統

大家都知道程式的主函式都在CPU上執行,圖形的渲染在GPU上執行,GPU亦可進行通用程式設計,但這樣的程式也需要在CPU上執行程式碼來操控GPU。現代計算機的硬體結構如下圖(摘自文獻[6]):

這個圖稍微有點過時(不過和我現在用的桌上型電腦基本吻合,哈哈),將各個資料傳輸頻寬值增加一倍基本就是目前最好PC的水平,不要驚訝於視訊記憶體的頻寬竟然是記憶體的5倍以上,因為視訊記憶體的位寬要大,而且視訊記憶體直接焊接在顯示卡上不像記憶體條有插槽,所以頻率也可以高一些,但視訊記憶體的延時一般不如記憶體低。PCIe的頻寬大約是記憶體速度的三分之一。這個層次上效能優化的主要思路是:減少程式對PCI傳輸頻寬的佔用,增加主程式(CPU)以及著色器(GPU)訪問儲存器的區域性性(增加快取命中率或更多使用暫存器)。提醒一點,GDDR5對應CPU記憶體的DDR3,GDDR3對應CPU記憶體的DDR2。

著色器程式在GPU上執行,OpenGL主程式在CPU上執行,主程式(CPU)向視訊記憶體輸入頂點等資料,啟動渲染過程,並對渲染過程進行控制。瞭解到這一點就可以瞭解顯示列表(Display Lists)以及像 glFinish() 這種函式存在的原因了,前者(顯示列表)將一組繪製指令放到GPU上,CPU只要發一條“執行這個顯示列表”這些指令就執行,而不必CPU每次渲染都發送大量指令到GPU,從而節約PCI頻寬(因為PCI匯流排比視訊記憶體慢);後者(glFinish)讓CPU等待GPU將已傳送的渲染指令執行完。

下面來看GPU硬體給OpenGL提供了怎樣的執行模型,這裡採用Nvidia的術語,不過OpenCL的術語和Nvidia的術語有很好對應關係,都差不多。GPU提供大規模並行機制,特別適合於執行高度並行的渲染過程,這個“並行”的概念可能要超出我們平常在CPU上開的幾十個執行緒,GPU的執行緒數可以達到上百萬個或更多(每個執行緒可以對應於每個頂點、圖元、片斷的處理過程)。如何執行如此多的執行緒呢,請看下圖CUDA程式執行模型(摘自這裡,和文獻[10]PTX ISA):

Host和Device分別表示CPU和GPU的程式設計檢視,基本思路是將執行緒按兩個層次分組,多個執行緒(Thread)組成Block(最多三維索引,目前最新硬體限制Block中執行緒總數不多於1024個),多個Block組成Grid(最多三維索引,目前限制x維度最多231-1個,yz維度216-1個),再來看看Grid的詳細情況,即儲存模型(摘自這裡,和文獻[10]PTX ISA):

關鍵點是Block內提供了共享儲存(Shared Memory),為什麼說這點關鍵呢,因為多個執行緒要相互通訊,共享儲存模型是最方便快速的通訊方法,但對眾多的執行緒全都提供共享儲存模型會影響效率(併發訪問儲存器,執行緒有上百萬個之多),CUDA(OpenCL也是類似的)採用一種折衷方式:提供有限的共享儲存程式設計,Block內提供高速共享儲存,而Block間的通過全域性儲存(視訊記憶體)的通訊要慢的多。

這種程式設計模型和GPU硬體模型是相對應的,來看(摘自這裡,和文獻[10]PTX ISA):

GPU主要由視訊記憶體(Device Memory)和SMs(流多處理器,Stream Multiprocessors)組成,目前最新的顯示卡(Compute Capability 5.x),一個SM由128個CUDA核心(Processor)組成,顯示卡的好壞基本就取決於有多少個SM了(小米平板有1個SM,Compute Capability 3.x,那時一個SM有192個CUDA核心)。

上述程式設計模型和GPU模型有對應關係:Block總是在一個SM上執行,Block內部的共享儲存模型由SM硬體的共享儲存器提供。執行緒層次上效能優化的主要思路是:儘量使 Kernel 程式碼(每個執行緒,尤其是同一個 Block 內的執行緒)具有相同的執行路徑(即分支跳轉情況儘量相同),以充分利用GPU訪存及程式碼執行方面的並行機制。

覺得各種模型有點虛,那來看CUDA程式(截圖自文獻[10]):

程式中的 numBlocks 和 thredsPerBloack 即,Grid中有多少Block 和 一個Block中有多少執行緒,numBlocks和thredsPerBloack最多可以由三個數xyz構成,表示三個維度的長度,可以看到因為一個執行緒可能要被執行上百萬次,但執行緒絕不可能做重複工作,它們根據自己的ID處理資料的不同部分。

回到OpenGL,OpenGL也定義的自己的執行模型(用 Compute Shader 進行通用計算),和CUDA執行模型非常類似(摘自文獻[4],該圖被稍作調整):

在OpenGL概念中,CUDA的Grid變成了Dispatch,Block變成了Work Group,Thread變成了Invocation,同樣,Dispatch可以由三維索引的Work Group組成,Work Group可以由三維索引的Invocation組成。

在說OpenGL具體管線之前,先說一下OpenGL Context(上下文)。在呼叫任何OpenGL函式之前,必須已經建立了GL Context,GL Context儲存了OpenGL的狀態變數以及其他渲染有關的資訊。我們都知道OpenGL是個狀態機,有很多狀態變數,是個標準的過程式操作過程,改變狀態會影響後續所有操作,這和麵向物件的解耦原則不符,畢竟渲染本身就是個複雜的過程。OpenGL採用Client-Server模型來解釋OpenGL程式,即Server儲存GL Context(可能不止一個),Client提出渲染請求,Server給予響應,一般Server和Client都在我們的PC上,但Server和Client也可以是通過網路連線(即將上面說的PCIe匯流排換成了網路)。參見文獻[1] 2.1 OpenGL Fundamentals。

建立GL Context一般和建立視窗一起進行,視窗有與之相聯絡的Default Frame Buffer(區別於Framebuffer Objects,前者由外界建立OpenGL隨後操作並只有一個,是GL Context的一部分,後者由OpenGL建立可以多個),OpenGL通過Default Frame Buffer將渲染內容顯示在螢幕上。我們平常用GLFW或者GLUT建立視窗一般就已經建立了GL Context,GLFW建立GL Context並進入渲染迴圈的程式碼如下(摘自這裡):

複製程式碼
GLFWwindow* window;
/* Initialize the library */
if (!glfwInit()) return -1;
/* Create a windowed mode window and its OpenGL context */
window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
if (!window){
    glfwTerminate();
    return -1;
}
/* Make the window's context current */
glfwMakeContextCurrent(window);
/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
    /* Render here */

    /* Swap front and back buffers */
    glfwSwapBuffers(window);
    /* Poll for and process events */
    glfwPollEvents();
}
複製程式碼

這裡的雙緩衝是一種常用的防止畫面撕裂的技術,即呼叫OpenGL函式進行渲染的結果都寫入“back” buffer,待所有渲染完成呼叫SwapBuffers函式,切換“back” buffer和“front” buffer,並將“front” buffer內容顯示在螢幕上,有個細節,顯示器重新整理頻率一般為60或120Hz,SwapBuffers呼叫時刻可能不是顯示器的重新整理時刻,這時SwapBuffers將會等待直到顯示器重新整理才返回(當然,肯定存在避免等待的技術)。

2.管線概覽

下圖截圖自文獻[4],OpenGL 4.5 API Reference Card 的 OpenGL Pipeline(調整了文字位置):

忽略一些高大上的新著色器只考慮頂點、幾何、片斷著色器,管線總結為:頂點資料(Vertices) > 頂點著色器(Vertex Shader) > 圖元裝配(Assembly) > 幾何著色器(Geometry Shader) > 光柵化(Rasterization) > 片斷著色器(Fragment Shader) > 逐片斷處理(Per-Fragment Operations) > 幀緩衝(FrameBuffer)。再經過雙緩衝的交換(SwapBuffer),渲染內容就顯示到了螢幕上。

再看經典管線版本,下圖截圖自文獻[1]第17頁,OpenGL 3.3 Specification (Compatibility rofile)(圖中淺藍色是我標註的):

再來看個民間提供的可讀性更好的圖(摘自文獻[8],這裡):

將上圖中的數字(4)和(4.2)(表示從OpenGL 4.2版本開始支援)中間部分去掉就是OpenGL 3.3的管線。

再看,一個有些過時,但很直觀的圖(摘自文獻[8],這裡):

在忽略細分、計算著色器,以及用固定管線功能代說頂點、幾何、片斷著色器之前,先來看看固定管線給這些著色器規定好的內建輸入輸出變數,看了這些輸入輸出基本就知道著色器該幹些什麼了(左圖截圖自文獻[4] GL4.5,右圖截圖自文獻[2] GL3.2,右圖中藍色字型是廢棄功能,正是固定管線功能部分):

3.管線圖中框的內部

下面將進入第二層次,說說上面各種管線圖中每個框的內部,將分為 頂點處理、圖元裝配裁剪等(加“等”是包括裝配後的其他操作)、光柵化、逐片斷處理 四個部分,這和上面圖中框的對應關係應該是明顯的。頂點處理基本對應頂點著色器,幾何著色器位於圖元裝配之後裁剪之前,片斷著色器位於逐片斷處理之前。在進入各個框之前,我們先大概劃清範圍,看看我們常用的固定管線功能都包括在哪個部分。頂點處理包括固定管線的頂點座標變換、光照(也即逐頂點光照)等;圖元裝配裁剪等包括圖元裝配、裁剪、透視除法、視口變換等;光柵化包括點線光柵化、多邊形填充、紋理(Texture)、霧(Fog)等;逐片斷處理包括各種測試(Scissor, Alpha, Stencil, Depth Test)、混合(Blending)等。

瞭解這些頗具指導意義,例如,知道了紋理屬於光柵化階段之後,就不會犯這樣的錯誤:將紋理的影響模式設定為Replace之後,還期望曲面在光照下有明暗變化(這麼想在Blender、Maya等軟體中是正確的,就好像用紋理的顏色值去定義曲面每點處的材質顏色),為什麼錯呢,因為紋理在光柵化階段進行,這時光照(在頂點處理部分進行)已經完成了,紋理的Replace模式直接對頂點光照計算後並插值到片斷上的顏色進行替換並最終寫入FrameBuffer,所以得不到光照明暗變化,要得到想要結果應該用Multiply影響模式並將光照材料設定為白色(這也是為什麼Multiply是預設模式的原因)。

另外,OpenGL Specification 和 API Quick Reference Card 也按照管線圖中的框來對內容進行分節的,確定一個東西屬於管線的哪個階段之後再去查就非常高效了。

3.1 頂點處理

頂點處理主要進行頂點齊次座標變換和光照(固定管線功能只有逐頂點光照)。頂點的齊次座標變換過程如下(文獻[1]第66頁):

正如圖中標的,頂點處理產生Clip Coordinates,藍線之後部分是下一節的內容。這裡還可能進行紋理座標自動生成(glEnable(GL_TEXTURE_GEN_S[TRQ])),即根據Object或者Eye Coordinates和指定的矩陣(glTexGeni(...))或者其他方式(球,立方體等)計算紋理座標(文獻[1]2.12.3)。

光照處理過程如下圖(文獻[1]第84頁):

若光照被關閉,頂點的顏色將直接設定成當前顏色(glColor()指定),若開啟,頂點將根據當前材料顏色(glMatiral(),可分正面背面分別設定)和法向量(glNormal())來計算環境、散射、高光、發射光的顏色,併疊加。光照分正背面進行(由glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TURE[FALSE])控制),也即頂點處理完成後每個頂點將有兩個顏色屬性值(見頂點著色器內建輸出變數,gl_FrontColor/BackColor),正面顏色的計算依據正面材料及法向量n,背面顏色計算依據背面材料及法向量的反方向-n,若雙面光照關閉,則只計算正面顏色並簡單將背面顏色設為和正面相同(關於單面光照最容易被誤解的就是,單面光照是簡單的將背面顏色設為和正面相同從而不去獨立計算,而不是隻照亮正面)。頂點的顏色將在隨後光柵化的時候被插值到片斷,光柵化時根據圖元頂點環繞方向(逆時針或順時針,見本文後面光柵化章節)確定正背面,並據此選擇用頂點的正面顏色值還是背面顏色值來插值。根據glShadeMode(GL_SMOOTH[FLAT])的設定,同一個圖元的頂點的顏色值將被單獨處理,或全都被賦值為其中一個頂點(ProvokingVertex)的值,請見文獻[1]2.21。固定管線的逐頂點光照類似於Gouraud模型,這區別於逐片斷光照(如Phong,對片斷先由其圖元頂點法向量插值出片斷法向量,再根據這個法向量計算光照,可由片斷著色器實現)。

另外,光照的計算涉及的距離、向量內積等均在視覺座標系(Eye Space)中進行(文獻[1]第80頁),函式glGetLightfv(GL_LIGHT0[1,2..],GL_POSITION,Glfloat*)返回的光源位置也是視覺座標(Eye Coordinates),這是因為按照頂點變換的實際意義,Eye Space還是直角座標系,而Clip Space就不一定了(當視景體近平面寬高比不為1時,在不考慮w座標,即不考慮投影矩陣第四行的情況下,xyz的縮放值可能不同,請見文獻[1]第69頁和文獻[7]第497頁投影矩陣的計算公式)。

將頂點的相關資料資訊合到一起,請看下圖(文獻[1]第20頁):

頂點的齊次座標變換、逐頂點光照等可以由頂點著色器代替,見文獻[1]2.14。

3.2 圖元裝配裁剪等

頂點處理或者頂點著色器的輸出是一些列變換後的位於Clip座標系的頂點,這些頂點首先根據頂點之間的連線關係(點、線、多邊形)進行圖元裝配(文獻[1]第21頁):

圖元裝配之後是裁剪(Clipping),見文獻[1]2.22,下面是裁剪公式(文獻[1]第142頁):

多邊形的裁剪可能產生新的頂點,這些的點的顏色值以及紋理座標等值要被插值。除了預設的xyz分別為±1的正方體的六個面的裁剪面,使用者可以指定額外的裁剪面:glClipPlane(GL_CLIP_PLANE0[1,2,...],double eqn[4]),glEnable/Disable(GL_CLIP_DISTANCE0[1,2,...]),注意指定的值會被乘以當前模型檢視矩陣的逆,乘完得到的值在視覺座標系中進行裁剪(同從頂點視覺座標自動生成紋理座標的引數),請見文獻[1]第142頁。

裁剪之後是透視除法(Perspective Division,文獻[1]第132頁):

透視除法之後是視口變換(Viewport Transformation,文獻[1]第132頁):

視口變換同時也將原來z座標縮放到[0,1]變成Depth值,深度值預設在[0,1]且值越小離攝像機越近,可以指定深度值範圍:glDepthRange(GLclampd n,GLclampd f),其中GLclampd[f]型別表示值將被鉗位到[0,1](文獻[1]第16頁),請見文獻[1]第132頁。視口變換完成後的圖元將進入光柵化階段。

上面的圖元裝配之後,裁剪、透視除法、視口變換等操作之前,也可以由幾何著色器對圖元進行操作,即在圖元裝配之後插入幾何著色器,見文獻[1]2.15.4。

3.3 光柵化

到目前為止,管線裡的資料都是頂點,經過圖元裝配之後,哪些頂點就是一個點、哪兩個頂點是直線段、哪三個或更多頂點是一個三角形或多邊形,這些圖元資訊都已經知道了,但它們還是隻是頂點而已:頂點處都還沒有“畫素點”、直線段端點之間是空的、多邊形的邊和內部也是空的,光柵化的任務就是構造這些。由於已經經過了視口變換,光柵化是在二維(附帶深度值)的螢幕座標系(Window Space)中進行的。

光柵化有兩個任務:1.確定圖元包含哪些由整數座標確定的“小方塊”(和螢幕畫素對應,現在還不能叫片斷,光柵化完成後才能叫片斷),2.確定這些小方塊的Depth值和Color值(從圖片頂點的Depth和Color插值得到),這些顏色後來可能被其他如紋理操作修改。見文獻[1]第150頁第3章開頭的描述,如下圖(文獻[1]第151頁):

光柵化在對多邊形圖元進行“方塊化”之前,要給出多邊形是front-facing還是back-facing(正面還是背面,點和直線只有正面),這是根據多邊形頂點的環繞方向確定的(是順時針還是逆時針,預設逆時針為front-facing,可由glFrontFace(GL_CCW[CW])控制)。正背面判斷結果將用於選擇是用頂點的正面顏色還是背面顏色來對片斷顏色進行插值(頂點正背面光照顏色請見本文前面頂點處理章節)。隨後如果glIsEnabled(GL_CULL_FACE)為真,對於方向和glCullFace(GL_FRONT[BACK])的引數相同的多邊形圖元,將被剔除,即直接跳過光柵化的後續操作。另外,光柵化除了直接對多邊形進行填充這種方式之外,還可以只構造邊或只有點,這由glPolyMode(GL_FRONT[BACK,FRONT_AND_BACK],GL_FILL[LINE,POINT])控制。

這裡強調一下光柵化判斷正背面和正背面光照的區別,前者是對圖元的操作並依據頂點環繞方向(一個多邊形圖元有多個頂點,也就有多個法向量,這些向量可能不同,所以不可能依據法向量來判斷圖元朝向),後者是對頂點的操作並依據頂點的法向量。舉個例子,如果一個三角形按照頂點環繞的右手法則方向的反方向指定法向量,並且法向量朝物體外側,當光照為單面光照時,因為光柵化判斷為背面的多邊形圖元其片斷用頂點背面光照顏色進行插值,單面光照下,和頂點正面光照顏色相同,所以沒有問題,但當光照為雙面光照時,圖元的沿法向量這邊的“光照正面”卻是光柵化依據頂點環繞方面判斷的“光柵化背面”,這時,我們將看到一個灰色的好像沒有光照一樣的東西,從而得不到結果。所以,對三角形或多邊形的由頂點法向量確定的正面(三個或更多頂點法向量確定的正面一致,指這個面)要和光柵化用頂點環繞方面的正面相同,這樣才不會出現意想不到的效果。

最為複雜的紋理在光柵化階段進行,下圖是多重紋理的操作示意(文獻[1]第280頁):

之所以說紋理複雜,在於紋理座標的計算上,每個片斷要找到一個紋理座標以索引紋理畫素,這個計算看似簡單,但出問題時將產生意想不到的效果,如下圖(摘自這裡):

圖中所說的Projective和Real Space座標就是我們所說的透視除法前後的座標。

在光柵化對圖元進行“小方塊化”並對“小方塊”進行插值之後,後來的紋理和霧等操作可以由片斷著色器代替,片斷著色器還可以對片斷進行更多計算,如逐片斷光照,處理後的片斷將進入下一步逐片斷處理。

3.4 逐片斷處理

光柵化的輸出是一些列片斷(Fragments,這些片斷可能經過片斷著色器處理),片斷被稱為“準畫素”,要能想象出螢幕座標系的一個整數座標上只有一個畫素,但可以前後“堆疊”多個片斷。這些片斷進入逐片斷處理(Per-Fragment Operations),首先進行各種測試(下圖中共5個),每步測試,不通過的片斷將被丟棄從而不能進入後續操作,然後進行一些操作(如混合),最終通過所有處理的片斷將被寫入FrameBuffer用於最終螢幕顯示,這個過程如下圖(文獻[1]第294頁):

Scissor Test對使用者指定的scissor rectangle進行測試,Alpha Test用片斷Alpha值進行測試(如片斷Alpha值小於設定ref值時通過),Depth Buffer Test和遮擋處理有關(如片斷深度值小於其同坐標的深度緩衝區元素的值時通過),Stencil Test可以根據Stencil或Depth Buffer Test結果分條件更新Stencil Buffer實現很多功能(如zfail時,片斷同坐標的Stencil緩衝區元素加1,zpass時減1,第二遍渲染再設定Stencil test為片斷同坐標Stencil緩衝區元素值為0時通過),如Shadow Volumes演算法。上圖中,框下面有小箭頭連線FrameBuffer的說明該測試要訪問或更新FrameBuffer的值。

所有操作均通過的片斷將被寫入FrameBuffer,包括RGBA緩衝、Depth緩衝,注意Stencil緩衝僅用於測試,片斷沒有Stencil值。還有一個緩衝叫做Accumulation Buffer,多用於運動模糊、景深模糊等,但不能直接寫入,而是將RGBA緩衝整幅累積。

這些操作可以用glEnable/glDisable(GL_ALPHA/STENCIL/DEPTH_TEST)、glEnable/glDisable(GL_BLEND)等開啟或關閉,對於RGBA, Depth, Stencil Buffer,可以用glColor/Depth/StencilMask(GLboolean/GLuint)進行控制是否可寫。注意,緩衝區使能和緩衝區遮蔽是獨立的,使能控制是否進行測試,如果不進行測試,片斷將直接通過,然後對於通過測試的片斷根據是否遮蔽決定是否更新緩衝區。

3.5 畫素處理及小結

在進入下一層次之前,先來看看畫素處理,請見下圖(文獻[1]第76頁):

可以看到,畫素處理主要工作就是,將畫素資料的例如[0,255]的整數RGBA值轉換到管線所需的[0,1]的浮點數。

將上述頂點處理、圖元裝配裁剪等、光柵化、逐片斷處理以及畫素處理合到一起,請看下圖(文獻[7]英文版第11頁,中文版在第6頁):

4.程式設計細節,OpenGL函式總結

下面進入下一個層次,OpenGL程式設計細節,這裡涉及的內容是:OpenGL的每個API函式如何影響渲染管線的狀態,並最終如何影響渲染過程。這裡主要參考文獻[2] OpenGL 3.2 API Quick Reference Card,它對包含固定管線功能在內的API做了非常好的總結。本文這一節對常用的OpenGL函式進行總結,也是針對固定管線功能,不包括著色器部分。

下面的總結以方便閱讀為目的,並不全面,某些函式有xx3f, xx4f, xx3fv等多個版本的只給出一個版本。OpenGL有很多狀態變數,很多時候我們在改變一個狀態並完成一些操作之後希望恢復這個狀態的原來的值,這需要對狀態進行查詢,這裡將給出操作對應的查詢函式,並給出狀態的初始值。

4.1 頂點資料輸入

操作:文獻[1]第22頁

glBegin(GL_POINTS[GL_LINES, GL_TRIANGLES, GL_POLYGON, ...]) 圖元資料開始
glColor4fv(GLfloat*) 頂點顏色屬性
glNormal3fv(GLfloat*) 頂點法向量
glTexCoord4fv(GLfloat*) 頂點紋理座標,原點位於圖片左下角,寬和高範圍[0,1]
glFogCoordf(GLfloat) 頂點霧座標
glVertex4f(GLfloat*) 頂點座標
glEnd() 圖元資料結束

查詢:文獻[1]第415頁,文獻[7]第471頁

glGetFloatv(GLenum, GLfloat*)
  GL_CURRENT_COLOR 初值 (1, 1, 1, 1)
  GL_CURRENT_NORMAL 初值 (0, 0, 1)
  GL_CURRENT_TEXTURE_COORDS 初值 (0, 0, 0, 1)
  GL_CURRENT_FOG_COORD 初值 0

4.2 變換矩陣,視口

操作:文獻[1]第66頁,第132頁

glMatrixMode(GL_MODELVIEW[, GL_PROJECTION, GL_TEXTURE, GL_COLOR]) 操作哪個矩陣
glLoadIdentity() 將當前矩陣設定為單位陣
glLoadMatrixf(GLfloat*) 在當前矩陣替換為引數指定矩陣(列優先)
glLoadTransposeMatrixf(GLfloat*) 同glLoadMatrixf,但行優先
glMultMatrixf(GLfloat*) 在當前矩陣右邊乘以引數指定矩陣(列優先)
glMultTransposeMatrixf(GLfloat*) 同glMultMatrixf,但行優先
glTranslatef(x, y, z) 在當前矩陣右邊乘以平移矩陣
glRotatef(angle, nx, ny, nz) 在當前矩陣右邊乘以旋轉矩陣
glScalef(sx, sy, sz) 在當前矩陣右邊乘以縮放矩陣
glFrustum(left,right,bottom,top,zNear,zFar) 在當前矩陣右邊乘以透視投影矩陣
glOrtho(l,r,b,t,n,f) 在當前矩陣右邊乘以平行投影矩陣
glPushMatrix() 向矩陣堆疊壓入原頂部矩陣
glPopMatrix() 矩陣堆疊彈出頂部矩陣
glViewport(ox,oy,width,height) 視口變換

查詢:文獻[1]第422頁,文獻[7]第474頁

glGetIntegerv(GLenum, GLint*)
  GL_MATRIX_MODE, 初值 GL_MODELVIEW
glGetFloatv(GLenum, GLfloat*)
  GL_MODELVIEW_MATRIX 初值 單位陣
  GL_PROJECTION_MATRIX 初值 單位陣
  GL_TEXTURE_MATRIX 初值 單位陣
  GL_COLOR_MATRIX 初值 單位陣
glGetIntegerv(GLenum pname, GLint*)
  GL_VIEWPORT 初值 未定義

4.3 光照

操作: 文獻[7]第138頁、第130頁,光照公式148頁

glEnable/glDisable(GL_LIGHTING)  使能光照
glEnable/glDisable(GL_LIGHT0[1,2,...])  使能第i個光源
glLightModel[if][v](GLenum, para)
  GL_LIGHT_MODEL_AMBIENT  全域性環境光顏色強度值
  GL_LIGHT_MODEL_LOCAL_VIEWER  高光是否為有限遠光源
  GL_LIGHT_MODEL_TWO_SIDE  是否為雙面光照
  GL_LIGHT_MODEL_COLOR_CONTROL (下面2行是該引數後續引數值)
    GL_SINGLE_COLOR  所有光照顏色在紋理前計算
    GL_SEPARATE_SPECULAR_COLOR  將高光推遲到紋理後計算
glLightf[v](GL_LIGHT0[1,2,...], GLenum, para)
  GL_AMBIENT[DIFFUSE,SPECULAR]  光源各成分環境、漫反射、高光的顏色強度
  GL_POSITION  光源位置(受到當前模型檢視矩陣的變換,w座標為0表示方向性光源)
  GL_SPOT_CUTOFF  聚光燈扇角(邊沿和中心線夾角),180度為點光源,小於180度才是聚光燈
  GL_SPOT_EXPONENT  聚光燈聚光指數,即中心到邊沿衰減速度,0為不變化
  GL_SPOT_DIRECTION  聚光燈照射方向
  GL_CONSTANT_ATTENUATION  光強隨到光源距離d衰減,分母中常數項d0係數
  GL_LINEAR_ATTENUATION  光強隨到光源距離d衰減,分母中一次項d1的係數
  GL_QUADRATIC_ATTENUATION  光強隨到光源距離d衰減,分母中二次項d2的係數
glMaterialfv(GL_FRONT[BACK,FRONT_AND_BACK], GLenum, para)
  GL_AMBIENT[DIFFUSE,AMBIENT_AND_DIFFUSE,SPECULAR]  設定材料的各成分顏色值
  GL_EMISSION  設定材料自發光值,注意這個光不是光源,不會照射其他物體
  GL_SHININESS  設定高光的亮斑聚光係數

查詢:文獻[1]第424頁,文獻[7]第475頁

glIsEnabled(GLenum)
  GL_LIGHTING 初值 GL_FALSE
  GL_LIGHT0[1,2,...] 初值 GL_FALSE
glGetFloatv(GLenum, GLfloat*)
  GL_LIGHT_MODEL_AMBIENT 初值 (0.2,0.2,0.2,1)
glGetBooleanv(GLenum, GLboolean*)
  GL_LIGHT_MODEL_LOCAL_VIEWER 初值 GL_FALSE
  GL_LIGHT_MODEL_TWO_SIDE 初值 GL_FALSE
glGetIntegerv(GLenum, GLint*)
  GL_LIGHT_MODEL_COLOR_CONTROL 初值 GL_SINGLE_COLOR
glGetLightfv(GL_LIGHT0[1,2,...], GLenum, GLfloat*)
  GL_AMBIENT[DIFFUSE,SPECULAR] 初值 (0,0,0,1)
  GL_POSITION 光源的視覺座標系座標 初值 (0,0,1,0)
  GL_SPOT_CUTOFF 初值 180
  GL_SPOT_EXPONENT 初值 0
  GL_SPOT_DIRECTION 初值 (0,0,-1)
  GL_CONSTANT_ATTENUATION 初值 1
  GL_LINEAR_ATTENUATION 初值 0
  GL_QUADRATIC_ATTENUATION 初值 0
glGetMaterialfv(GL_FRONT[BACK], GLenum, GLfloat*)
  GL_AMBIENT 初值 (0.2,0.2,0.2,1)
  GL_DIFFUSE 初值 (0.8,0.8,0.8,1)
  GL_SPECULAR 初值 (0,0,0,1)
  GL_EMISSION 初值 (0,0,0,1)
  GL_SHININESS 初值 0

4.4 光柵化 

操作:文獻[1]第169頁 

glPointSize(GLfloat) 點的直徑
glLineWidth(GLfloat) 直線寬度
glEnable/glDisable(GL_CULL_FACE) 使能表面剔除
glFrontFace(GL_CCW[CW]) 頂點環繞方向為逆時針還是順時針為正面
glCullFace(GL_FRONT[BACK,FRONT_AND_BACK]) 剔除正面還是背面
glPolygonMode(GL_FRONT[BACK,FRONT_AND_BACK], GL_POINT[LINE,FILL])
  設定多邊形正面或背面的光柵化方式:頂點、邊線、填充面
glEnable/glDisable(GL_POLYGON_OFFSET_FILL[LINE,POINT]) 使能多邊形偏移
glPolygonOffset(factor,units)
  設定多邊形片斷深度值的偏移值:factor*多邊形斜率+units*深度值的最小分度

查詢: 文獻[1]第426頁,文獻[7]第476頁 

glGetFloatv(GLenum, GLfloat*)
  GL_POINT_SIZE 初值 1
  GL_LINE_WIDTH 初值 1
glIsEnabled(GLenum)
  GL_CULL_FACE 初值 GL_FALSE
glGetIntegerv(GLenum, GLint*)
  GL_FRONT_FACE 初值 GL_CCW
  GL_CULL_FACE_MODE 初值 GL_BACK
  GL_POLYGON_MODE 返回正背面兩個數 初值 GL_FILL
glIsEnabled(GLenum)
  GL_POLYGON_OFFSET_FILL 初值 GL_FALSE
  GL_POLYGON_OFFSET_LINE 初值 GL_FALSE
  GL_POLYGON_OFFSET_POINT 初值 GL_FALSE
glGetFloatv(GLenum, GLfloat*)
  GL_POLYGON_OFFSET_FACTOR 初值 0
  GL_POLYGON_OFFSET_UNITS 初值 0

4.5 紋理

操作:文獻[1]第217頁,紋理引數251頁,紋理函式270頁;文獻[7]紋理引數288頁,紋理函式282頁 

glActiveTexture(GL_TEXTURE0[1,2,...]) 多重紋理,設定當前紋理單元
glEnable/glDisable(GL_TEXTURE_2D[1D,3D]) 使能紋理功能
glGenTextures(GLsizei n, GLuint *texs)
  分配紋理物件,紋理物件儲存紋理的引數、畫素資料等,第一次繫結時才分配引數的儲存空間
glDeleteTextures(GLsizei n, GLuint *texs) 刪除紋理物件
glBindTexture(GL_TEXTURE_2D[1D,3D], tex) 繫結tex為當前紋理
glTexImage2D(GL_TEXTURE_2D[...], level, internalFormat, width, height, border,
  format, type, *pixels) 指定紋理畫素
  level為LOD的第幾層,沒有LOD為 0 border為邊框,沒有邊框為 0
  internalFormat和format為 GL_RGBA[RED,ALPHA,LUMINANCE,DEPTH_COMPONENT,...]
    指定紋理和pixels畫素格式
  type為 GL_UNSIGNED_BYTE[FLOAT,...] 為儲存格式
  函式呼叫之後pixels的內容被拷貝,pixels可以delete以釋放記憶體
glCopyTexImage2D(target,level,internalFormat,ox,oy,width,height,border)
  引數意義和glTexImage2D基本相同,但從FrameBuffer中拷貝畫素資料
glTexSubImage2D(target,level,xoffset,yoffset,width,height,format,type,*pixels)
glCopyTexSubImage2D(target,level,xoffset,yoffset,ox,oy,width,height)
  和glTexImage2D、glCopyTexImage2D基本相同,但只操作紋理的子區域
glTexParameteri(GL_TEXTURE_2D[1D,3D], GLenum, para) 紋理的引數
  GL_TEXTURE_WRAP_S[T,R,Q] 紋理座標超出[0,1]後處理方式 GL_REPEAT[CLAMP,...]
  GL_TEXTURE_MAG[MIN]_FILTER 紋理放大縮小時畫素插值方式 GL_LINEAR[NEAREST]
  GL_TEXTURE_BORDER_COLOR 紋理邊框顏色
  GL_DEPTH_TEXTURE_MODE 紋理深度值使用模式 GL_LUMINANCE[INTENSITY,ALPHA,RED]
  GL_TEXTURE_COMPARE_MODE 紋