OpenGL可编程管线
參考出處:
1. OpenGL管線(用經典管線代說著色器內部)
2. 頂點、 圖元、片元、像素的含義
0. OpenGL可編程管線流圖
可編程管線中的頂點處理器和片元(又稱片段)處理器分別代替了固定管線上的各頂點的操作和片元處理。
在進入各個框之前,我們先大概劃清范圍,看看我們常用的固定管線功能都包括在哪個部分。頂點處理包括固定管線的頂點坐標變換、光照(也即逐頂點光照)等;圖元裝配裁剪等包括圖元裝配、裁剪、透視除法、視口變換等;光柵化包括點線光柵化、多邊形填充、紋理(Texture)、霧(Fog)等;逐片斷處理包括各種測試(Scissor, Alpha, Stencil, Depth Test)、混合(Blending)等。
1. 頂點處理器
固定管線上的各頂點操作主要負責頂點的轉換(Transformation)和光照(Lighting),故替代它的頂點處理器必須具備這些功能,這就要求頂點處理器必須根據輸入進來的gl_Vertex數據給出正確的輸出數據gl_Position,以供給下一階段正確的運行。如何將gl_Vertex變換為gl_Position,這就是比變換的工作。變換的工作包括三個部分,模型變換、視圖變換和投影變換。其中通過將gl_Vertex乘上gl_ModelViewMatrix矩陣可以將其變換到攝像機空間(又稱Eye空間或攝像機坐標系),而乘上gl_ModelViewProjectionMatrix矩陣可以將頂點數據變換到齊次裁剪空間,此時即得到了gl_Position,也就是說gl_Position是位于齊次裁剪空間的頂點數據。變換部分可參考OpenGL矩陣變換。
varying vec3 ecPosition; ecPositon = vec3(gl_ModelViewMatrix * gl_Vertex); //視點 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;頂點處理器中的光照操作一般發生在eye Space,故關于光照的處理都會根據上述代碼的ecPosition進行相關操作,處理之后傳遞到片元處理器中作進一步的處理。
2. 圖元裝配
圖元裝配使用到的頂點數據是就是位于齊次裁剪空間的gl_Position,裝配過程依據用戶指定的四邊形,三角形,直線或點的方式對頂點數據進行組裝。
3. 裁剪、投影、視口、消隱
裁剪
裁剪發生在齊次裁剪空間內,即各頂點使用的都是類似于(x, y, z, w)形式的齊次坐標。
拼裝好的圖元會與視景體(View Volume)和用戶自定義的裁剪平面(通過glClipPlane函數設置的)進行比較,若位于視景體和用戶自定義的裁剪平面的內部,則不進行裁剪,若全部位于外部,則拋棄全部圖元,但若一部分位于其內,一部分位于其外,則裁剪它,這樣就產生了新的頂點,這些的點的顏色值以及紋理坐標等值要被插值。
裁剪公式如示:
投影
該處的投影意為透視投影的操作,這里針對于透視視圖進行透視除法,即將頂點的齊次坐標除以w,將頂點數據的(x, y, z)規范到[-1, 1]的閉區間內,此時w=1。
透視除法公式:
視口
此處視口意即視口變換,是OpenGL矩陣變換的最后一步變換。是齊次裁剪空間到屏幕窗口的變換,有時也稱為是攝像機鏡頭平面到窗體視口平面的變換。
視口變換可通過函數void Viewport(int x, int y, sizei w, sizei, h)進行設置,
視口變換公式:
公式中的頂點坐標(x, y, z)的下標’d’表示經過透視除法后的坐標值。公式中”f”和”n”代表函數glDepthRange(GLclampd znear, GLclampd zfar)中的參數”zfar”和”znear”,類型GLclampd 表示參數值被鉗位到了區間[0, 1],顯而易見該函數用來設置深度值范圍的。若不指定范圍,則z坐標的默認范圍為[0, 1],且值越小表示場景數據距離視口的距離越近。
消隱
使用上述計算好的窗口坐標來測試各個多邊形圖元,可以查看它是否遠離當前的查看位置。通過調用glEnable可以啟用消隱狀態,調用glCullFace可以指定要丟棄背面的多邊形、正面的多邊形或者兩者都丟棄。
4. 光柵化
到目前為止,管線里的數據都是頂點,經過圖元裝配之后,哪些頂點就是一個點、哪兩個頂點是直線段、哪三個或更多頂點是一個三角形或多邊形,這些圖元信息都已經知道了,但它們還是只是頂點而已:頂點處都還沒有“像素點”、直線段端點之間是空的、多邊形的邊和內部也是空的,光柵化的任務就是構造這些。由于已經經過了視口變換,光柵化是在二維(附帶深度值)的屏幕坐標系(Window Space)中進行的。
光柵化有兩個任務:
1. 確定圖元包含哪些由整數坐標確定的“點”(和屏幕像素對應,現在還不能叫片斷,光柵化完成后才能叫片斷);
2. 確定這些”點”的Depth值和Color值(從圖片頂點的Depth和Color插值得到),這些顏色后來可能被其他如紋理操作修改。
一個片段(fragment,又稱片元)由一個窗口坐標、深度以及其他相關屬性(如顏色、紋理坐標等)組成,這些屬性的值是通過對在圖元的頂點上指定的值之間進行插值而確定的。在進行柵格化時,頂點有一個主顏色和一個次顏色,可以通過glShadeModel函數來指定是在頂點之間對這些顏色值進行插值(平滑著色,GL_SMOOTH),還是對整個圖元都使用該圖元最后一個頂點的顏色值(平面著色,GL_FLAT)。
5. 片元處理器
片元處理器主要有兩個工作,根據光柵化后得到的紋理坐標,獲取紋理,并傳遞給輸出變量gl_FragColor;另一個作用就是“霧化”操作,即根據片元距離當前視點的位置來控制霧化在最終gl_FragColor變量中比例。
uniform float visibility; uniform vec4 fogColor; uniform sampler2D decal; vec4 textureColor = texture2D(decal, gl_TexCoord[1].st); float abs_z = abs(mPositon.z); float fogFactor = ((visibility == 0) ? 0.0 : clamp(abs_z/visibility, 0.0, 1.0)); gl_FragColor = mix(textureColor, fogColor, fogFactor); //fogFactor為計算出的比例因子片元著色器的目的是計算要應用到片元上的顏色(gl_FragColor )和計算片元的深度值(gl_FragDepth),但一般情況下由OpenGL的柵格化階段計算的深度值就已經很令人滿意了,故一般片元著色器的工作就是計算當前片元的顏色。這也是在上述程序中沒有計算gl_FragDepth的原因。
6. 各片元的操作
各片元的操作又譯為逐片元處理,逐片段處理。
光柵化的輸出是一系列片斷(Fragments,這些片斷又經過片斷著色器處理),片斷被稱為“準像素”,這是因為屏幕坐標系的一個整數坐標上只有一個像素,但可以前后“堆疊”多個片斷。這些片斷進入逐片斷處理(Per-Fragment Operations),首先進行各種測試(下圖中共5個),每步測試,不通過的片斷將被丟棄從而不能進入后續操作,然后進行一些操作(如混合),最終通過所有處理的片斷將被寫入FrameBuffer用于最終屏幕顯示,過程如下:
- 像素所有權測試(Pixel Ownership Test)
確定目標像素是可見的還是被一個重疊的窗口蓋住了。 - 剪切測試(Scissor Test)
根據通過調用 glScissor建立的一個矩形區域來裁剪片元。 - Alpha測試(Alpha Test)
使用片元的 alpha值和通過調用 glAlphaFunc建立的函數來確定是否丟棄片元。 - 模板測試(Stencil Test)
使用通過調用 glStencilFunc和 glStencilOp建立的比較關系來將模板緩沖區中的值和一個參考值做比較,以便確定片元的命運。 - 深度測試(DepthBuffer Test)
使用通過調用 glDepthFunc建立的函數來將輸入片元的深度與幀緩沖區中存儲的深度做比較,從而確定是否繪制片元。 - 遮擋查詢(Occlusion query)
遮擋查詢允許我們在一組幾何圖形進行了深度測試之后是否可見。如果查詢的結果表示某部分幾何圖形的渲染不會對當前可見的片段或樣本產生影響。那么這部分幾何圖形就在場景中不可見,也就不需要進行繪制。 - 混合(Blending)
使用片元的顏色、存儲在幀緩沖區中的顏色以及通過調用 glBlendFunc、 glBlendColor和 glBlendEquation設置的混合狀態來確定要寫入緩沖區中的顏色。 - 抖動(dithering),以及邏輯操作(Logicop)
使用通過調用 glLogicOp建立的邏輯操作將最終的片元值與幀緩沖區中的值結合起來。
7. 幀緩沖區操作
該階段主要供用戶設置OpenGL的狀態,以控制要在其中繪制圖元的幀緩沖區區域。
8. 幀緩沖區
幀緩沖區(FrameBuffer),包括RGBA緩沖、Depth緩沖,Stencil緩沖和Accum緩沖。
- Stencil緩沖僅用于測試,片斷中不包括Stencil值。
- Accum緩沖,多用于運動模糊、景深模糊等,但不能直接寫入,而是將RGBA緩沖整幅累積。
注意幀緩沖區不包括雙緩沖,雙緩沖的交換發生在幀緩沖之后,然后就是將渲染的內容顯示在屏幕了。
雙緩沖是一種常用的防止畫面撕裂的技術,即調用OpenGL函數進行渲染的結果都寫入“back” buffer,待所有渲染完成調用SwapBuffers函數,切換“back” buffer和“front” buffer,并將“front” buffer內容顯示在屏幕上,有個細節,顯示器刷新頻率一般為60或120Hz,SwapBuffers調用時刻可能不是顯示器的刷新時刻,這時SwapBuffers將會等待直到顯示器刷新才返回(當然,肯定存在避免等待的技術)。
總結
以上是生活随笔為你收集整理的OpenGL可编程管线的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 空间换时间,查表法的经典例子
- 下一篇: Linux内核系统架构介绍