圖形管線(graphics pipeline)向來以複雜爲特色,這歸結爲圖形任務的複雜性和挑戰性。OpenGL做爲圖形硬件標準,是最通用的圖形管線版本。本文用自頂向下的思路來簡單總結OpenGL圖形管線,即從最高層開始,而後逐步細化到管線圖中的每一個框,再進一步細化到OpenGL具體函數。注意,這裏用經典管線代說着色器內部,也就是OpenGL固定管線功能(Fixed-Function,相對於programmable也便可編程着色器),也會涉及着色器,但差很少僅限於「這些固定管線功能對應xx着色器」。之後有機會會單獨說着色器。html
有人可能會說,固定管線功能在不少年前就過期啦,並且在最新的OpenGL標準裏都不支持了,不是兼容支持,而是完全刪除了。我是這麼認爲的,首先,雖然OpenGL最新標準(4.0或更高)明確刪除了固定管線功能,但顯卡廠商還在提供固定管線的驅動程序支持,由於還有不少人在用這些固定管線功能;其次,OpenGL自己僅是一個工具,在這個工具上設計巧妙的算法,或者實現須要的效果纔是重點,若是用固定管線就能實現這些,也就沒必要要用着色器,由於即便用着色器也可能只是在實現固定管線功能而已;再次,對於學習着色器來講,瞭解固定管線是一個很好的案例學習,我想好多學習着色器的人都是這麼開始的吧:用頂點着色器實現固定管線的變換功能,固定管線做爲最通用功能的實現,必然通過了頂尖工程師的精心設計,很值得參考。web
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]。spring
文獻[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]):app
這個圖稍微有點過期(不過和我如今用的臺式機基本吻合,哈哈),將各個數據傳輸帶寬值增長一倍基本就是目前最好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]。
參考文獻