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 紋理比較模式 GL_NONE[COMPARE_R_TO_TEXTURE,...]
  GL_TEXTURE_COMPARE_FUNC 比較函數 GL_LEQUAL[EQUAL,LESS,GREATER,...]
glTexEnvfv(GL_TEXTURE_ENV[...], GLenum, para) 紋理單元的參數
  GL_TEXTURE_ENV_MODE 紋理如何影響片斷的顏色 GL_MODULATE[REPLASE]
glEnable(GL_TEXTURE_GEN_S[T,R,Q]) 使能紋理座標自動生成
glTexGeni(GL_S[T,R,Q], GLenum, para) 紋理座標自動生成參數
  GL_TEXTURE_GEN_MODE 生成方式 GL_OBJECT[EYE]_LINEAR
  GL_OBJECT_PLANE 從物體座標系座標生成紋理座標,指定矩陣的一行
  GL_EYE_PLANE 從視覺座標系座標生成紋理座標,指定矩陣的一行,會被乘以當前模型視圖矩陣的逆

查詢:文獻[1]第429頁,文獻[7]第477頁

glGetIntegerv(GLenum, GLint*)
  GL_ACTIVE_TEXTURE 初值 GL_TEXTURE0
glIsEnabled(GLenum)
  GL_TEXTURE_2D[1D,3D] 初值 GL_FALSE
glGetIntegerv(GLenum, GLint*)
  GL_TEXTURE_BINDING_2D[1D,3D] 初值 0
glGetTexImage(GL_TEXTURE_2D[1D,3D], level, format, type, *pixels)
  初值 未定義
glGetTexParameterfv(GL_TEXTURE_2D[1D,3D], GLenum, para)
  GL_TEXTURE_WRAP_S[T,R] 初值 GL_REPEAT
  GL_TEXTURE_MAG[MIN]_FILTER 初值 GL_LINEAR[NEAREST_MIPMAP_LINEAR]
  GL_TEXTURE_BORDER_COLOR 初值 (0,0,0,0)
  GL_DEPTH_TEXTURE_MODE 初值 GL_LUMINANCE
  GL_TEXTURE_COMPARE_MODE 初值 GL_NONE
  GL_TEXTURE_COMPARE_FUNC 初值 GL_LEQUAL
glGetTexEnvfv(GL_TEXTURE_2D[1D,3D], GLenum, para)
  GL_TEXTURE_ENV_MODE 初值 GL_MODULATE
glIsEnabled(GLenum)
  GL_TEXTURE_GEN_S[T,R,Q] 初值 GL_FALSE
glGetTexGeni[f]v(GLenum, para)
  GL_TEXTURE_GEN_MODE 初值 GL_EYE_LINEAR
  GL_OBJECT_PLANE 初值 未定義
  GL_EYE_PLANE 初值 未定義

4.6 逐片斷處理

操作:文獻[1]第294頁 

glEnable/glDisable(GLenum)
  GL_SCISSOR_TEST Scissor測試
  GL_ALPHA_TEST Alpha測試
  GL_STENCIL_TEST 模板測試
  GL_DEPTH_TEST 深度測試
  GL_BLEND 顏色混合
glClear(GL_COLOR_BUFFER_BIT[|DEPTH][|STENCIL][|ACCUM])
  清除顏色[深度,模板,積累]緩衝區,用指定的清除值
glClearColor(GLfloat r,g,b,a) 顏色緩衝區清除顏色
glClearDepth(GLfloat d) 深度緩衝區清除值
glClearStencil(GLint s) 模板緩衝區清除值
glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)
  GL_TRUE,GL_FALSE 分別指定顏色緩衝區各個分量是否可寫
glDepthMask(GLboolean) 深度緩衝區是否可寫
glStencilMask(GLuint mask) mask每個比特位爲1緩衝區元素該比特位可寫
glAlphaFunc(GLenum fun, GLfloat ref) Alpha測試,片斷Alpha值之於ref的通過條件
  GL_NEVER,ALWAYS,LESS,LEQUAL,EQUAL,NOTEQUAL,GREATER,GREATER
glStencilFunc(GLenum fun, GLint ref, GLuint mask)  fun取值同glAphaFunc
  片斷對應像素座標位置的模板Buffer元素設爲a,(a&mask)和(ref&mask)的比較結果爲通過條件
glStencilFuncSeparate(GLenum face, fun, ref, mask)
  同glStencilFunc,但對多邊形分正背面單獨設置,點和直線只有正面,fun取值同glAlphaFunc
glDepthFunc(GLenum fun) 深度測試函數(片斷之於Buffer對應元素的通過條件),fun取值同glAlphaFunc
glStencilOp(GLenum fail, GLenum pzfail, GLenum pzpass)
  片斷模板測試失敗、模板通過但深度測試失敗、通過但深度測試通過時,對片斷對應模板Buffer元素的操作
  GL_KEEP,ZERO,REPLACE,INCR,DECR,INVERT,INCR_WRAP,DECR_WRAP
  分別表示:保持,替換爲0,替換爲ref,帶封頂++,帶封頂--,逐位反轉,帶繞回++,帶繞回--
glStencilOpSeparate(GLenum face, fail, zfail, zpass)
  同glStencilOp,但對多邊形分正背面單獨設置
glBlendEquation(GLenum mode) 混合方程的符號
  GL_FUNC_ADD,GL_FUNC_SUBTRACT,GL_FUNC_REVERSE_SUBTRACT
glBlendEquationSeparate(modeRGB, modeAlpha) 同glBlendEquation,RGB和A分開設置
glBlendFunc(GLenum sfactor, GLenum dfactor) 源(片斷)和目標(FrameBuffer中的值)混合係數
  GL_ZERO,ONE,SRC_ALPHA,ONE_MINUS_SRC_ALPHA,DST_ALPHA,DST_COLOR,...
glBlendFuncSeparate(srcRGB,dstRGB,srcAlpha,dstAlpha) 同glBlendFunc,但RGB和A分開設置 

查詢:文獻[1]第436頁,文獻[7]第480頁 

glIsEnabled(GLenum)
  GL_SCISSOR_TEST 初值 GL_FALSE
  GL_ALPHA_TEST 初值 GL_FALSE
  GL_STENCIL_TEST 初值 GL_FALSE
  GL_DEPTH_TEST 初值 GL_FALSE
  GL_BLEND 初值 GL_FALSE
glGetFloatv(GLenum, GLfloat*)
  GL_COLOR_CLEAR_VALUE 初值 (0,0,0,0)
  GL_DEPTH_CLEAR_VALUE 初值 1
  GL_STENCIL_CLEAR_VALUE 初值 0
glGetBooleani_v(GLuint bufferi, GLenum, GLboolean*)
  GL_COLOR_WRITEMASK 初值 (GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE)
glGetBooleanv(GLenum, GLboolean*)
  GL_DEPTH_WRITEMASK 初值 GL_TRUE
glGetIntegerv(GLenum, GLint*)
  GL_STENCIL_WRITEMASK 初值 全1
glGetIntegerv(GLenum, GLint*)
  GL_ALPHA_TEST_FUNC 初值 GL_ALWAYS
  GL_STENCIL[_BACK]_FUNC 初值 GL_ALWAYS 正面和背面
  GL_STENCIL[_BACK]_REF 初值 0
  GL_STENCIL[_BACK]_VALUE_MASK 初值 全1
  GL_DEPTH_FUNC 初值 GL_LESS
  GL_STENCIL[_BACK]_FAIL 初值 GL_KEEP
  GL_STENCIL[_BACK]_PASS_DEPTH_FAIL 初值 GL_KEEP
  GL_STENCIL[_BACK]_PASS_DEPTH_PASS 初值 GL_KEEP
  GL_BLEND_EQUATION_RGB[ALPHA] 初值 GL_FUNC_ADD
  GL_BLEND_SRC_RGB[ALPHA] 初值 GL_ONE
  GL_BLEND_DST_RGB[ALPHA] 初值 GL_ZERO 

4.7 其他函數

glReadPixels(ox, oy, width, height, GLenum format, GLenum type, GLvoid*pixels)
  讀取FrameBuffer
glDrawPixels(width, height, GLenum format, GLenum type, const GLvoid *pixels)
  寫入FrameBuffer

被漏掉議題:顯示列表,霧,頂點列表,緩衝區對象,幀緩衝對象,條件渲染,渲染查詢,同步,着色器等,參考文獻[2]。

 

參考文獻

  1. OpenGL 3.3 Specification (Compatibility rofile) - March 11, 2010(去官網下載);
  2. OpenGL 3.2 API Quick Reference Card(去官網下載);
  3. OpenGL 4.5 Specification (Compatibility rofile) - October 30, 2014(去官網下載);
  4. OpenGL 4.5 API Reference Card(去官網下載);
  5. University of Freiburg的Computer Graphics小組課程主頁(去課程主頁圖形管線PPT);
  6. Stanford University, CS148 Introduction to Computer Graphics and Imaging (Fall 2014)(去課程主頁圖形管線PPT);
  7. 《OpenGL編程指南》(原書第7版,Dave Shreiner等著,李軍等譯,機械工業出版社,2011)(到噹噹網買);
  8. http://www.lighthouse3d.com/tutorials/現代管線經典管線);
  9. Patrick Cozzi and Christophe Riccio, Opengl Insights, CRC Press, 212(圖書網站圖形管線高清PDF版管線圖);
  10. CUDA Toolkit Documentation v6.5, Programming Guide(在線版本);
  11. 《GPU高性能運算之CUDA》(張舒等主編,中國水利水電出版社,2009)(到噹噹網買);
  12. https://www.opengl.org/wiki/Rendering_Pipeline_Overview

發佈了60 篇原創文章 · 獲贊 33 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章