OpenGL 4.5 Core Profile管線(GLSL與應用程序接口詳解)【未完成】

 

以前寫過一篇博客,OpenGL管線(用經典管線代說着色器內部)說的主要是OpenGL的經典管線。你們都知道,現代OpenGL已經棄用(從OpenGL 3.0開始)經典管線功能(glBegin,變換矩陣,光照,霧,紋理座標自動生成,等),這些功能能夠在須要時由着色器實現。現代OpenGL分爲core profile和compatibility profile兩個版本(文獻[1]Appendix D p682),core profile不包含任何棄用功能,而compatibility profile不刪除任何功能,本文要講的是OpenGL core profile。html

所謂OpenGL管線(OpenGL pipeline),就是指OpenGL的渲染過程,即從輸入數據到最終產生渲染結果數據所通過的通路及所經受的處理,本文詳細解釋這一過程。除了講解OpenGL管線,本文還將解釋如何使用OpenGL API實現這些管線功能,也即應用程序如何和着色器(shader,用OpenGL shading language,GLSL編寫)接口,這包括應用程序和着色器以及着色器之間如何通訊。linux

在正式進入OpenGL管線以前咱們先來看看有關OpenGL Context(OpenGL上下文環境)的問題,在使用OpenGL以前必須先建立OpenGL Context,並將此OpenGL Context 「make current」(線程的current context)。OpenGL標準並不定義如何建立OpenGL Context,這個任務由其餘標準定義,如GLX(linux)、WGL(windows)、EGL(通常在移動設備上用),能夠選擇建立core profile或compatibility profile,一個GLX的例子請見這裏的87-134行。若是建立的是core profile OpenGL context,調用如glBegin()等兼容API將產生GL_INVALID_OPERATION錯誤(用glGetError()查詢)。咱們可能但願當咱們使用兼容API時(如glBegin()),編譯器可以提示編譯錯誤而不是運行時產生OpenGL錯誤,能夠包含"glcorearb.h"這個頭文件,並"#define __gl_h_"來屏蔽"<GL/gl.h>"(__gl_h_爲<GL/gl.h>的包含守衛宏,可能因實現差別而不一樣)。git

(本博文地址:http://www.cnblogs.com/liangliangh/p/4765645.html,轉載版本將得不到做者維護,另歡迎讀者指出文中錯誤,請直接博客後面留言) github

 

0. Client-Server模型編程

OpenGL的執行模型(Execution Model,參考文獻[1]2.1 p8) 能夠用Client-Server(客戶機-服務器)模型來解釋:windows

  • Client即咱們的OpenGL應用程序(program/application),Server即OpenGL引擎(姑且叫引擎吧),Client(應用程序)經過調用OpenGL API來簽發(issue)OpenGL命令(OpenGL command),這些命令由Server(OpenGL引擎,在不至混淆的狀況下也稱OpenGL或GL)來執行;
  • OpenGL(Server)在GPU上執行命令,其處理的數據緩存(buffer、texture等)通常存在於顯存(video memory)中,這些緩存能夠從應用程序(Client)拷貝數據,也能夠拷貝到應用程序;
  • OpenGL由固定管線功能部分(fixed function stages)和可編程部分(programmable stages)組成,可編程部分即着色器(shader),用GLSL(OpenGL OpenGL Shading Language,OpenGL着色語言)進行着色器編程;
  • 應用程序和OpenGL能夠在也能夠不在同一臺計算機上執行,即Client和Server是網絡透明的(network transparent)。一個網絡渲染的例子是經過Windows遠程桌面在遠程計算機上啓動OpenGL程序,應用程序在遠程計算機執行,而OpenGL命令在本地計算機執行(將幾何數據而不是將渲染結果圖像經過網絡傳輸)。當Client和Server位於同一臺計算機上時,也稱GPU爲Device,CPU爲Host,Device、Host這兩個術語一般在用GPU進行通用計算時使用。如無特別說明本文講解的都是本地渲染的狀況;
  • OpenGL命令執行的結果是影響OpenGL狀態(由OpenGL context保存,包括OpenGL數據緩存)或影響幀緩存;
  • 應用程序和OpenGL命令的執行一般是異步的,OpenGL API調用返回並不說明OpenGL執行完了相應命令,但OpenGL保證按簽發命令的順序執行相應命令,至少保證結果和按串行執行的結果是一致的(即一個命令已經對OpenGL狀態或幀緩存產生了影響再執行下一條命令),但實際執行過程可能並不如此。存在OpenGL API用於控制應用程序(CPU)和OpenGL(GPU)之間的同步以及和異步相關的查詢,此時將OpenGL命令序列看作命令流(command stream)是方便的。同步的典型例子是:glFlush()強制發出全部OpenGL命令並在此函數返回後的有限時間內執行完這些OpenGL命令,glFinish()等待直到此函數以前的OpenGL命令執行完畢才返回;
  • 數據綁定(data binding)發生在OpenGL命令調用時,即應用經過OpenGL API傳送給OpenGL的數據在API調用時解釋,並在調用返回時完成。典型的例子是經過指針指向的數據給OpenGL傳送數據(如glBufferData()),在此API調用返回後修改指針指向的數據將再也不對OpenGL狀態產生影響;
  • OpenGL的對象(objects)能夠被多個Context共享(參考文獻[1]chapter5 p50),此時的對象刪除、同步等操做要複雜一些。如無特別說明本文講解的都是單Context的無共享的狀況。

在有了Client-Server相關概念後,下一章咱們概覽OpenGL管線。api

 

1. 概覽緩存

 下圖摘自文獻[3] p8,圖中棕色線條是我加入的,表示此階段爲可選。服務器

首先解釋一下這個圖,圖中的藍色框表示數據緩存(data buffers),其右側字母B爲Buffer(緩存),字母T爲Texture(紋理),綠色框爲固定管線功能(fixed function stages),黃色框爲可編程部分(programmable stages)即着色器。圖中的箭頭表示數據流的方向,例如,看中間的藍色框,shader能夠讀和寫「Shader Storage」 buffer,但只能讀「Uniform Block」buffer,固然,應用程序能夠讀和寫任何buffer和texture。通常來講,並不把Compute Shader(計算着色器)看成管線的一部分,它執行通用計算,計算的結果能夠用於渲染,Compute Shader是可選的。網絡

從Vertex Puller(頂點拉取)到Framebuffer(幀緩存)的管線中,除了Vertex Shader(頂點着色器)是必須提供以外,其餘shader均是可選的,如上圖中我用醬色標註的部分,若是沒有提供相應shader,數據將不通過加工之間經過。注意Tessellation Control Shader(TCS,細分控制着色器)到Tessellation Evaluation Shader(TES,細分求值着色器)這整個Tessellation(曲面細分)過程是可選的,在Tessellation過程當中TCS是可選的,即TCS只能伴隨TES存在(不然會報運行時錯誤,後面細說)。Vertex Shader不可選是由於管線至少要包含一個着色器,而其餘着色器存在的前提是存在Vertex Shader(文獻[1]7.3 p90),從語義上來講,Vertex Shader定義了整個管線的輸入數據。

管線中的每一個階段其輸入爲上一階段的輸出,其輸出做爲下一階段的輸入,Vertex Shader的輸入和應用程序的頂點屬性數據接口,Fragment Shader的輸出和幀緩存的顏色緩存接口,互相對接的接口其內容和格式要一致。固定管線功能階段須要的一些特定輸入輸出由着色器的內置輸出輸入變量定義,下圖摘自文獻[3] p10:

這裏,粗略的解釋管線各個部分所作的處理,這裏說的「管線」在不做特別說明的狀況下指從Vertex Puller到Framebuffer:

  1. Vertex Puller(頂點拉取):管線從供給頂點數據開始。頂點數據中的每一個頂點有可任意定義的若干屬性,如位置、顏色、紋理座標等,頂點屬性數據存於Vertex Buffer(glBindBuffer( GL_ARRAY_BUFFER, ))中,每一個圖元(primitive,文獻[1]10.1 p322)的頂點索引存於Element Array Buffer(glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ))中。應用程序經過繪製命令啓動管線(如glDrawElements( GL_POINTS/LINES/TRIANGLES/PATCHES, ));
  2. Vertex ShaderVS,頂點着色器):對每一個頂點,OpenGL啓動一個VS,VS的輸入即每一個頂點的所有屬性,還包括該頂點的索引(index,由內置變量gl_VertexID指示)等,VS沒法訪問其餘頂點數據(非本VS的輸入),VS必須且只能輸出一個頂點。VS對頂點進行處理,典型的例子是頂點座標變換(將頂點齊次座標乘以模型視圖矩陣和投影矩陣)和逐頂點光照;
  3. Tessellation(曲面細分,可選階段):又分爲Tessellation Control ShaderTCS,細分控制着色器)、Tessellation Primitive Generation(細分圖元生成)、Tessellation Evaluation ShaderTES,細分求值着色器),其中TCS是可選的。當Tessellation階段存在時,只能給管線提供GL_PATCHES類型的圖元,不存在時,不能提供GL_PATCHES類型的圖元,即Tessellation和GL_PATCHES需同時出現。(1) 對每一個PATCH由TCSs處理後輸出的PATCH,OpenGL對其每一個輸出頂點啓動一個TCS(該輸出頂點在輸出PATCH中的索引由gl_InvocationID指示),該TCS能夠訪問其所在的輸入PATCH的所有頂點數據及PATCH的屬性(在TCS/TES內部由patch關鍵字指定),即一個輸入PATCH被一組TCSs處理獲得一個輸出PATCH,組內TCSs的個數等於輸出PATCH的頂點數,TCS必須且只能輸出一個頂點,而且能夠修改其所在輸出PATCH的屬性。TCS通常對頂點進行操做並計算PATCH的細分水平值(tessellation level,gl_TessLevelOuter[4]/Inner[2],是PATCH的屬性),若TCS不存在,該細分水平值均爲默認(可經過glPatchParameteri( GL_PATCH_VERTICES, )設置)。(2) Tessellation Primitive Generation根據此細分水平值對每一個PATCH在抽象圖元上(abstract patch,"triangles"或"quads")進行細分(此階段僅在TES存在時進行)並給出生成頂點相對抽象圖元頂點的相對座標(gl_TessCoord,包含抽象圖元的頂點,其相對座標某個元素爲1其他爲0)。(3) 對Tessellation Primitive Generation生成的每一個相對座標,OpenGL啓動一個TES,TES能夠訪問其所在輸入PATCH的所有頂點數據及PATCH的屬性(即TCS的輸出),即TCS輸出的PATCH外加相對座標被一組TESs處理獲得一些列輸出頂點,組內TESs的個數等於輸出頂點數,TES必須且只能輸出一個頂點。TES通常用相對座標從輸入PATCH的全部頂點計算輸出頂點的齊次座標(如根據貝塞爾曲面方程計算)。(end) Tessellation輸出的頂點(由TES產生)隨後被組裝爲圖元(非GL_PATCHES類型圖元,後續階段能夠處理);
  4. Geometry ShaderGS,幾何着色器,可選階段):前面階段中頂點數據被組裝爲圖元(primitive assembly),其類型由繪製命令的參數或TES的輸出決定。對每一個圖元,OpenGL啓動一個GS,能夠定義對每一個圖元啓動多個GS(GS中"layout(invocations=?) in;"),GS能夠輸出零個至多個新的圖元(用EmitVertex()、EndPrimitive()等),GS不能訪問除輸入圖元以外的圖元,當圖元類型爲GL_*_ADJACENCY時,每一個圖元帶有鄰接信息,該鄰接信息在GS不存在時將被忽略。能夠指定將GS的輸出定向到特定Stream中,只有Stream 0中的數據會繼續進入Transform Feedback以後的圖元裁剪、光柵化階段,能夠同時定向CS的輸出至多個Stream(此時圖元類型受限制),若GS不存在,圖元數據都將進入Stream 0。GS通常用做幾何計算,如Shadow Volumes。(end) 從VS到GS的階段合起來稱爲頂點處理(vertex processing),頂點處理階段應該對內置變量gl_Position進行寫入(可在VS到GS的任何階段),不然其值是未定義的(undefined),該裁剪空間的齊次座標(clip coordinates)是後續階段(圖元裁剪、光柵化等)須要的;
  5. Transform Feedback(變換反饋,可選階段):在GS以後,能夠開啓一個固定管線功能階段,若開啓(用glBeginTransformFeedback()開啓,glEndTransformFeedback()關閉,即在它們之間的繪製結果被記錄),頂點處理的結果即輸出的圖元能夠被寫入Transform Feedback Buffer(glBindBuffer( GL_TRANSFORM_FEEDBACK_BUFFER, )),Transform Feedback過程僅記錄數據不對圖元進行處理,即圖元直接經過(bypass)。記錄在buffer中的數據在隨後又能夠被用來進行繪製(用glDrawTransformFeedback()等);
  6. Vertex Post-Processing(頂點後處理):頂點處理的結果即輸出的圖元,其頂點的位置(gl_Position)是裁剪空間的齊次座標(clip coords),圖元在此空間內進行裁剪(clipping),隨後進行透視除法(perspective divide)轉換爲規範化座標(normalized coords),而後進行視口變換(viewport transform)轉換爲窗口座標(window coords),該座標將在光柵化階段使用,多邊形圖元將根據其頂點在二維窗口中的環繞方向(順時針或逆時針,可經過二維向量叉乘結果的符號肯定)被指定爲正面或背面(front/back-facing)。頂點的其餘屬性不進行這些變換操做,但在圖元被裁剪產生新頂點時,這些屬性值被插值;
  7. Rasterization(光柵化):頂點後處理的圖元其頂點爲窗口座標,光柵化階段將肯定哪些像素屬於圖元,對於屬於圖元的像素,從圖元的頂點屬性插值獲得相應像素屬性,這些具備和頂點同樣屬性格式的像素即「片段」(fragment)。插值的方式通常在裁剪空間進行(窗口空間的權值要除以wc),也能夠指定在窗口空間進行插值(FS中"layout(noperspective) in;"或在VS中"layout(noperspective) out;")或不進行插值(FS中"layout(flat) in;"或在VS中"layout(flat) out;"),但對於紋理座標等屬性在窗口空間插值將獲得錯誤結果;
  8. Fragment ShaderFS,片段着色器,可選階段):對於光柵化產生的每一個片,OpenGL啓動一個FS,FS的輸入數據即片段的座標(gl_FragCoord)、圖元ID(gl_PrimitiveID)等內置變量及其餘定義的屬性,FS沒法訪問其餘片段(尤爲是同一圖元的片段)。FS能夠丟棄(用discard關鍵字)但至多輸出一個片段,FS的輸出和應用程序的幀緩存接口,內置變量gl_FragDepth對應深度緩存,其餘屬性統稱爲「顏色」(colors),和顏色緩存對應;VS、TCS、TES、GS、FS都可以讀寫Image/Atomic Counter/Shader Storage Buffer,能夠讀Texture/Uniform Block Buffer(buffer參考文獻[1]6.1 p58及API reference頁);
  9. Per-Fragment Operations(逐片段操做):對FS或光柵化的輸出片段,在將其寫入幀緩存以前,要進行一些列操做,依次是:Stencil Test、Depth BuffrTest、Occlusion Query、Blending、Logicop等,其中前三個操做能夠提早到FS以前(FS中"layout(early_fragment_tests) in;"),這樣將減小FS的調用次數。經過這些操做而且未被丟棄的片段將被寫入幀緩存;
  10. Framebuffer(幀緩存):幀緩存最多由深度緩存(depth buffer)、模板緩存(stencil buffer)、若干顏色緩存(color buffers),幀緩存自己是個容器,將具體緩存加入幀緩存的操做稱爲attach(這些具體緩存稱爲attachment),緩存的數據統稱爲像素數據。能夠經過緩存或紋理直接寫入或讀取像素數據,即Pixel Unpack/Pack Buffer;
  11. Compute ShaderCS,計算着色器,獨立階段):CS不能和其餘着色器一同連接到同一個Program對象(文獻[1] p91),和上面說的圖形/渲染管線相對,能夠認爲CS獨自構成計算管線。和CUDA相似,CS分爲兩層線程模型被調用,內層爲多個Invocation構成Work Group(Group內線程ID由gl_LocalInvocationID指示),多個Work Group再構成Dispath(Dispatch內Group ID由gl_WorkGroupID指示),能夠在內層聲明Work Group內共享的變量(用shared關鍵字),Work Group內還支持高效的同步機制,Dispatch總體共享Image/Atomic Counter/Shader Storage/Texture/Uniform Block Buffer,這也是CS和VS、TCS、TES、GS、FS通訊的方式。

 下面,咱們將深刻管線每一個階段的內部。

 

【這是一篇從很早很早就開始寫的博客,尚未寫完,做者如今不在從事OpenGL相關開發了,因此估計也很難完成了吧,但看着寫了這麼多,仍是發出來吧】

 

2. 管線內部

2.1 - 1;2.2 - 234;2.3 - 56;2.4 - 7;2.5 - 8;2.6 - 9 10;2.7 - 11;

2.0 構造一個管線

 pipeline obj,buffer,UIform, opaque

2.1 管線數據供給

2.2 頂點處理

 加個圖,在框圖中標出這部分

2.3 頂點後處理

2.4 光柵化

2.5 片段着色器

2.6 逐片段操做及寫入幀緩存

2.7 計算着色器 

 

3. OpenGL API總結

3.1 OpenGL API語法 

 (by gl, GL_, and GL, respectively)

3.2 

 有道筆記,glerror

 

參考文獻:

  1. The OpenGL Graphics System: A Specification, Version 4.5 (Core Profile), May 28, 2015. OpenGL Core Profile官方手冊,詳細定義OpenGL機制;
  2. The OpenGL Shading Language, Language Version: 4.50, Document Revision: 5, 30-Jan-2015. GLSL官方手冊,定義GLSL語言;
  3. OpenGL 4.5 API Reference Card, Rev. 0814. GL API、GLSL API速查手冊,內含精美的管線圖;
  4. OpenGL 4.5 Reference Pages. GL API、GLSL Functions、GLSL Built-In Variables,詳細描述了API在不一樣狀況下產生的影響、錯誤等;
  5. OpenGL Wiki.(OverviewVertex SpecificationVertex ProcessingVertex Post-ProcessingPrimitive AssemblyRasterizationFragment ShaderPer-Sample Processing
  6. The OpenGL Graphics System: A Specification, Version 4.5 (Compatibility Profile), May 28, 2015
  7. Lighthouse3d.com, GLSL Tutorial – Core.
  8. OpenGL管線(用經典管線代說着色器內部).
相關文章
相關標籤/搜索